1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00
jellyfin-web/src/controllers/itemDetails/index.js

2035 lines
77 KiB
JavaScript
Raw Normal View History

import appHost from 'apphost';
2020-07-29 10:05:26 +01:00
import loading from 'loading';
import appRouter from 'appRouter';
import itemShortcuts from 'itemShortcuts';
2020-07-29 10:05:26 +01:00
import layoutManager from 'layoutManager';
import connectionManager from 'connectionManager';
import * as userSettings from 'userSettings';
import cardBuilder from 'cardBuilder';
import datetime from 'datetime';
import mediaInfo from 'mediaInfo';
import backdrop from 'backdrop';
import listView from 'listView';
import itemContextMenu from 'itemContextMenu';
import itemHelper from 'itemHelper';
import dom from 'dom';
import indicators from 'indicators';
import imageLoader from 'imageLoader';
import libraryMenu from 'libraryMenu';
import globalize from 'globalize';
import browser from 'browser';
import events from 'events';
import playbackManager from 'playbackManager';
import 'scrollStyles';
import 'emby-itemscontainer';
import 'emby-checkbox';
import 'emby-button';
import 'emby-playstatebutton';
import 'emby-ratingbutton';
import 'emby-scroller';
import 'emby-select';
/* eslint-disable indent */
2018-10-23 01:05:09 +03:00
function getPromise(apiClient, params) {
2020-07-29 10:05:26 +01:00
const id = params.id;
if (id) {
return apiClient.getItem(apiClient.getCurrentUserId(), id);
}
if (params.seriesTimerId) {
return apiClient.getLiveTvSeriesTimer(params.seriesTimerId);
}
if (params.genre) {
return apiClient.getGenre(params.genre, apiClient.getCurrentUserId());
}
if (params.musicgenre) {
return apiClient.getMusicGenre(params.musicgenre, apiClient.getCurrentUserId());
}
if (params.musicartist) {
return apiClient.getArtist(params.musicartist, apiClient.getCurrentUserId());
}
2020-05-04 12:44:12 +02:00
throw new Error('Invalid request');
2018-10-23 01:05:09 +03:00
}
function hideAll(page, className, show) {
for (const elem of page.querySelectorAll('.' + className)) {
if (show) {
elem.classList.remove('hide');
} else {
elem.classList.add('hide');
}
}
2018-10-23 01:05:09 +03:00
}
function getContextMenuOptions(item, user, button) {
2020-07-30 19:42:30 +02:00
return {
2018-10-23 01:05:09 +03:00
item: item,
open: false,
play: false,
playAllFromHere: false,
queueAllFromHere: false,
2018-10-23 01:05:09 +03:00
positionTo: button,
cancelTimer: false,
record: false,
2020-05-08 19:57:36 +02:00
deleteItem: item.CanDelete === true,
shuffle: false,
instantMix: false,
2018-10-23 01:05:09 +03:00
user: user,
share: true
2018-10-23 01:05:09 +03:00
};
}
2020-03-01 15:33:41 +01:00
function getProgramScheduleHtml(items) {
2020-07-29 10:05:26 +01:00
let html = '';
2020-07-12 04:48:38 +09:00
html += '<div is="emby-itemscontainer" class="itemsContainer vertical-list" data-contextmenu="false">';
html += listView.getListViewHtml({
2018-10-23 01:05:09 +03:00
items: items,
enableUserDataButtons: false,
image: true,
2020-05-04 12:44:12 +02:00
imageSource: 'channel',
showProgramDateTime: true,
showChannel: false,
mediaInfo: false,
2020-05-04 12:44:12 +02:00
action: 'none',
moreButton: false,
recordButton: false
});
2020-07-12 04:48:38 +09:00
html += '</div>';
return html;
2018-10-23 01:05:09 +03:00
}
function renderSeriesTimerSchedule(page, apiClient, seriesTimerId) {
apiClient.getLiveTvTimers({
UserId: apiClient.getCurrentUserId(),
ImageTypeLimit: 1,
2020-05-04 12:44:12 +02:00
EnableImageTypes: 'Primary,Backdrop,Thumb',
SortBy: 'StartDate',
EnableTotalRecordCount: false,
EnableUserData: false,
2018-10-23 01:05:09 +03:00
SeriesTimerId: seriesTimerId,
2020-05-04 12:44:12 +02:00
Fields: 'ChannelInfo,ChannelImage'
}).then(function (result) {
if (result.Items.length && result.Items[0].SeriesTimerId != seriesTimerId) {
result.Items = [];
}
2020-07-29 10:05:26 +01:00
const html = getProgramScheduleHtml(result.Items);
const scheduleTab = page.querySelector('.seriesTimerSchedule');
scheduleTab.innerHTML = html;
imageLoader.lazyChildren(scheduleTab);
});
2018-10-23 01:05:09 +03:00
}
function renderTimerEditor(page, item, apiClient, user) {
2020-07-30 16:07:13 +02:00
if (item.Type !== 'Recording' || !user.Policy.EnableLiveTvManagement || !item.TimerId || item.Status !== 'InProgress') {
2020-05-04 12:44:12 +02:00
return void hideAll(page, 'btnCancelTimer');
}
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnCancelTimer', true);
2018-10-23 01:05:09 +03:00
}
function renderSeriesTimerEditor(page, item, apiClient, user) {
2020-07-30 16:07:13 +02:00
if (item.Type !== 'SeriesTimer') {
2020-05-04 12:44:12 +02:00
return void hideAll(page, 'btnCancelSeriesTimer');
}
if (user.Policy.EnableLiveTvManagement) {
2020-07-29 10:05:26 +01:00
import('seriesRecordingEditor').then(({default: seriesRecordingEditor}) => {
seriesRecordingEditor.embed(item, apiClient.serverId(), {
2020-05-04 12:44:12 +02:00
context: page.querySelector('.seriesRecordingEditor')
});
});
2020-05-04 12:44:12 +02:00
page.querySelector('.seriesTimerScheduleSection').classList.remove('hide');
hideAll(page, 'btnCancelSeriesTimer', true);
return void renderSeriesTimerSchedule(page, apiClient, item.Id);
}
2020-05-04 12:44:12 +02:00
page.querySelector('.seriesTimerScheduleSection').classList.add('hide');
return void hideAll(page, 'btnCancelSeriesTimer');
2018-10-23 01:05:09 +03:00
}
function renderTrackSelections(page, instance, item, forceReload) {
2020-07-29 10:05:26 +01:00
const select = page.querySelector('.selectSource');
2020-05-04 12:44:12 +02:00
2020-07-30 16:07:13 +02:00
if (!item.MediaSources || !itemHelper.supportsMediaSourceSelection(item) || playbackManager.getSupportedCommands().indexOf('PlayMediaSource') === -1 || !playbackManager.canPlay(item)) {
2020-05-04 12:44:12 +02:00
page.querySelector('.trackSelections').classList.add('hide');
select.innerHTML = '';
page.querySelector('.selectVideo').innerHTML = '';
page.querySelector('.selectAudio').innerHTML = '';
page.querySelector('.selectSubtitles').innerHTML = '';
return;
}
2020-07-29 10:05:26 +01:00
const mediaSources = item.MediaSources;
instance._currentPlaybackMediaSources = mediaSources;
page.querySelector('.trackSelections').classList.remove('hide');
select.setLabel(globalize.translate('LabelVersion'));
2020-07-29 10:05:26 +01:00
const currentValue = select.value;
2020-07-29 10:05:26 +01:00
const selectedId = mediaSources[0].Id;
select.innerHTML = mediaSources.map(function (v) {
2020-07-29 10:05:26 +01:00
const selected = v.Id === selectedId ? ' selected' : '';
return '<option value="' + v.Id + '"' + selected + '>' + v.Name + '</option>';
}).join('');
if (mediaSources.length > 1) {
page.querySelector('.selectSourceContainer').classList.remove('hide');
} else {
page.querySelector('.selectSourceContainer').classList.add('hide');
}
if (select.value !== currentValue || forceReload) {
renderVideoSelections(page, mediaSources);
renderAudioSelections(page, mediaSources);
renderSubtitleSelections(page, mediaSources);
}
2018-10-23 01:05:09 +03:00
}
function renderVideoSelections(page, mediaSources) {
2020-07-29 10:05:26 +01:00
const mediaSourceId = page.querySelector('.selectSource').value;
const mediaSource = mediaSources.filter(function (m) {
return m.Id === mediaSourceId;
})[0];
2020-07-12 04:48:38 +09:00
2020-07-29 10:05:26 +01:00
const tracks = mediaSource.MediaStreams.filter(function (m) {
2020-06-23 17:29:59 +02:00
return m.Type === 'Video';
});
2020-07-12 04:48:38 +09:00
2020-07-29 10:05:26 +01:00
const select = page.querySelector('.selectVideo');
2020-05-04 12:44:12 +02:00
select.setLabel(globalize.translate('LabelVideo'));
2020-07-29 10:05:26 +01:00
const selectedId = tracks.length ? tracks[0].Index : -1;
select.innerHTML = tracks.map(function (v) {
2020-07-29 10:05:26 +01:00
const selected = v.Index === selectedId ? ' selected' : '';
const titleParts = [];
const resolutionText = mediaInfo.getResolutionText(v);
if (resolutionText) {
titleParts.push(resolutionText);
}
if (v.Codec) {
titleParts.push(v.Codec.toUpperCase());
}
2020-05-04 12:44:12 +02:00
return '<option value="' + v.Index + '" ' + selected + '>' + (v.DisplayTitle || titleParts.join(' ')) + '</option>';
}).join('');
select.setAttribute('disabled', 'disabled');
if (tracks.length) {
2020-05-04 12:44:12 +02:00
page.querySelector('.selectVideoContainer').classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('.selectVideoContainer').classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function renderAudioSelections(page, mediaSources) {
2020-07-29 10:05:26 +01:00
const mediaSourceId = page.querySelector('.selectSource').value;
const mediaSource = mediaSources.filter(function (m) {
return m.Id === mediaSourceId;
})[0];
2020-07-29 10:05:26 +01:00
const tracks = mediaSource.MediaStreams.filter(function (m) {
2020-07-30 16:07:13 +02:00
return m.Type === 'Audio';
});
2020-07-29 10:05:26 +01:00
const select = page.querySelector('.selectAudio');
2020-08-16 20:34:39 +09:00
select.setLabel(globalize.translate('Audio'));
2020-07-29 10:05:26 +01:00
const selectedId = mediaSource.DefaultAudioStreamIndex;
select.innerHTML = tracks.map(function (v) {
2020-07-29 10:05:26 +01:00
const selected = v.Index === selectedId ? ' selected' : '';
2020-05-04 12:44:12 +02:00
return '<option value="' + v.Index + '" ' + selected + '>' + v.DisplayTitle + '</option>';
}).join('');
if (tracks.length > 1) {
2020-05-04 12:44:12 +02:00
select.removeAttribute('disabled');
} else {
2020-05-04 12:44:12 +02:00
select.setAttribute('disabled', 'disabled');
}
if (tracks.length) {
2020-05-04 12:44:12 +02:00
page.querySelector('.selectAudioContainer').classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('.selectAudioContainer').classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function renderSubtitleSelections(page, mediaSources) {
2020-07-29 10:05:26 +01:00
const mediaSourceId = page.querySelector('.selectSource').value;
const mediaSource = mediaSources.filter(function (m) {
return m.Id === mediaSourceId;
})[0];
2020-07-29 10:05:26 +01:00
const tracks = mediaSource.MediaStreams.filter(function (m) {
2020-07-30 16:07:13 +02:00
return m.Type === 'Subtitle';
});
2020-07-29 10:05:26 +01:00
const select = page.querySelector('.selectSubtitles');
2020-08-16 20:34:39 +09:00
select.setLabel(globalize.translate('Subtitles'));
const selectedId = mediaSource.DefaultSubtitleStreamIndex == null ? -1 : mediaSource.DefaultSubtitleStreamIndex;
2020-07-29 10:05:26 +01:00
const videoTracks = mediaSource.MediaStreams.filter(function (m) {
2020-07-30 16:07:13 +02:00
return m.Type === 'Video';
});
// This only makes sense on Video items
if (videoTracks.length) {
let selected = selectedId === -1 ? ' selected' : '';
2020-05-04 12:44:12 +02:00
select.innerHTML = '<option value="-1">' + globalize.translate('Off') + '</option>' + tracks.map(function (v) {
selected = v.Index === selectedId ? ' selected' : '';
return '<option value="' + v.Index + '" ' + selected + '>' + v.DisplayTitle + '</option>';
}).join('');
if (tracks.length > 0) {
select.removeAttribute('disabled');
} else {
select.setAttribute('disabled', 'disabled');
}
2020-05-04 12:44:12 +02:00
page.querySelector('.selectSubtitlesContainer').classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
select.innerHTML = '';
page.querySelector('.selectSubtitlesContainer').classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function reloadPlayButtons(page, item) {
2020-07-29 10:05:26 +01:00
let canPlay = false;
2020-07-30 16:07:13 +02:00
if (item.Type == 'Program') {
2020-07-29 10:05:26 +01:00
const now = new Date();
if (now >= datetime.parseISO8601Date(item.StartDate, true) && now < datetime.parseISO8601Date(item.EndDate, true)) {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnPlay', true);
canPlay = true;
} else {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnPlay');
}
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnResume');
hideAll(page, 'btnInstantMix');
hideAll(page, 'btnShuffle');
2018-10-23 01:05:09 +03:00
} else if (playbackManager.canPlay(item)) {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnPlay', true);
const enableInstantMix = ['Audio', 'MusicAlbum', 'MusicGenre', 'MusicArtist'].indexOf(item.Type) !== -1;
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnInstantMix', enableInstantMix);
const enableShuffle = item.IsFolder || ['MusicAlbum', 'MusicGenre', 'MusicArtist'].indexOf(item.Type) !== -1;
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnShuffle', enableShuffle);
canPlay = true;
2020-06-21 09:31:56 +02:00
const isResumable = item.UserData && item.UserData.PlaybackPositionTicks > 0;
hideAll(page, 'btnResume', isResumable);
if (isResumable) {
for (const elem of page.querySelectorAll('.btnPlay')) {
elem.querySelector('.detailButton-icon').classList.replace('play_arrow', 'replay');
}
}
} else {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnPlay');
hideAll(page, 'btnResume');
hideAll(page, 'btnInstantMix');
hideAll(page, 'btnShuffle');
}
return canPlay;
2018-10-23 01:05:09 +03:00
}
function reloadUserDataButtons(page, item) {
2020-07-29 10:05:26 +01:00
let i;
let length;
const btnPlaystates = page.querySelectorAll('.btnPlaystate');
2018-10-23 01:05:09 +03:00
for (i = 0, length = btnPlaystates.length; i < length; i++) {
2020-07-29 10:05:26 +01:00
const btnPlaystate = btnPlaystates[i];
if (itemHelper.canMarkPlayed(item)) {
2020-05-04 12:44:12 +02:00
btnPlaystate.classList.remove('hide');
btnPlaystate.setItem(item);
} else {
2020-05-04 12:44:12 +02:00
btnPlaystate.classList.add('hide');
btnPlaystate.setItem(null);
}
2018-10-23 01:05:09 +03:00
}
2020-07-29 10:05:26 +01:00
const btnUserRatings = page.querySelectorAll('.btnUserRating');
2018-10-23 01:05:09 +03:00
for (i = 0, length = btnUserRatings.length; i < length; i++) {
2020-07-29 10:05:26 +01:00
const btnUserRating = btnUserRatings[i];
if (itemHelper.canRate(item)) {
2020-05-04 12:44:12 +02:00
btnUserRating.classList.remove('hide');
btnUserRating.setItem(item);
} else {
2020-05-04 12:44:12 +02:00
btnUserRating.classList.add('hide');
btnUserRating.setItem(null);
}
2018-10-23 01:05:09 +03:00
}
}
function getArtistLinksHtml(artists, serverId, context) {
2020-07-19 17:38:42 +02:00
const html = [];
for (const artist of artists) {
2020-07-29 10:05:26 +01:00
const href = appRouter.getRouteUrl(artist, {
context: context,
2020-05-04 12:44:12 +02:00
itemType: 'MusicArtist',
serverId: serverId
});
2020-05-04 12:44:12 +02:00
html.push('<a style="color:inherit;" class="button-link" is="emby-linkbutton" href="' + href + '">' + artist.Name + '</a>');
2018-10-23 01:05:09 +03:00
}
2020-07-30 19:42:30 +02:00
return html.join(' / ');
2018-10-23 01:05:09 +03:00
}
2020-05-10 13:36:26 +02:00
/**
2020-06-23 17:29:59 +02:00
* Renders the item's name block
2020-05-10 13:36:26 +02:00
* @param {Object} item - Item used to render the name.
* @param {HTMLDivElement} container - Container to render the information into.
* @param {Object} context - Application context.
*/
function renderName(item, container, context) {
2020-07-29 10:05:26 +01:00
let parentRoute;
const parentNameHtml = [];
let parentNameLast = false;
if (item.AlbumArtists) {
parentNameHtml.push(getArtistLinksHtml(item.AlbumArtists, item.ServerId, context));
parentNameLast = true;
2020-07-30 16:07:13 +02:00
} else if (item.ArtistItems && item.ArtistItems.length && item.Type === 'MusicVideo') {
parentNameHtml.push(getArtistLinksHtml(item.ArtistItems, item.ServerId, context));
parentNameLast = true;
2020-07-30 16:07:13 +02:00
} else if (item.SeriesName && item.Type === 'Episode') {
parentRoute = appRouter.getRouteUrl({
Id: item.SeriesId,
Name: item.SeriesName,
2020-05-04 12:44:12 +02:00
Type: 'Series',
IsFolder: true,
ServerId: item.ServerId
}, {
context: context
});
2020-05-04 12:44:12 +02:00
parentNameHtml.push('<a style="color:inherit;" class="button-link" is="emby-linkbutton" href="' + parentRoute + '">' + item.SeriesName + '</a>');
} else if (item.IsSeries || item.EpisodeTitle) {
parentNameHtml.push(item.Name);
}
2020-07-30 16:07:13 +02:00
if (item.SeriesName && item.Type === 'Season') {
parentRoute = appRouter.getRouteUrl({
Id: item.SeriesId,
Name: item.SeriesName,
2020-05-04 12:44:12 +02:00
Type: 'Series',
IsFolder: true,
ServerId: item.ServerId
}, {
context: context
});
2020-05-04 12:44:12 +02:00
parentNameHtml.push('<a style="color:inherit;" class="button-link" is="emby-linkbutton" href="' + parentRoute + '">' + item.SeriesName + '</a>');
2020-07-30 16:07:13 +02:00
} else if (item.ParentIndexNumber != null && item.Type === 'Episode') {
parentRoute = appRouter.getRouteUrl({
Id: item.SeasonId,
Name: item.SeasonName,
2020-05-04 12:44:12 +02:00
Type: 'Season',
IsFolder: true,
ServerId: item.ServerId
}, {
context: context
});
2020-05-04 12:44:12 +02:00
parentNameHtml.push('<a style="color:inherit;" class="button-link" is="emby-linkbutton" href="' + parentRoute + '">' + item.SeasonName + '</a>');
2020-07-30 16:07:13 +02:00
} else if (item.ParentIndexNumber != null && item.IsSeries) {
2020-05-04 12:44:12 +02:00
parentNameHtml.push(item.SeasonName || 'S' + item.ParentIndexNumber);
2020-07-30 16:07:13 +02:00
} else if (item.Album && item.AlbumId && (item.Type === 'MusicVideo' || item.Type === 'Audio')) {
parentRoute = appRouter.getRouteUrl({
Id: item.AlbumId,
Name: item.Album,
2020-05-04 12:44:12 +02:00
Type: 'MusicAlbum',
IsFolder: true,
ServerId: item.ServerId
}, {
context: context
});
2020-05-04 12:44:12 +02:00
parentNameHtml.push('<a style="color:inherit;" class="button-link" is="emby-linkbutton" href="' + parentRoute + '">' + item.Album + '</a>');
} else if (item.Album) {
parentNameHtml.push(item.Album);
}
2020-06-16 00:32:23 +09:00
2020-04-05 14:19:31 +02:00
// FIXME: This whole section needs some refactoring, so it becames easier to scale across all form factors. See GH #1022
2020-07-29 10:05:26 +01:00
let html = '';
const tvShowHtml = parentNameHtml[0];
const tvSeasonHtml = parentNameHtml[1];
if (parentNameHtml.length) {
if (parentNameLast) {
2020-04-05 00:59:37 +02:00
// Music
if (layoutManager.mobile) {
html = '<h3 class="parentName musicParentName">' + parentNameHtml.join('</br>') + '</h3>';
2020-04-05 00:59:37 +02:00
} else {
html = '<h3 class="parentName musicParentName">' + parentNameHtml.join(' - ') + '</h3>';
2020-04-05 00:59:37 +02:00
}
} else {
html = '<h1 class="parentName">' + tvShowHtml + '</h1>';
}
}
2020-07-29 10:05:26 +01:00
const name = itemHelper.getDisplayName(item, {
includeParentInfo: false
2018-10-23 01:05:09 +03:00
});
2019-09-11 17:59:00 +02:00
if (html && !parentNameLast) {
if (tvSeasonHtml) {
2020-06-21 09:06:51 +02:00
html += '<h3 class="itemName infoText subtitle">' + tvSeasonHtml + ' - ' + name + '</h3>';
2020-04-05 00:59:37 +02:00
} else {
2020-06-21 09:06:51 +02:00
html += '<h3 class="itemName infoText subtitle">' + name + '</h3>';
2020-04-05 00:59:37 +02:00
}
} else if (item.OriginalTitle && item.OriginalTitle != item.Name) {
html = '<h1 class="itemName infoText parentNameLast withOriginalTitle">' + name + '</h1>' + html;
2019-09-11 17:59:00 +02:00
} else {
html = '<h1 class="itemName infoText parentNameLast">' + name + '</h1>' + html;
2019-09-11 17:59:00 +02:00
}
2019-09-11 18:19:17 +02:00
if (item.OriginalTitle && item.OriginalTitle != item.Name) {
html += '<h4 class="itemName infoText originalTitle">' + item.OriginalTitle + '</h4>';
2019-09-11 17:59:00 +02:00
}
container.innerHTML = html;
if (html.length) {
2020-05-04 12:44:12 +02:00
container.classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
container.classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function setTrailerButtonVisibility(page, item) {
2020-07-30 16:07:13 +02:00
if ((item.LocalTrailerCount || item.RemoteTrailers && item.RemoteTrailers.length) && playbackManager.getSupportedCommands().indexOf('PlayTrailers') !== -1) {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnPlayTrailer', true);
} else {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnPlayTrailer');
}
2018-10-23 01:05:09 +03:00
}
2020-03-01 15:33:41 +01:00
function renderBackdrop(item) {
if (dom.getWindowSize().innerWidth >= 1000) {
backdrop.setBackdrops([item]);
} else {
backdrop.clearBackdrop();
2019-11-28 22:46:57 +03:00
}
}
2018-10-23 01:05:09 +03:00
function renderDetailPageBackdrop(page, item, apiClient) {
2020-07-29 10:05:26 +01:00
let imgUrl;
let hasbackdrop = false;
const itemBackdropElement = page.querySelector('#itemBackdrop');
2020-06-25 18:40:19 +02:00
if (!layoutManager.mobile && !userSettings.detailsBanner()) {
return false;
}
if (item.BackdropImageTags && item.BackdropImageTags.length) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
2020-05-04 12:44:12 +02:00
type: 'Backdrop',
2020-03-08 19:08:07 +01:00
maxWidth: dom.getScreenWidth(),
index: 0,
tag: item.BackdropImageTags[0]
});
2020-04-12 14:29:42 +02:00
imageLoader.lazyImage(itemBackdropElement, imgUrl);
hasbackdrop = true;
} else if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, {
2020-05-04 12:44:12 +02:00
type: 'Backdrop',
2020-03-08 19:08:07 +01:00
maxWidth: dom.getScreenWidth(),
index: 0,
2019-11-24 21:34:30 +09:00
tag: item.ParentBackdropImageTags[0]
});
2020-04-12 14:29:42 +02:00
imageLoader.lazyImage(itemBackdropElement, imgUrl);
hasbackdrop = true;
} else if (item.ImageTags && item.ImageTags.Primary) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Primary',
maxWidth: dom.getScreenWidth(),
tag: item.ImageTags.Primary
});
imageLoader.lazyImage(itemBackdropElement, imgUrl);
hasbackdrop = true;
} else {
2020-05-04 12:44:12 +02:00
itemBackdropElement.style.backgroundImage = '';
}
return hasbackdrop;
2018-10-23 01:05:09 +03:00
}
function reloadFromItem(instance, page, params, item, user) {
2020-05-10 13:36:26 +02:00
const apiClient = connectionManager.getApiClient(item.ServerId);
2020-02-23 21:14:38 +01:00
2020-05-10 13:36:26 +02:00
Emby.Page.setTitle('');
// Start rendering the artwork first
renderImage(page, item);
renderLogo(page, item, apiClient);
2020-03-01 15:33:41 +01:00
renderBackdrop(item);
renderDetailPageBackdrop(page, item, apiClient);
2020-05-10 13:36:26 +02:00
// Render the main information for the item
page.querySelector('.detailPagePrimaryContainer').classList.add('detailRibbon');
renderName(item, page.querySelector('.nameContainer'), params.context);
renderDetails(page, item, apiClient, params.context);
renderTrackSelections(page, instance, item);
renderSeriesTimerEditor(page, item, apiClient, user);
renderTimerEditor(page, item, apiClient, user);
setInitialCollapsibleState(page, item, apiClient, params.context, user);
2020-07-29 10:05:26 +01:00
const canPlay = reloadPlayButtons(page, item);
2020-07-30 16:07:13 +02:00
if ((item.LocalTrailerCount || item.RemoteTrailers && item.RemoteTrailers.length) && playbackManager.getSupportedCommands().indexOf('PlayTrailers') !== -1) {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnPlayTrailer', true);
} else {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnPlayTrailer');
}
2019-03-18 22:22:48 -07:00
setTrailerButtonVisibility(page, item);
2020-07-30 16:07:13 +02:00
if (item.Type !== 'Program' || canPlay) {
2020-05-04 12:44:12 +02:00
hideAll(page, 'mainDetailButtons', true);
} else {
2020-05-04 12:44:12 +02:00
hideAll(page, 'mainDetailButtons');
}
2019-03-18 22:22:48 -07:00
showRecordingFields(instance, page, item, user);
2020-07-29 10:05:26 +01:00
const groupedVersions = (item.MediaSources || []).filter(function (g) {
2020-07-30 16:07:13 +02:00
return g.Type == 'Grouping';
2018-10-23 01:05:09 +03:00
});
if (user.Policy.IsAdministrator && groupedVersions.length) {
2020-05-04 12:44:12 +02:00
page.querySelector('.btnSplitVersions').classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('.btnSplitVersions').classList.add('hide');
}
if (itemContextMenu.getCommands(getContextMenuOptions(item, user)).length) {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnMoreCommands', true);
} else {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnMoreCommands');
}
2020-07-29 10:05:26 +01:00
const itemBirthday = page.querySelector('#itemBirthday');
2020-07-30 16:07:13 +02:00
if (item.Type == 'Person' && item.PremiereDate) {
try {
2020-07-29 10:05:26 +01:00
const birthday = datetime.parseISO8601Date(item.PremiereDate, true).toDateString();
2020-05-04 12:44:12 +02:00
itemBirthday.classList.remove('hide');
itemBirthday.innerHTML = globalize.translate('BirthDateValue', birthday);
} catch (err) {
2020-05-04 12:44:12 +02:00
itemBirthday.classList.add('hide');
}
} else {
2020-05-04 12:44:12 +02:00
itemBirthday.classList.add('hide');
}
2020-07-29 10:05:26 +01:00
const itemDeathDate = page.querySelector('#itemDeathDate');
2020-07-30 16:07:13 +02:00
if (item.Type == 'Person' && item.EndDate) {
try {
2020-07-29 10:05:26 +01:00
const deathday = datetime.parseISO8601Date(item.EndDate, true).toDateString();
2020-05-04 12:44:12 +02:00
itemDeathDate.classList.remove('hide');
itemDeathDate.innerHTML = globalize.translate('DeathDateValue', deathday);
} catch (err) {
2020-05-04 12:44:12 +02:00
itemDeathDate.classList.add('hide');
}
} else {
2020-05-04 12:44:12 +02:00
itemDeathDate.classList.add('hide');
2018-10-23 01:05:09 +03:00
}
2020-07-29 10:05:26 +01:00
const itemBirthLocation = page.querySelector('#itemBirthLocation');
2020-07-30 16:07:13 +02:00
if (item.Type == 'Person' && item.ProductionLocations && item.ProductionLocations.length) {
2020-07-29 10:05:26 +01:00
const gmap = '<a is="emby-linkbutton" class="button-link textlink" target="_blank" href="https://maps.google.com/maps?q=' + item.ProductionLocations[0] + '">' + item.ProductionLocations[0] + '</a>';
2020-05-04 12:44:12 +02:00
itemBirthLocation.classList.remove('hide');
itemBirthLocation.innerHTML = globalize.translate('BirthPlaceValue', gmap);
} else {
2020-05-04 12:44:12 +02:00
itemBirthLocation.classList.add('hide');
}
setPeopleHeader(page, item);
loading.hide();
if (item.Type === 'Book' && item.CanDownload && appHost.supports('filedownload')) {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnDownload', true);
}
2020-07-29 10:05:26 +01:00
import('autoFocuser').then(({default: autoFocuser}) => {
2019-11-02 20:38:58 +03:00
autoFocuser.autoFocus(page);
});
2018-10-23 01:05:09 +03:00
}
function logoImageUrl(item, apiClient, options) {
options = options || {};
2020-05-04 12:44:12 +02:00
options.type = 'Logo';
if (item.ImageTags && item.ImageTags.Logo) {
options.tag = item.ImageTags.Logo;
return apiClient.getScaledImageUrl(item.Id, options);
}
if (item.ParentLogoImageTag) {
options.tag = item.ParentLogoImageTag;
return apiClient.getScaledImageUrl(item.ParentLogoItemId, options);
}
return null;
2018-10-23 01:05:09 +03:00
}
function renderLogo(page, item, apiClient) {
2020-07-29 10:05:26 +01:00
const detailLogo = page.querySelector('.detailLogo');
2020-05-10 13:36:26 +02:00
2020-07-29 10:05:26 +01:00
const url = logoImageUrl(item, apiClient, {});
if (!layoutManager.mobile && !userSettings.enableBackdrops()) {
2020-05-04 12:44:12 +02:00
detailLogo.classList.add('hide');
} else if (url) {
2020-05-04 12:44:12 +02:00
detailLogo.classList.remove('hide');
2020-05-10 13:36:26 +02:00
imageLoader.setLazyImage(detailLogo, url);
} else {
2020-05-04 12:44:12 +02:00
detailLogo.classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function showRecordingFields(instance, page, item, user) {
if (!instance.currentRecordingFields) {
2020-07-29 10:05:26 +01:00
const recordingFieldsElement = page.querySelector('.recordingFields');
2020-07-30 16:07:13 +02:00
if (item.Type == 'Program' && user.Policy.EnableLiveTvManagement) {
2020-07-29 10:05:26 +01:00
import('recordingFields').then(({default: recordingFields}) => {
instance.currentRecordingFields = new recordingFields({
parent: recordingFieldsElement,
programId: item.Id,
serverId: item.ServerId
});
2020-05-04 12:44:12 +02:00
recordingFieldsElement.classList.remove('hide');
});
} else {
2020-05-04 12:44:12 +02:00
recordingFieldsElement.classList.add('hide');
recordingFieldsElement.innerHTML = '';
}
2018-10-23 01:05:09 +03:00
}
}
2020-05-10 13:36:26 +02:00
function renderLinks(page, item) {
2020-07-29 10:05:26 +01:00
const externalLinksElem = page.querySelector('.itemExternalLinks');
2020-07-29 10:05:26 +01:00
const links = [];
if (!layoutManager.tv && item.HomePageUrl) {
2020-05-10 13:36:26 +02:00
links.push(`<a is="emby-linkbutton" class="button-link" href="${item.HomePageUrl}" target="_blank">${globalize.translate('ButtonWebsite')}</a>`);
}
2020-05-10 13:36:26 +02:00
if (item.ExternalUrls) {
for (const url of item.ExternalUrls) {
2020-05-10 13:36:26 +02:00
links.push(`<a is="emby-linkbutton" class="button-link" href="${url.Url}" target="_blank">${url.Name}</a>`);
2018-10-23 01:05:09 +03:00
}
}
2020-07-29 10:05:26 +01:00
const html = [];
if (links.length) {
2020-05-04 12:44:12 +02:00
html.push(links.join(', '));
}
2020-05-10 13:36:26 +02:00
externalLinksElem.innerHTML = html.join(', ');
if (html.length) {
2020-05-10 13:36:26 +02:00
externalLinksElem.classList.remove('hide');
} else {
2020-05-10 13:36:26 +02:00
externalLinksElem.classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function renderDetailImage(elem, item, imageLoader) {
const itemArray = [];
itemArray.push(item);
const cardHtml = cardBuilder.getCardsHtml(itemArray, {
shape: 'auto',
showTitle: false,
centerText: true,
overlayText: false,
transition: false,
disableIndicators: true,
overlayPlayButton: true,
action: 'play',
2020-07-31 15:18:32 +02:00
width: dom.getWindowSize().innerWidth * 0.25
});
elem.innerHTML = cardHtml;
imageLoader.lazyChildren(elem);
2018-10-23 01:05:09 +03:00
}
function renderImage(page, item) {
renderDetailImage(
2020-05-04 12:44:12 +02:00
page.querySelector('.detailImageContainer'),
item,
imageLoader
);
2018-10-23 01:05:09 +03:00
}
function refreshDetailImageUserData(elem, item) {
2020-05-04 12:44:12 +02:00
elem.querySelector('.detailImageProgressContainer').innerHTML = indicators.getProgressBarHtml(item);
2018-10-23 01:05:09 +03:00
}
2020-03-01 15:33:41 +01:00
function refreshImage(page, item) {
2020-05-04 12:44:12 +02:00
refreshDetailImageUserData(page.querySelector('.detailImageContainer'), item);
2018-10-23 01:05:09 +03:00
}
function setPeopleHeader(page, item) {
2020-07-30 16:07:13 +02:00
if (item.MediaType == 'Audio' || item.Type == 'MusicAlbum' || item.MediaType == 'Book' || item.MediaType == 'Photo') {
2020-08-13 21:56:01 +09:00
page.querySelector('#peopleHeader').innerHTML = globalize.translate('People');
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('#peopleHeader').innerHTML = globalize.translate('HeaderCastAndCrew');
}
2018-10-23 01:05:09 +03:00
}
function renderNextUp(page, item, user) {
2020-07-29 10:05:26 +01:00
const section = page.querySelector('.nextUpSection');
2020-07-30 16:07:13 +02:00
if (item.Type != 'Series') {
2020-05-04 12:44:12 +02:00
return void section.classList.add('hide');
}
2018-10-23 01:05:09 +03:00
connectionManager.getApiClient(item.ServerId).getNextUpEpisodes({
SeriesId: item.Id,
UserId: user.Id
}).then(function (result) {
if (result.Items.length) {
2020-05-04 12:44:12 +02:00
section.classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
section.classList.add('hide');
}
2020-07-29 10:05:26 +01:00
const html = cardBuilder.getCardsHtml({
items: result.Items,
2020-05-04 12:44:12 +02:00
shape: 'overflowBackdrop',
showTitle: true,
2020-07-30 16:07:13 +02:00
displayAsSpecial: item.Type == 'Season' && item.IndexNumber,
overlayText: false,
centerText: true,
overlayPlayButton: true
});
2020-07-29 10:05:26 +01:00
const itemsContainer = section.querySelector('.nextUpItems');
itemsContainer.innerHTML = html;
imageLoader.lazyChildren(itemsContainer);
});
2018-10-23 01:05:09 +03:00
}
function setInitialCollapsibleState(page, item, apiClient, context, user) {
2020-05-04 12:44:12 +02:00
page.querySelector('.collectionItems').innerHTML = '';
2020-07-30 16:07:13 +02:00
if (item.Type == 'Playlist') {
2020-05-04 12:44:12 +02:00
page.querySelector('#childrenCollapsible').classList.remove('hide');
2020-03-01 15:33:41 +01:00
renderPlaylistItems(page, item);
2020-07-30 16:07:13 +02:00
} else if (item.Type == 'Studio' || item.Type == 'Person' || item.Type == 'Genre' || item.Type == 'MusicGenre' || item.Type == 'MusicArtist') {
2020-05-04 12:44:12 +02:00
page.querySelector('#childrenCollapsible').classList.remove('hide');
2020-03-01 15:33:41 +01:00
renderItemsByName(page, item);
} else if (item.IsFolder) {
2020-07-30 16:07:13 +02:00
if (item.Type == 'BoxSet') {
2020-05-04 12:44:12 +02:00
page.querySelector('#childrenCollapsible').classList.add('hide');
}
renderChildren(page, item);
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('#childrenCollapsible').classList.add('hide');
}
2020-07-30 16:07:13 +02:00
if (item.Type == 'Series') {
2020-03-01 15:33:41 +01:00
renderSeriesSchedule(page, item);
renderNextUp(page, item, user);
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('.nextUpSection').classList.add('hide');
}
renderScenes(page, item);
2020-07-30 16:07:13 +02:00
if (item.SpecialFeatureCount && item.SpecialFeatureCount != 0 && item.Type != 'Series') {
2020-05-04 12:44:12 +02:00
page.querySelector('#specialsCollapsible').classList.remove('hide');
2020-07-24 00:16:17 +03:00
renderSpecials(page, item, user);
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('#specialsCollapsible').classList.add('hide');
}
2020-03-01 15:33:41 +01:00
renderCast(page, item);
if (item.PartCount && item.PartCount > 1) {
2020-05-04 12:44:12 +02:00
page.querySelector('#additionalPartsCollapsible').classList.remove('hide');
renderAdditionalParts(page, item, user);
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('#additionalPartsCollapsible').classList.add('hide');
}
2020-07-30 16:07:13 +02:00
if (item.Type == 'MusicAlbum') {
renderMusicVideos(page, item, user);
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('#musicVideosCollapsible').classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function toggleLineClamp(clampTarget, e) {
2020-07-29 10:05:26 +01:00
const expandButton = e.target;
const clampClassName = 'detail-clamp-text';
if (clampTarget.classList.contains(clampClassName)) {
clampTarget.classList.remove(clampClassName);
expandButton.innerHTML = globalize.translate('ShowLess');
} else {
clampTarget.classList.add(clampClassName);
expandButton.innerHTML = globalize.translate('ShowMore');
}
}
2020-05-10 13:36:26 +02:00
function renderOverview(page, item) {
2020-06-21 09:06:51 +02:00
for (const overviewElemnt of page.querySelectorAll('.overview')) {
2020-07-29 10:05:26 +01:00
const overview = item.Overview || '';
2018-10-23 01:05:09 +03:00
if (overview) {
2020-05-10 13:36:26 +02:00
overviewElemnt.innerHTML = overview;
overviewElemnt.classList.remove('hide');
overviewElemnt.classList.add('detail-clamp-text');
// Grab the sibling element to control the expand state
2020-07-29 10:05:26 +01:00
const expandButton = overviewElemnt.parentElement.querySelector('.overview-expand');
// Detect if we have overflow of text. Based on this StackOverflow answer
// https://stackoverflow.com/a/35157976
2020-05-10 13:36:26 +02:00
if (Math.abs(overviewElemnt.scrollHeight - overviewElemnt.offsetHeight) > 2) {
expandButton.classList.remove('hide');
} else {
expandButton.classList.add('hide');
}
2020-05-10 13:36:26 +02:00
expandButton.addEventListener('click', toggleLineClamp.bind(null, overviewElemnt));
for (const anchor of overviewElemnt.querySelectorAll('a')) {
2020-05-10 13:36:26 +02:00
anchor.setAttribute('target', '_blank');
}
} else {
2020-05-10 13:36:26 +02:00
overviewElemnt.innerHTML = '';
overviewElemnt.classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
}
function renderGenres(page, item, context = inferContext(item)) {
2020-07-29 10:05:26 +01:00
const genres = item.GenreItems || [];
const type = context === 'music' ? 'MusicGenre' : 'Genre';
2020-07-29 10:05:26 +01:00
const html = genres.map(function (p) {
return '<a style="color:inherit;" class="button-link" is="emby-linkbutton" href="' + appRouter.getRouteUrl({
Name: p.Name,
Type: type,
ServerId: item.ServerId,
Id: p.Id
}, {
context: context
2020-05-04 12:44:12 +02:00
}) + '">' + p.Name + '</a>';
}).join(', ');
2020-07-29 10:05:26 +01:00
const genresLabel = page.querySelector('.genresLabel');
2020-05-04 12:44:12 +02:00
genresLabel.innerHTML = globalize.translate(genres.length > 1 ? 'Genres' : 'Genre');
2020-07-29 10:05:26 +01:00
const genresValue = page.querySelector('.genres');
genresValue.innerHTML = html;
2020-07-29 10:05:26 +01:00
const genresGroup = page.querySelector('.genresGroup');
if (genres.length) {
2020-05-04 12:44:12 +02:00
genresGroup.classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
genresGroup.classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function renderWriter(page, item, context) {
2020-07-29 10:05:26 +01:00
const writers = (item.People || []).filter(function (person) {
return person.Type === 'Writer';
});
2020-07-29 10:05:26 +01:00
const html = writers.map(function (person) {
return '<a style="color:inherit;" class="button-link" is="emby-linkbutton" href="' + appRouter.getRouteUrl({
Name: person.Name,
Type: 'Person',
ServerId: item.ServerId,
Id: person.Id
}, {
context: context
}) + '">' + person.Name + '</a>';
}).join(', ');
2020-07-29 10:05:26 +01:00
const writersLabel = page.querySelector('.writersLabel');
writersLabel.innerHTML = globalize.translate(writers.length > 1 ? 'Writers' : 'Writer');
2020-07-29 10:05:26 +01:00
const writersValue = page.querySelector('.writers');
2020-06-23 17:32:55 +02:00
writersValue.innerHTML = html;
2020-07-29 10:05:26 +01:00
const writersGroup = page.querySelector('.writersGroup');
if (writers.length) {
writersGroup.classList.remove('hide');
} else {
writersGroup.classList.add('hide');
}
}
2020-03-01 15:33:41 +01:00
function renderDirector(page, item, context) {
2020-07-29 10:05:26 +01:00
const directors = (item.People || []).filter(function (person) {
2020-05-10 13:36:26 +02:00
return person.Type === 'Director';
});
2020-05-10 13:36:26 +02:00
2020-07-29 10:05:26 +01:00
const html = directors.map(function (person) {
return '<a style="color:inherit;" class="button-link" is="emby-linkbutton" href="' + appRouter.getRouteUrl({
2020-05-10 13:36:26 +02:00
Name: person.Name,
2020-05-04 12:44:12 +02:00
Type: 'Person',
ServerId: item.ServerId,
2020-05-10 13:36:26 +02:00
Id: person.Id
}, {
context: context
2020-05-10 13:36:26 +02:00
}) + '">' + person.Name + '</a>';
2020-05-04 12:44:12 +02:00
}).join(', ');
2020-07-29 10:05:26 +01:00
const directorsLabel = page.querySelector('.directorsLabel');
2020-05-04 12:44:12 +02:00
directorsLabel.innerHTML = globalize.translate(directors.length > 1 ? 'Directors' : 'Director');
2020-07-29 10:05:26 +01:00
const directorsValue = page.querySelector('.directors');
directorsValue.innerHTML = html;
2020-07-29 10:05:26 +01:00
const directorsGroup = page.querySelector('.directorsGroup');
if (directors.length) {
2020-05-04 12:44:12 +02:00
directorsGroup.classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
directorsGroup.classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
2020-05-10 13:36:26 +02:00
function renderMiscInfo(page, item) {
const primaryItemMiscInfo = page.querySelectorAll('.itemMiscInfo-primary');
2019-09-11 18:47:39 +02:00
for (const miscInfo of primaryItemMiscInfo) {
2020-05-10 13:36:26 +02:00
mediaInfo.fillPrimaryMediaInfo(miscInfo, item, {
interactive: true,
episodeTitle: false,
subtitles: false
2019-09-11 18:47:39 +02:00
});
2020-07-30 16:07:13 +02:00
if (miscInfo.innerHTML && item.Type !== 'SeriesTimer') {
2020-05-10 13:36:26 +02:00
miscInfo.classList.remove('hide');
2019-09-11 18:47:39 +02:00
} else {
2020-05-10 13:36:26 +02:00
miscInfo.classList.add('hide');
2019-09-11 18:47:39 +02:00
}
}
2020-05-10 13:36:26 +02:00
const secondaryItemMiscInfo = page.querySelectorAll('.itemMiscInfo-secondary');
for (const miscInfo of secondaryItemMiscInfo) {
2020-05-10 13:36:26 +02:00
mediaInfo.fillSecondaryMediaInfo(miscInfo, item, {
interactive: true
});
2019-09-11 18:47:39 +02:00
2020-07-30 16:07:13 +02:00
if (miscInfo.innerHTML && item.Type !== 'SeriesTimer') {
2020-05-10 13:36:26 +02:00
miscInfo.classList.remove('hide');
2019-09-11 18:47:39 +02:00
} else {
2020-05-10 13:36:26 +02:00
miscInfo.classList.add('hide');
2019-09-11 18:47:39 +02:00
}
}
2020-05-10 13:36:26 +02:00
}
function renderTagline(page, item) {
2020-07-29 10:05:26 +01:00
const taglineElement = page.querySelector('.tagline');
2020-05-10 13:36:26 +02:00
if (item.Taglines && item.Taglines.length) {
taglineElement.classList.remove('hide');
taglineElement.innerHTML = item.Taglines[0];
} else {
taglineElement.classList.add('hide');
}
}
function renderDetails(page, item, apiClient, context, isStatic) {
renderSimilarItems(page, item, context);
renderMoreFromSeason(page, item, apiClient);
renderMoreFromArtist(page, item, apiClient);
renderDirector(page, item, context);
renderWriter(page, item, context);
2020-05-10 13:36:26 +02:00
renderGenres(page, item, context);
renderChannelGuide(page, apiClient, item);
renderTagline(page, item);
renderOverview(page, item);
renderMiscInfo(page, item);
2019-09-11 18:47:39 +02:00
reloadUserDataButtons(page, item);
2020-05-10 13:36:26 +02:00
renderLinks(page, item);
2019-09-11 18:47:39 +02:00
renderTags(page, item);
renderSeriesAirTime(page, item, isStatic);
2018-10-23 01:05:09 +03:00
}
function enableScrollX() {
return browser.mobile && screen.availWidth <= 1000;
2018-10-23 01:05:09 +03:00
}
function getPortraitShape(scrollX) {
2020-07-30 16:07:13 +02:00
if (scrollX == null) {
scrollX = enableScrollX();
}
2020-05-04 12:44:12 +02:00
return scrollX ? 'overflowPortrait' : 'portrait';
2018-10-23 01:05:09 +03:00
}
function getSquareShape(scrollX) {
2020-07-30 16:07:13 +02:00
if (scrollX == null) {
scrollX = enableScrollX();
}
2020-05-04 12:44:12 +02:00
return scrollX ? 'overflowSquare' : 'square';
2018-10-23 01:05:09 +03:00
}
function renderMoreFromSeason(view, item, apiClient) {
2020-07-29 10:05:26 +01:00
const section = view.querySelector('.moreFromSeasonSection');
2018-10-23 01:05:09 +03:00
if (section) {
2020-07-30 16:07:13 +02:00
if (item.Type !== 'Episode' || !item.SeasonId || !item.SeriesId) {
2020-05-04 12:44:12 +02:00
return void section.classList.add('hide');
}
2020-07-29 10:05:26 +01:00
const userId = apiClient.getCurrentUserId();
2018-10-23 01:05:09 +03:00
apiClient.getEpisodes(item.SeriesId, {
SeasonId: item.SeasonId,
UserId: userId,
2020-05-04 12:44:12 +02:00
Fields: 'ItemCounts,PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount'
}).then(function (result) {
if (result.Items.length < 2) {
2020-05-04 12:44:12 +02:00
return void section.classList.add('hide');
}
2020-05-04 12:44:12 +02:00
section.classList.remove('hide');
section.querySelector('h2').innerHTML = globalize.translate('MoreFromValue', item.SeasonName);
2020-07-29 10:05:26 +01:00
const itemsContainer = section.querySelector('.itemsContainer');
2018-10-23 01:05:09 +03:00
cardBuilder.buildCards(result.Items, {
parentContainer: section,
itemsContainer: itemsContainer,
2020-05-04 12:44:12 +02:00
shape: 'autooverflow',
sectionTitleTagName: 'h2',
scalable: true,
showTitle: true,
overlayText: false,
centerText: true,
includeParentInfoInTitle: false,
allowBottomPadding: false
2018-10-23 01:05:09 +03:00
});
2020-07-29 10:05:26 +01:00
const card = itemsContainer.querySelector('.card[data-id="' + item.Id + '"]');
if (card) {
setTimeout(function () {
2020-05-04 12:44:12 +02:00
section.querySelector('.emby-scroller').toStart(card.previousSibling || card, true);
}, 100);
}
});
2018-10-23 01:05:09 +03:00
}
}
function renderMoreFromArtist(view, item, apiClient) {
2020-07-29 10:05:26 +01:00
const section = view.querySelector('.moreFromArtistSection');
2018-10-23 01:05:09 +03:00
if (section) {
2020-07-30 16:07:13 +02:00
if (item.Type === 'MusicArtist') {
2020-05-04 12:44:12 +02:00
if (!apiClient.isMinServerVersion('3.4.1.19')) {
return void section.classList.add('hide');
}
2020-07-30 16:07:13 +02:00
} else if (item.Type !== 'MusicAlbum' || !item.AlbumArtists || !item.AlbumArtists.length) {
2020-05-04 12:44:12 +02:00
return void section.classList.add('hide');
}
2020-07-29 10:05:26 +01:00
const query = {
2020-05-04 12:44:12 +02:00
IncludeItemTypes: 'MusicAlbum',
Recursive: true,
2018-10-23 01:05:09 +03:00
ExcludeItemIds: item.Id,
2020-05-04 12:44:12 +02:00
SortBy: 'ProductionYear,SortName',
SortOrder: 'Descending'
2018-10-23 01:05:09 +03:00
};
2020-07-30 16:07:13 +02:00
if (item.Type === 'MusicArtist') {
query.ContributingArtistIds = item.Id;
2020-05-04 12:44:12 +02:00
} else if (apiClient.isMinServerVersion('3.4.1.18')) {
query.AlbumArtistIds = item.AlbumArtists[0].Id;
} else {
query.ArtistIds = item.AlbumArtists[0].Id;
}
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (result) {
if (!result.Items.length) {
2020-05-04 12:44:12 +02:00
return void section.classList.add('hide');
}
2020-05-04 12:44:12 +02:00
section.classList.remove('hide');
2020-07-30 16:07:13 +02:00
if (item.Type === 'MusicArtist') {
2020-05-04 12:44:12 +02:00
section.querySelector('h2').innerHTML = globalize.translate('HeaderAppearsOn');
} else {
2020-05-04 12:44:12 +02:00
section.querySelector('h2').innerHTML = globalize.translate('MoreFromValue', item.AlbumArtists[0].Name);
}
cardBuilder.buildCards(result.Items, {
2018-10-23 01:05:09 +03:00
parentContainer: section,
2020-05-04 12:44:12 +02:00
itemsContainer: section.querySelector('.itemsContainer'),
shape: 'autooverflow',
sectionTitleTagName: 'h2',
scalable: true,
2020-07-30 16:07:13 +02:00
coverImage: item.Type === 'MusicArtist' || item.Type === 'MusicAlbum',
showTitle: true,
showParentTitle: false,
centerText: true,
overlayText: false,
overlayPlayButton: true,
showYear: true
});
});
2018-10-23 01:05:09 +03:00
}
}
function renderSimilarItems(page, item, context) {
2020-07-29 10:05:26 +01:00
const similarCollapsible = page.querySelector('#similarCollapsible');
2018-10-23 01:05:09 +03:00
if (similarCollapsible) {
2020-07-30 16:07:13 +02:00
if (item.Type != 'Movie' && item.Type != 'Trailer' && item.Type != 'Series' && item.Type != 'Program' && item.Type != 'Recording' && item.Type != 'MusicAlbum' && item.Type != 'MusicArtist' && item.Type != 'Playlist') {
2020-05-04 12:44:12 +02:00
return void similarCollapsible.classList.add('hide');
}
2020-05-04 12:44:12 +02:00
similarCollapsible.classList.remove('hide');
2020-07-29 10:05:26 +01:00
const apiClient = connectionManager.getApiClient(item.ServerId);
const options = {
userId: apiClient.getCurrentUserId(),
limit: 12,
2020-05-04 12:44:12 +02:00
fields: 'PrimaryImageAspectRatio,UserData,CanDelete'
};
2020-07-30 16:07:13 +02:00
if (item.Type == 'MusicAlbum' && item.AlbumArtists && item.AlbumArtists.length) {
options.ExcludeArtistIds = item.AlbumArtists[0].Id;
}
apiClient.getSimilarItems(item.Id, options).then(function (result) {
if (!result.Items.length) {
2020-05-04 12:44:12 +02:00
return void similarCollapsible.classList.add('hide');
}
2020-05-04 12:44:12 +02:00
similarCollapsible.classList.remove('hide');
2020-07-29 10:05:26 +01:00
let html = '';
2018-10-23 01:05:09 +03:00
html += cardBuilder.getCardsHtml({
items: result.Items,
2020-05-04 12:44:12 +02:00
shape: 'autooverflow',
2020-07-30 16:07:13 +02:00
showParentTitle: item.Type == 'MusicAlbum',
centerText: true,
showTitle: true,
2018-10-23 01:05:09 +03:00
context: context,
lazy: true,
showDetailsMenu: true,
2020-07-30 16:07:13 +02:00
coverImage: item.Type == 'MusicAlbum' || item.Type == 'MusicArtist',
overlayPlayButton: true,
overlayText: false,
2020-07-30 16:07:13 +02:00
showYear: item.Type === 'Movie' || item.Type === 'Trailer' || item.Type === 'Series'
2018-10-23 01:05:09 +03:00
});
2020-07-29 10:05:26 +01:00
const similarContent = similarCollapsible.querySelector('.similarContent');
similarContent.innerHTML = html;
imageLoader.lazyChildren(similarContent);
});
2018-10-23 01:05:09 +03:00
}
}
function renderSeriesAirTime(page, item, isStatic) {
2020-07-29 10:05:26 +01:00
const seriesAirTime = page.querySelector('#seriesAirTime');
2020-07-30 16:07:13 +02:00
if (item.Type != 'Series') {
2020-05-04 12:44:12 +02:00
seriesAirTime.classList.add('hide');
return;
}
2020-07-29 10:05:26 +01:00
let html = '';
if (item.AirDays && item.AirDays.length) {
2020-07-30 16:07:13 +02:00
if (item.AirDays.length == 7) {
2020-05-04 12:44:12 +02:00
html += 'daily';
} else {
html += item.AirDays.map(function (a) {
2020-05-04 12:44:12 +02:00
return a + 's';
}).join(',');
}
}
if (item.AirTime) {
2020-05-04 12:44:12 +02:00
html += ' at ' + item.AirTime;
}
if (item.Studios.length) {
if (isStatic) {
2020-05-04 12:44:12 +02:00
html += ' on ' + item.Studios[0].Name;
} else {
2020-07-29 10:05:26 +01:00
const context = inferContext(item);
const href = appRouter.getRouteUrl(item.Studios[0], {
context: context,
2020-05-04 12:44:12 +02:00
itemType: 'Studio',
serverId: item.ServerId
});
2020-05-04 12:44:12 +02:00
html += ' on <a class="textlink button-link" is="emby-linkbutton" href="' + href + '">' + item.Studios[0].Name + '</a>';
}
}
if (html) {
2020-07-30 16:07:13 +02:00
html = (item.Status == 'Ended' ? 'Aired ' : 'Airs ') + html;
seriesAirTime.innerHTML = html;
2020-05-04 12:44:12 +02:00
seriesAirTime.classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
seriesAirTime.classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function renderTags(page, item) {
2020-07-29 10:05:26 +01:00
const itemTags = page.querySelector('.itemTags');
const tagElements = [];
let tags = item.Tags || [];
2020-07-30 16:07:13 +02:00
if (item.Type === 'Program') {
tags = [];
}
2020-07-29 10:05:26 +01:00
for (let i = 0, length = tags.length; i < length; i++) {
tagElements.push(tags[i]);
}
if (tagElements.length) {
2020-05-04 12:44:12 +02:00
itemTags.innerHTML = globalize.translate('TagsValue', tagElements.join(', '));
itemTags.classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
itemTags.innerHTML = '';
itemTags.classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
function renderChildren(page, item) {
2020-07-29 10:05:26 +01:00
let fields = 'ItemCounts,PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount';
const query = {
ParentId: item.Id,
2018-10-23 01:05:09 +03:00
Fields: fields
};
2020-07-30 16:07:13 +02:00
if (item.Type !== 'BoxSet') {
2020-05-04 12:44:12 +02:00
query.SortBy = 'SortName';
}
2020-07-29 10:05:26 +01:00
let promise;
const apiClient = connectionManager.getApiClient(item.ServerId);
const userId = apiClient.getCurrentUserId();
2020-07-30 16:07:13 +02:00
if (item.Type == 'Series') {
promise = apiClient.getSeasons(item.Id, {
userId: userId,
Fields: fields
});
2020-07-30 16:07:13 +02:00
} else if (item.Type == 'Season') {
2020-05-04 12:44:12 +02:00
fields += ',Overview';
promise = apiClient.getEpisodes(item.SeriesId, {
seasonId: item.Id,
userId: userId,
Fields: fields
2018-10-23 01:05:09 +03:00
});
2020-07-30 16:07:13 +02:00
} else if (item.Type == 'MusicArtist') {
2020-05-04 12:44:12 +02:00
query.SortBy = 'ProductionYear,SortName';
}
promise = promise || apiClient.getItems(apiClient.getCurrentUserId(), query);
promise.then(function (result) {
2020-07-29 10:05:26 +01:00
let html = '';
let scrollX = false;
let isList = false;
const childrenItemsContainer = page.querySelector('.childrenItemsContainer');
2020-07-30 16:07:13 +02:00
if (item.Type == 'MusicAlbum') {
html = listView.getListViewHtml({
2018-10-23 01:05:09 +03:00
items: result.Items,
smallIcon: true,
showIndex: true,
2020-05-04 12:44:12 +02:00
index: 'disc',
showIndexNumberLeft: true,
playFromHere: true,
2020-05-04 12:44:12 +02:00
action: 'playallfromhere',
image: false,
2020-05-04 12:44:12 +02:00
artist: 'auto',
containerAlbumArtists: item.AlbumArtists
});
isList = true;
2020-07-30 16:07:13 +02:00
} else if (item.Type == 'Series') {
scrollX = enableScrollX();
html = cardBuilder.getCardsHtml({
2018-10-23 01:05:09 +03:00
items: result.Items,
2020-05-04 12:44:12 +02:00
shape: 'overflowPortrait',
showTitle: true,
centerText: true,
lazy: true,
overlayPlayButton: true,
allowBottomPadding: !scrollX
});
2020-07-30 16:07:13 +02:00
} else if (item.Type == 'Season' || item.Type == 'Episode') {
if (item.Type !== 'Episode') {
isList = true;
}
2020-07-30 16:07:13 +02:00
scrollX = item.Type == 'Episode';
if (result.Items.length < 2 && item.Type === 'Episode') {
return;
}
2020-07-30 16:07:13 +02:00
if (item.Type === 'Episode') {
html = cardBuilder.getCardsHtml({
items: result.Items,
2020-05-04 12:44:12 +02:00
shape: 'overflowBackdrop',
showTitle: true,
2020-07-30 16:07:13 +02:00
displayAsSpecial: item.Type == 'Season' && item.IndexNumber,
playFromHere: true,
overlayText: true,
lazy: true,
showDetailsMenu: true,
overlayPlayButton: true,
allowBottomPadding: !scrollX,
includeParentInfoInTitle: false
});
2020-07-30 16:07:13 +02:00
} else if (item.Type === 'Season') {
html = listView.getListViewHtml({
items: result.Items,
showIndexNumber: false,
enableOverview: true,
enablePlayedButton: layoutManager.mobile ? false : true,
2020-06-21 09:06:51 +02:00
infoButton: layoutManager.mobile ? false : true,
2020-05-04 12:44:12 +02:00
imageSize: 'large',
enableSideMediaInfo: false,
highlight: false,
action: !layoutManager.desktop ? 'link' : 'none',
imagePlayButton: true,
includeParentInfoInTitle: false
});
}
}
2020-07-30 16:07:13 +02:00
if (item.Type !== 'BoxSet') {
2020-05-04 12:44:12 +02:00
page.querySelector('#childrenCollapsible').classList.remove('hide');
2019-01-27 22:10:07 +01:00
}
if (scrollX) {
2020-05-04 12:44:12 +02:00
childrenItemsContainer.classList.add('scrollX');
childrenItemsContainer.classList.add('hiddenScrollX');
childrenItemsContainer.classList.remove('vertical-wrap');
childrenItemsContainer.classList.remove('vertical-list');
} else {
2020-05-04 12:44:12 +02:00
childrenItemsContainer.classList.remove('scrollX');
childrenItemsContainer.classList.remove('hiddenScrollX');
childrenItemsContainer.classList.remove('smoothScrollX');
if (isList) {
2020-05-04 12:44:12 +02:00
childrenItemsContainer.classList.add('vertical-list');
childrenItemsContainer.classList.remove('vertical-wrap');
} else {
2020-05-04 12:44:12 +02:00
childrenItemsContainer.classList.add('vertical-wrap');
childrenItemsContainer.classList.remove('vertical-list');
}
}
2020-06-20 21:06:00 +02:00
if (layoutManager.mobile) {
childrenItemsContainer.classList.remove('padded-right');
}
childrenItemsContainer.innerHTML = html;
imageLoader.lazyChildren(childrenItemsContainer);
2020-07-30 16:07:13 +02:00
if (item.Type == 'BoxSet') {
2020-07-29 10:05:26 +01:00
const collectionItemTypes = [{
2020-05-04 12:44:12 +02:00
name: globalize.translate('HeaderVideos'),
mediaType: 'Video'
2018-10-23 01:05:09 +03:00
}, {
2020-08-13 21:56:01 +09:00
name: globalize.translate('Series'),
2020-05-04 12:44:12 +02:00
type: 'Series'
2018-10-23 01:05:09 +03:00
}, {
2020-08-13 21:23:51 +09:00
name: globalize.translate('Albums'),
2020-05-04 12:44:12 +02:00
type: 'MusicAlbum'
2018-10-23 01:05:09 +03:00
}, {
2020-08-16 20:34:39 +09:00
name: globalize.translate('Books'),
2020-05-04 12:44:12 +02:00
type: 'Book'
2018-10-23 01:05:09 +03:00
}];
renderCollectionItems(page, item, collectionItemTypes, result.Items);
2018-10-23 01:05:09 +03:00
}
});
2020-07-30 16:07:13 +02:00
if (item.Type == 'Season') {
2020-08-16 20:34:39 +09:00
page.querySelector('#childrenTitle').innerHTML = globalize.translate('Episodes');
2020-07-30 16:07:13 +02:00
} else if (item.Type == 'Series') {
2020-05-04 12:44:12 +02:00
page.querySelector('#childrenTitle').innerHTML = globalize.translate('HeaderSeasons');
2020-07-30 16:07:13 +02:00
} else if (item.Type == 'MusicAlbum') {
2020-05-04 12:44:12 +02:00
page.querySelector('#childrenTitle').innerHTML = globalize.translate('HeaderTracks');
} else {
2020-08-13 21:31:29 +09:00
page.querySelector('#childrenTitle').innerHTML = globalize.translate('Items');
}
2020-07-30 16:07:13 +02:00
if (item.Type == 'MusicAlbum' || item.Type == 'Season') {
2020-05-04 12:44:12 +02:00
page.querySelector('.childrenSectionHeader').classList.add('hide');
page.querySelector('#childrenCollapsible').classList.add('verticalSection-extrabottompadding');
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('.childrenSectionHeader').classList.remove('hide');
}
2018-10-23 01:05:09 +03:00
}
2020-03-01 15:33:41 +01:00
function renderItemsByName(page, item) {
2020-07-29 10:05:26 +01:00
import('scripts/itembynamedetailpage').then(() => {
window.ItemsByName.renderItems(page, item);
});
2018-10-23 01:05:09 +03:00
}
2020-03-01 15:33:41 +01:00
function renderPlaylistItems(page, item) {
2020-07-29 10:05:26 +01:00
import('scripts/playlistedit').then(() => {
PlaylistViewer.render(page, item);
});
2018-10-23 01:05:09 +03:00
}
function renderProgramsForChannel(page, result) {
2020-07-29 10:05:26 +01:00
let html = '';
let currentItems = [];
let currentStartDate = null;
2020-07-29 10:05:26 +01:00
for (let i = 0, length = result.Items.length; i < length; i++) {
const item = result.Items[i];
const itemStartDate = datetime.parseISO8601Date(item.StartDate);
if (!(currentStartDate && currentStartDate.toDateString() === itemStartDate.toDateString())) {
if (currentItems.length) {
html += '<div class="verticalSection verticalDetailSection">';
html += '<h2 class="sectionTitle padded-left">' + datetime.toLocaleDateString(currentStartDate, {
2020-05-04 12:44:12 +02:00
weekday: 'long',
month: 'long',
day: 'numeric'
}) + '</h2>';
html += '<div is="emby-itemscontainer" class="vertical-list padded-left padded-right">' + listView.getListViewHtml({
items: currentItems,
enableUserDataButtons: false,
showParentTitle: true,
image: false,
showProgramTime: true,
mediaInfo: false,
parentTitleWithTitle: true
2020-05-04 12:44:12 +02:00
}) + '</div></div>';
}
currentStartDate = itemStartDate;
currentItems = [];
}
currentItems.push(item);
}
if (currentItems.length) {
html += '<div class="verticalSection verticalDetailSection">';
html += '<h2 class="sectionTitle padded-left">' + datetime.toLocaleDateString(currentStartDate, {
2020-05-04 12:44:12 +02:00
weekday: 'long',
month: 'long',
day: 'numeric'
}) + '</h2>';
html += '<div is="emby-itemscontainer" class="vertical-list padded-left padded-right">' + listView.getListViewHtml({
2018-10-23 01:05:09 +03:00
items: currentItems,
enableUserDataButtons: false,
showParentTitle: true,
image: false,
showProgramTime: true,
mediaInfo: false,
parentTitleWithTitle: true
2020-05-04 12:44:12 +02:00
}) + '</div></div>';
}
2020-05-04 12:44:12 +02:00
page.querySelector('.programGuide').innerHTML = html;
2018-10-23 01:05:09 +03:00
}
function renderChannelGuide(page, apiClient, item) {
2020-07-30 16:07:13 +02:00
if (item.Type === 'TvChannel') {
2020-05-04 12:44:12 +02:00
page.querySelector('.programGuideSection').classList.remove('hide');
apiClient.getLiveTvPrograms({
ChannelIds: item.Id,
UserId: apiClient.getCurrentUserId(),
HasAired: false,
2020-05-04 12:44:12 +02:00
SortBy: 'StartDate',
EnableTotalRecordCount: false,
EnableImages: false,
ImageTypeLimit: 0,
EnableUserData: false
}).then(function (result) {
renderProgramsForChannel(page, result);
});
}
2018-10-23 01:05:09 +03:00
}
2020-03-01 15:33:41 +01:00
function renderSeriesSchedule(page, item) {
2020-07-29 10:05:26 +01:00
const apiClient = connectionManager.getApiClient(item.ServerId);
2018-10-23 01:05:09 +03:00
apiClient.getLiveTvPrograms({
UserId: apiClient.getCurrentUserId(),
HasAired: false,
2020-05-04 12:44:12 +02:00
SortBy: 'StartDate',
EnableTotalRecordCount: false,
EnableImages: false,
2018-10-23 01:05:09 +03:00
ImageTypeLimit: 0,
Limit: 50,
EnableUserData: false,
2018-10-23 01:05:09 +03:00
LibrarySeriesId: item.Id
}).then(function (result) {
if (result.Items.length) {
2020-05-04 12:44:12 +02:00
page.querySelector('#seriesScheduleSection').classList.remove('hide');
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('#seriesScheduleSection').classList.add('hide');
}
2020-05-04 12:44:12 +02:00
page.querySelector('#seriesScheduleList').innerHTML = listView.getListViewHtml({
2018-10-23 01:05:09 +03:00
items: result.Items,
enableUserDataButtons: false,
showParentTitle: false,
image: false,
showProgramDateTime: true,
mediaInfo: false,
showTitle: true,
moreButton: false,
2020-05-04 12:44:12 +02:00
action: 'programdialog'
});
loading.hide();
});
2018-10-23 01:05:09 +03:00
}
function inferContext(item) {
2020-07-30 16:07:13 +02:00
if (item.Type === 'Movie' || item.Type === 'BoxSet') {
2020-05-04 12:44:12 +02:00
return 'movies';
}
2020-07-30 16:07:13 +02:00
if (item.Type === 'Series' || item.Type === 'Season' || item.Type === 'Episode') {
2020-05-04 12:44:12 +02:00
return 'tvshows';
}
2020-07-30 16:07:13 +02:00
if (item.Type === 'MusicArtist' || item.Type === 'MusicAlbum' || item.Type === 'Audio' || item.Type === 'AudioBook') {
2020-05-04 12:44:12 +02:00
return 'music';
}
2020-07-30 16:07:13 +02:00
if (item.Type === 'Program') {
2020-05-04 12:44:12 +02:00
return 'livetv';
}
return null;
2018-10-23 01:05:09 +03:00
}
function filterItemsByCollectionItemType(items, typeInfo) {
return items.filter(function (item) {
if (typeInfo.mediaType) {
return item.MediaType == typeInfo.mediaType;
}
return item.Type == typeInfo.type;
});
2018-10-23 01:05:09 +03:00
}
function canPlaySomeItemInCollection(items) {
2020-07-29 10:05:26 +01:00
let i = 0;
2020-07-29 10:05:26 +01:00
for (let length = items.length; i < length; i++) {
if (playbackManager.canPlay(items[i])) {
return true;
}
}
return false;
}
2018-10-23 01:05:09 +03:00
function renderCollectionItems(page, parentItem, types, items) {
page.querySelector('.collectionItems').classList.remove('hide');
2020-05-04 12:44:12 +02:00
page.querySelector('.collectionItems').innerHTML = '';
for (const type of types) {
2020-07-29 10:05:26 +01:00
const typeItems = filterItemsByCollectionItemType(items, type);
if (typeItems.length) {
renderCollectionItemType(page, parentItem, type, typeItems);
}
2018-10-23 01:05:09 +03:00
}
2020-07-29 10:05:26 +01:00
const otherType = {
2020-05-04 12:44:12 +02:00
name: globalize.translate('HeaderOtherItems')
};
2020-07-29 10:05:26 +01:00
const otherTypeItems = items.filter(function (curr) {
return !types.filter(function (t) {
return filterItemsByCollectionItemType([curr], t).length > 0;
}).length;
});
if (otherTypeItems.length) {
renderCollectionItemType(page, parentItem, otherType, otherTypeItems);
}
if (!items.length) {
renderCollectionItemType(page, parentItem, {
2020-08-13 21:31:29 +09:00
name: globalize.translate('Items')
}, items);
}
2020-07-29 10:05:26 +01:00
const containers = page.querySelectorAll('.collectionItemsContainer');
2020-07-29 10:05:26 +01:00
const notifyRefreshNeeded = function () {
renderChildren(page, parentItem);
};
for (const container of containers) {
container.notifyRefreshNeeded = notifyRefreshNeeded;
}
// if nothing in the collection can be played hide play and shuffle buttons
if (!canPlaySomeItemInCollection(items)) {
2020-05-04 12:44:12 +02:00
hideAll(page, 'btnPlay', false);
hideAll(page, 'btnShuffle', false);
}
2019-11-14 20:30:59 +03:00
// HACK: Call autoFocuser again because btnPlay may be hidden, but focused by reloadFromItem
// FIXME: Sometimes focus does not move until all (?) sections are loaded
2020-07-29 10:05:26 +01:00
import('autoFocuser').then(({default: autoFocuser}) => {
2019-11-14 20:30:59 +03:00
autoFocuser.autoFocus(page);
});
2018-10-23 01:05:09 +03:00
}
function renderCollectionItemType(page, parentItem, type, items) {
2020-07-29 10:05:26 +01:00
let html = '';
html += '<div class="verticalSection">';
html += '<div class="sectionTitleContainer sectionTitleContainer-cards padded-left">';
html += '<h2 class="sectionTitle sectionTitle-cards">';
2020-05-04 12:44:12 +02:00
html += '<span>' + type.name + '</span>';
html += '</h2>';
html += '</div>';
html += '<div is="emby-itemscontainer" class="itemsContainer collectionItemsContainer vertical-wrap padded-left padded-right">';
const shape = type.type == 'MusicAlbum' ? getSquareShape(false) : getPortraitShape(false);
2018-10-23 01:05:09 +03:00
html += cardBuilder.getCardsHtml({
items: items,
shape: shape,
showTitle: true,
2020-07-30 16:07:13 +02:00
showYear: type.mediaType === 'Video' || type.type === 'Series',
centerText: true,
lazy: true,
showDetailsMenu: true,
overlayMoreButton: true,
showAddToCollection: false,
showRemoveFromCollection: true,
2018-10-23 01:05:09 +03:00
collectionId: parentItem.Id
});
2020-05-04 12:44:12 +02:00
html += '</div>';
html += '</div>';
2020-07-29 10:05:26 +01:00
const collectionItems = page.querySelector('.collectionItems');
2020-05-04 12:44:12 +02:00
collectionItems.insertAdjacentHTML('beforeend', html);
imageLoader.lazyChildren(collectionItems);
2018-10-23 01:05:09 +03:00
}
function renderMusicVideos(page, item, user) {
connectionManager.getApiClient(item.ServerId).getItems(user.Id, {
2020-05-04 12:44:12 +02:00
SortBy: 'SortName',
SortOrder: 'Ascending',
IncludeItemTypes: 'MusicVideo',
Recursive: true,
2020-05-04 12:44:12 +02:00
Fields: 'PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount',
2018-10-23 01:05:09 +03:00
AlbumIds: item.Id
}).then(function (result) {
2018-10-23 01:05:09 +03:00
if (result.Items.length) {
2020-05-04 12:44:12 +02:00
page.querySelector('#musicVideosCollapsible').classList.remove('hide');
2020-07-29 10:05:26 +01:00
const musicVideosContent = page.querySelector('.musicVideosContent');
2020-07-24 01:33:44 +03:00
musicVideosContent.innerHTML = getVideosHtml(result.Items);
imageLoader.lazyChildren(musicVideosContent);
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('#musicVideosCollapsible').classList.add('hide');
}
});
2018-10-23 01:05:09 +03:00
}
function renderAdditionalParts(page, item, user) {
connectionManager.getApiClient(item.ServerId).getAdditionalVideoParts(user.Id, item.Id).then(function (result) {
2018-10-23 01:05:09 +03:00
if (result.Items.length) {
2020-05-04 12:44:12 +02:00
page.querySelector('#additionalPartsCollapsible').classList.remove('hide');
2020-07-29 10:05:26 +01:00
const additionalPartsContent = page.querySelector('#additionalPartsContent');
2020-07-24 01:33:44 +03:00
additionalPartsContent.innerHTML = getVideosHtml(result.Items);
imageLoader.lazyChildren(additionalPartsContent);
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('#additionalPartsCollapsible').classList.add('hide');
}
});
2018-10-23 01:05:09 +03:00
}
function renderScenes(page, item) {
2020-07-29 10:05:26 +01:00
let chapters = item.Chapters || [];
2018-10-23 01:05:09 +03:00
if (chapters.length && !chapters[0].ImageTag && (chapters = []), chapters.length) {
2020-05-04 12:44:12 +02:00
page.querySelector('#scenesCollapsible').classList.remove('hide');
2020-07-29 10:05:26 +01:00
const scenesContent = page.querySelector('#scenesContent');
2020-07-29 10:05:26 +01:00
import('chaptercardbuilder').then(({default: chaptercardbuilder}) => {
2018-10-23 01:05:09 +03:00
chaptercardbuilder.buildChapterCards(item, chapters, {
itemsContainer: scenesContent,
2020-05-04 12:44:12 +02:00
backdropShape: 'overflowBackdrop',
2020-06-09 00:48:56 +02:00
squareShape: 'overflowSquare',
imageBlurhashes: item.ImageBlurHashes
});
});
} else {
2020-05-04 12:44:12 +02:00
page.querySelector('#scenesCollapsible').classList.add('hide');
}
2018-10-23 01:05:09 +03:00
}
2020-07-24 00:16:17 +03:00
function getVideosHtml(items) {
2020-07-30 19:42:30 +02:00
return cardBuilder.getCardsHtml({
2018-10-23 01:05:09 +03:00
items: items,
2020-07-24 01:20:35 +03:00
shape: 'autooverflow',
showTitle: true,
2020-05-04 12:44:12 +02:00
action: 'play',
overlayText: false,
centerText: true,
showRuntime: true
2018-10-23 01:05:09 +03:00
});
}
2020-07-24 00:16:17 +03:00
function renderSpecials(page, item, user) {
connectionManager.getApiClient(item.ServerId).getSpecialFeatures(user.Id, item.Id).then(function (specials) {
2020-07-29 10:05:26 +01:00
const specialsContent = page.querySelector('#specialsContent');
2020-07-24 00:16:17 +03:00
specialsContent.innerHTML = getVideosHtml(specials);
imageLoader.lazyChildren(specialsContent);
});
2018-10-23 01:05:09 +03:00
}
2020-03-01 15:33:41 +01:00
function renderCast(page, item) {
2020-07-29 10:05:26 +01:00
const people = (item.People || []).filter(function (p) {
return p.Type === 'Actor';
2018-10-23 01:05:09 +03:00
});
if (!people.length) {
2020-05-04 12:44:12 +02:00
return void page.querySelector('#castCollapsible').classList.add('hide');
}
2020-05-04 12:44:12 +02:00
page.querySelector('#castCollapsible').classList.remove('hide');
2020-07-29 10:05:26 +01:00
const castContent = page.querySelector('#castContent');
2020-07-29 10:05:26 +01:00
import('peoplecardbuilder').then(({default: peoplecardbuilder}) => {
2018-10-23 01:05:09 +03:00
peoplecardbuilder.buildPeopleCards(people, {
itemsContainer: castContent,
coverImage: true,
2018-10-23 01:05:09 +03:00
serverId: item.ServerId,
shape: 'overflowPortrait',
imageBlurhashes: item.ImageBlurHashes
});
});
2018-10-23 01:05:09 +03:00
}
function itemDetailPage() {
2020-07-29 10:05:26 +01:00
const self = this;
self.setInitialCollapsibleState = setInitialCollapsibleState;
self.renderDetails = renderDetails;
self.renderCast = renderCast;
2018-10-23 01:05:09 +03:00
}
function bindAll(view, selector, eventName, fn) {
2020-07-29 10:05:26 +01:00
const elems = view.querySelectorAll(selector);
for (const elem of elems) {
elem.addEventListener(eventName, fn);
}
2018-10-23 01:05:09 +03:00
}
function onTrackSelectionsSubmit(e) {
e.preventDefault();
return false;
}
2018-10-23 01:05:09 +03:00
window.ItemDetailPage = new itemDetailPage();
2020-07-29 10:05:26 +01:00
export default function (view, params) {
function reload(instance, page, params) {
loading.show();
2020-05-10 13:36:26 +02:00
2020-07-29 10:05:26 +01:00
const apiClient = params.serverId ? connectionManager.getApiClient(params.serverId) : ApiClient;
2020-05-10 13:36:26 +02:00
Promise.all([getPromise(apiClient, params), apiClient.getCurrentUser()]).then(([item, user]) => {
currentItem = item;
reloadFromItem(instance, page, params, item, user);
2020-05-10 13:36:26 +02:00
}).catch((error) => {
console.error('failed to get item or current user: ', error);
});
}
2018-10-23 01:05:09 +03:00
function splitVersions(instance, page, apiClient, params) {
2020-07-29 10:05:26 +01:00
import('confirm').then(({default: confirm}) => {
confirm('Are you sure you wish to split the media sources into separate items?', 'Split Media Apart').then(function () {
loading.show();
apiClient.ajax({
2020-05-04 12:44:12 +02:00
type: 'DELETE',
url: apiClient.getUrl('Videos/' + params.id + '/AlternateSources')
}).then(function () {
loading.hide();
reload(instance, page, params);
});
});
});
}
2018-10-23 01:05:09 +03:00
function getPlayOptions(startPosition) {
2020-07-29 10:05:26 +01:00
const audioStreamIndex = view.querySelector('.selectAudio').value || null;
return {
startPositionTicks: startPosition,
2020-05-04 12:44:12 +02:00
mediaSourceId: view.querySelector('.selectSource').value,
audioStreamIndex: audioStreamIndex,
2020-05-04 12:44:12 +02:00
subtitleStreamIndex: view.querySelector('.selectSubtitles').value
};
}
2018-10-23 01:05:09 +03:00
function playItem(item, startPosition) {
2020-07-29 10:05:26 +01:00
const playOptions = getPlayOptions(startPosition);
playOptions.items = [item];
playbackManager.play(playOptions);
}
2018-10-23 01:05:09 +03:00
2020-03-01 15:33:41 +01:00
function playTrailer() {
playbackManager.playTrailers(currentItem);
}
2018-10-23 01:05:09 +03:00
function playCurrentItem(button, mode) {
2020-07-29 10:05:26 +01:00
const item = currentItem;
2018-10-23 01:05:09 +03:00
2020-07-30 16:07:13 +02:00
if (item.Type === 'Program') {
2020-07-29 10:05:26 +01:00
const apiClient = connectionManager.getApiClient(item.ServerId);
return void apiClient.getLiveTvChannel(item.ChannelId, apiClient.getCurrentUserId()).then(function (channel) {
playbackManager.play({
items: [channel]
});
});
2018-10-23 01:05:09 +03:00
}
2020-05-08 19:50:56 +02:00
playItem(item, item.UserData && mode === 'resume' ? item.UserData.PlaybackPositionTicks : 0);
}
2018-10-23 01:05:09 +03:00
function onPlayClick() {
2020-05-04 12:44:12 +02:00
playCurrentItem(this, this.getAttribute('data-mode'));
}
2018-10-23 01:05:09 +03:00
function onPosterClick(e) {
itemShortcuts.onClick.call(view.querySelector('.detailImageContainer'), e);
}
function onInstantMixClick() {
playbackManager.instantMix(currentItem);
}
2018-10-23 01:05:09 +03:00
function onShuffleClick() {
playbackManager.shuffle(currentItem);
}
2018-10-23 01:05:09 +03:00
function onCancelSeriesTimerClick() {
2020-07-29 10:05:26 +01:00
import('recordingHelper').then(({default: recordingHelper}) => {
recordingHelper.cancelSeriesTimerWithConfirmation(currentItem.Id, currentItem.ServerId).then(function () {
2020-05-04 12:44:12 +02:00
Dashboard.navigate('livetv.html');
});
});
}
function onCancelTimerClick() {
2020-07-29 10:05:26 +01:00
import('recordingHelper').then(({default: recordingHelper}) => {
recordingHelper.cancelTimer(connectionManager.getApiClient(currentItem.ServerId), currentItem.TimerId).then(function () {
reload(self, view, params);
});
});
}
function onPlayTrailerClick() {
2020-03-01 15:33:41 +01:00
playTrailer();
}
function onDownloadClick() {
2020-07-29 10:05:26 +01:00
import('fileDownloader').then(({default: fileDownloader}) => {
const downloadHref = apiClient.getItemDownloadUrl(currentItem.Id);
fileDownloader.download([{
url: downloadHref,
itemId: currentItem.Id,
serverId: currentItem.serverId
}]);
});
}
function onMoreCommandsClick() {
var button = this;
var selectedItem = currentItem;
2020-08-18 21:35:47 +02:00
apiClient.getItem(apiClient.getCurrentUserId(), view.querySelector('.selectSource').value).then(function (item) {
selectedItem = item;
apiClient.getCurrentUser().then(function (user) {
itemContextMenu.show(getContextMenuOptions(selectedItem, user, button)).then(function (result) {
if (result.deleted) {
appRouter.goHome();
} else if (result.updated) {
reload(self, view, params);
}
});
});
});
}
function onPlayerChange() {
renderTrackSelections(view, self, currentItem);
setTrailerButtonVisibility(view, currentItem);
}
2018-10-23 01:05:09 +03:00
function onWebSocketMessage(e, data) {
2020-07-29 10:05:26 +01:00
const msg = data;
2018-10-23 01:05:09 +03:00
2020-07-30 16:07:13 +02:00
if (msg.MessageType === 'UserDataChanged' && currentItem && msg.Data.UserId == apiClient.getCurrentUserId()) {
2020-07-29 10:05:26 +01:00
const key = currentItem.UserData.Key;
const userData = msg.Data.UserDataList.filter(function (u) {
return u.Key == key;
})[0];
2018-10-23 01:05:09 +03:00
if (userData) {
currentItem.UserData = userData;
reloadPlayButtons(view, currentItem);
2020-03-01 15:33:41 +01:00
refreshImage(view, currentItem);
}
2018-10-23 01:05:09 +03:00
}
}
2018-10-23 01:05:09 +03:00
2020-07-29 10:05:26 +01:00
let currentItem;
const self = this;
const apiClient = params.serverId ? connectionManager.getApiClient(params.serverId) : ApiClient;
2020-05-04 12:44:12 +02:00
view.querySelectorAll('.btnPlay');
bindAll(view, '.btnPlay', 'click', onPlayClick);
bindAll(view, '.btnResume', 'click', onPlayClick);
bindAll(view, '.btnInstantMix', 'click', onInstantMixClick);
bindAll(view, '.btnShuffle', 'click', onShuffleClick);
bindAll(view, '.btnPlayTrailer', 'click', onPlayTrailerClick);
bindAll(view, '.btnCancelSeriesTimer', 'click', onCancelSeriesTimerClick);
bindAll(view, '.btnCancelTimer', 'click', onCancelTimerClick);
bindAll(view, '.btnDownload', 'click', onDownloadClick);
view.querySelector('.detailImageContainer').addEventListener('click', onPosterClick);
2020-05-04 12:44:12 +02:00
view.querySelector('.trackSelections').addEventListener('submit', onTrackSelectionsSubmit);
view.querySelector('.btnSplitVersions').addEventListener('click', function () {
splitVersions(self, view, apiClient, params);
});
2020-05-04 12:44:12 +02:00
bindAll(view, '.btnMoreCommands', 'click', onMoreCommandsClick);
view.querySelector('.selectSource').addEventListener('change', function () {
renderVideoSelections(view, self._currentPlaybackMediaSources);
renderAudioSelections(view, self._currentPlaybackMediaSources);
renderSubtitleSelections(view, self._currentPlaybackMediaSources);
});
2020-05-04 12:44:12 +02:00
view.addEventListener('viewshow', function (e) {
2020-07-29 10:05:26 +01:00
const page = this;
libraryMenu.setTransparentMenu(true);
if (e.detail.isRestored) {
if (currentItem) {
Emby.Page.setTitle('');
renderTrackSelections(page, self, currentItem, true);
2018-10-23 01:05:09 +03:00
}
} else {
reload(self, page, params);
2018-10-23 01:05:09 +03:00
}
2019-03-18 22:22:48 -07:00
2020-05-04 12:44:12 +02:00
events.on(apiClient, 'message', onWebSocketMessage);
events.on(playbackManager, 'playerchange', onPlayerChange);
});
2020-05-04 12:44:12 +02:00
view.addEventListener('viewbeforehide', function () {
events.off(apiClient, 'message', onWebSocketMessage);
events.off(playbackManager, 'playerchange', onPlayerChange);
libraryMenu.setTransparentMenu(false);
});
2020-05-04 12:44:12 +02:00
view.addEventListener('viewdestroy', function () {
currentItem = null;
self._currentPlaybackMediaSources = null;
self.currentRecordingFields = null;
});
2020-07-29 10:05:26 +01:00
}
/* eslint-enable indent */