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/dashboard/dashboard.js

879 lines
38 KiB
JavaScript
Raw Normal View History

2020-08-14 08:46:34 +02:00
import datetime from '../../scripts/datetime';
import { Events } from 'jellyfin-apiclient';
2020-08-14 08:46:34 +02:00
import itemHelper from '../../components/itemHelper';
import serverNotifications from '../../scripts/serverNotifications';
import dom from '../../scripts/dom';
import globalize from '../../scripts/globalize';
import { formatDistanceToNow } from 'date-fns';
import { localeWithSuffix } from '../../scripts/dfnshelper';
import loading from '../../components/loading/loading';
import playMethodHelper from '../../components/playback/playmethodhelper';
import cardBuilder from '../../components/cardbuilder/cardBuilder';
import imageLoader from '../../components/images/imageLoader';
import ActivityLog from '../../components/activitylog';
import imageHelper from '../../scripts/imagehelper';
import indicators from '../../components/indicators/indicators';
import '../../components/listview/listview.css';
import '../../elements/emby-button/emby-button';
import '../../assets/css/flexstyles.css';
import '../../elements/emby-itemscontainer/emby-itemscontainer';
import taskButton from '../../scripts/taskbutton';
import Dashboard from '../../scripts/clientUtils';
import ServerConnections from '../../components/ServerConnections';
/* eslint-disable indent */
2018-10-23 01:05:09 +03:00
function showPlaybackInfo(btn, session) {
2020-08-14 08:46:34 +02:00
import('../../components/alert').then(({default: alert}) => {
2020-07-11 11:56:57 +01:00
let title;
2020-07-19 17:38:42 +02:00
const text = [];
2020-07-11 11:56:57 +01:00
const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
2020-05-04 12:44:12 +02:00
if (displayPlayMethod === 'DirectStream') {
title = globalize.translate('DirectStreaming');
text.push(globalize.translate('DirectStreamHelp1'));
text.push('<br/>');
text.push(globalize.translate('DirectStreamHelp2'));
} else if (displayPlayMethod === 'Transcode') {
title = globalize.translate('Transcoding');
text.push(globalize.translate('MediaIsBeingConverted'));
2019-01-10 22:10:45 +01:00
if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length) {
2020-05-04 12:44:12 +02:00
text.push('<br/>');
text.push(globalize.translate('LabelReasonForTranscoding'));
2019-01-14 22:52:33 +01:00
session.TranscodingInfo.TranscodeReasons.forEach(function (transcodeReason) {
2019-09-05 11:42:38 -07:00
text.push(globalize.translate(transcodeReason));
2019-01-14 22:52:33 +01:00
});
2019-01-10 21:37:02 +01:00
}
}
2019-01-10 21:37:02 +01:00
alert({
2020-05-04 12:44:12 +02:00
text: text.join('<br/>'),
2018-10-23 01:05:09 +03:00
title: title
2019-01-10 21:37:02 +01:00
});
});
2018-10-23 01:05:09 +03:00
}
function showSendMessageForm(btn, session) {
2020-08-14 08:46:34 +02:00
import('../../components/prompt/prompt').then(({default: prompt}) => {
2018-10-23 01:05:09 +03:00
prompt({
2020-05-04 12:44:12 +02:00
title: globalize.translate('HeaderSendMessage'),
label: globalize.translate('LabelMessageText'),
confirmText: globalize.translate('ButtonSend')
2019-01-10 21:37:02 +01:00
}).then(function (text) {
2018-10-23 01:05:09 +03:00
if (text) {
ServerConnections.getApiClient(session.ServerId).sendMessageCommand(session.Id, {
2018-10-23 01:05:09 +03:00
Text: text,
TimeoutMs: 5e3
2019-01-10 21:37:02 +01:00
});
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
});
});
2018-10-23 01:05:09 +03:00
}
function showOptionsMenu(btn, session) {
2020-08-14 08:46:34 +02:00
import('../../components/actionSheet/actionSheet').then(({default: actionsheet}) => {
2020-07-11 11:56:57 +01:00
const menuItems = [];
2019-01-10 21:37:02 +01:00
if (session.ServerId && session.DeviceId !== ServerConnections.deviceId()) {
2019-01-10 21:37:02 +01:00
menuItems.push({
2020-05-04 12:44:12 +02:00
name: globalize.translate('SendMessage'),
id: 'sendmessage'
2019-01-10 21:37:02 +01:00
});
}
if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length) {
menuItems.push({
2020-05-04 12:44:12 +02:00
name: globalize.translate('ViewPlaybackInfo'),
id: 'transcodinginfo'
2019-01-10 21:37:02 +01:00
});
}
return actionsheet.show({
2018-10-23 01:05:09 +03:00
items: menuItems,
positionTo: btn
2019-01-10 21:37:02 +01:00
}).then(function (id) {
2018-10-23 01:05:09 +03:00
switch (id) {
2020-05-04 12:44:12 +02:00
case 'sendmessage':
2018-10-23 01:05:09 +03:00
showSendMessageForm(btn, session);
break;
2020-05-04 12:44:12 +02:00
case 'transcodinginfo':
2019-01-10 21:37:02 +01:00
showPlaybackInfo(btn, session);
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
});
});
2018-10-23 01:05:09 +03:00
}
2019-01-10 22:10:45 +01:00
function onActiveDevicesClick(evt) {
2020-07-11 11:56:57 +01:00
const btn = dom.parentWithClass(evt.target, 'sessionCardButton');
2019-01-10 21:37:02 +01:00
2018-10-23 01:05:09 +03:00
if (btn) {
2020-07-11 11:56:57 +01:00
const card = dom.parentWithClass(btn, 'card');
2019-01-10 21:37:02 +01:00
2018-10-23 01:05:09 +03:00
if (card) {
2020-07-11 11:56:57 +01:00
const sessionId = card.id;
const session = (DashboardPage.sessionsList || []).filter(function (dashboardSession) {
2020-05-04 12:44:12 +02:00
return 'session' + dashboardSession.Id === sessionId;
2019-01-10 21:37:02 +01:00
})[0];
if (session) {
2020-05-04 12:44:12 +02:00
if (btn.classList.contains('btnCardOptions')) {
2019-01-10 21:37:02 +01:00
showOptionsMenu(btn, session);
2020-05-04 12:44:12 +02:00
} else if (btn.classList.contains('btnSessionInfo')) {
2019-01-10 22:10:45 +01:00
showPlaybackInfo(btn, session);
2020-05-04 12:44:12 +02:00
} else if (btn.classList.contains('btnSessionSendMessage')) {
2019-01-10 22:10:45 +01:00
showSendMessageForm(btn, session);
2020-05-04 12:44:12 +02:00
} else if (btn.classList.contains('btnSessionStop')) {
ServerConnections.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'Stop');
2020-05-04 12:44:12 +02:00
} else if (btn.classList.contains('btnSessionPlayPause') && session.PlayState) {
ServerConnections.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'PlayPause');
2019-01-10 21:37:02 +01:00
}
}
2018-10-23 01:05:09 +03:00
}
}
}
function filterSessions(sessions) {
2020-07-11 11:56:57 +01:00
const list = [];
const minActiveDate = new Date().getTime() - 9e5;
2019-01-10 21:37:02 +01:00
2020-07-11 11:56:57 +01:00
for (let i = 0, length = sessions.length; i < length; i++) {
const session = sessions[i];
2019-01-10 21:37:02 +01:00
2019-01-11 17:10:05 +01:00
if (!session.NowPlayingItem && !session.UserId) {
continue;
}
if (datetime.parseISO8601Date(session.LastActivityDate, true).getTime() >= minActiveDate) {
list.push(session);
2018-10-23 01:05:09 +03:00
}
}
2019-01-10 21:37:02 +01:00
return list;
2018-10-23 01:05:09 +03:00
}
function refreshActiveRecordings(view, apiClient) {
apiClient.getLiveTvRecordings({
UserId: Dashboard.getCurrentUserId(),
2019-01-10 21:37:02 +01:00
IsInProgress: true,
2020-05-04 12:44:12 +02:00
Fields: 'CanDelete,PrimaryImageAspectRatio',
2019-01-10 21:37:02 +01:00
EnableTotalRecordCount: false,
2020-05-04 12:44:12 +02:00
EnableImageTypes: 'Primary,Thumb,Backdrop'
2019-01-10 21:37:02 +01:00
}).then(function (result) {
2020-07-11 11:56:57 +01:00
const itemsContainer = view.querySelector('.activeRecordingItems');
2019-01-10 21:37:02 +01:00
if (!result.Items.length) {
2020-05-04 12:44:12 +02:00
view.querySelector('.activeRecordingsSection').classList.add('hide');
return void(itemsContainer.innerHTML = '');
2019-01-10 21:37:02 +01:00
}
2020-05-04 12:44:12 +02:00
view.querySelector('.activeRecordingsSection').classList.remove('hide');
2018-10-23 01:05:09 +03:00
itemsContainer.innerHTML = cardBuilder.getCardsHtml({
items: result.Items,
2020-05-04 12:44:12 +02:00
shape: 'auto',
defaultShape: 'backdrop',
2019-01-10 21:37:02 +01:00
showTitle: true,
showParentTitle: true,
coverImage: true,
cardLayout: false,
centerText: true,
2020-05-04 12:44:12 +02:00
preferThumb: 'auto',
2019-01-10 21:37:02 +01:00
overlayText: false,
overlayMoreButton: true,
2020-05-04 12:44:12 +02:00
action: 'none',
2019-01-10 21:37:02 +01:00
centerPlayButton: true
});
imageLoader.lazyChildren(itemsContainer);
});
2018-10-23 01:05:09 +03:00
}
function reloadSystemInfo(view, apiClient) {
2019-01-10 21:37:02 +01:00
apiClient.getSystemInfo().then(function (systemInfo) {
2020-05-04 12:44:12 +02:00
view.querySelector('#serverName').innerHTML = globalize.translate('DashboardServerName', systemInfo.ServerName);
2020-07-12 04:24:45 +09:00
view.querySelector('#versionNumber').innerHTML = globalize.translate('DashboardVersionNumber', systemInfo.Version);
2020-05-04 12:44:12 +02:00
view.querySelector('#operatingSystem').innerHTML = globalize.translate('DashboardOperatingSystem', systemInfo.OperatingSystem);
view.querySelector('#architecture').innerHTML = globalize.translate('DashboardArchitecture', systemInfo.SystemArchitecture);
2020-05-04 12:44:12 +02:00
view.querySelector('#cachePath').innerHTML = systemInfo.CachePath;
view.querySelector('#logPath').innerHTML = systemInfo.LogPath;
view.querySelector('#transcodePath').innerHTML = systemInfo.TranscodingTempPath;
view.querySelector('#metadataPath').innerHTML = systemInfo.InternalMetadataPath;
view.querySelector('#webPath').innerHTML = systemInfo.WebPath;
2019-01-10 21:37:02 +01:00
});
2018-10-23 01:05:09 +03:00
}
function renderInfo(view, sessions, forceUpdate) {
2019-01-10 21:37:02 +01:00
sessions = filterSessions(sessions);
renderActiveConnections(view, sessions);
loading.hide();
2018-10-23 01:05:09 +03:00
}
function pollForInfo(view, apiClient, forceUpdate) {
apiClient.getSessions({
ActiveWithinSeconds: 960
2019-01-10 21:37:02 +01:00
}).then(function (sessions) {
renderInfo(view, sessions, forceUpdate);
});
apiClient.getScheduledTasks().then(function (tasks) {
renderRunningTasks(view, tasks);
});
2018-10-23 01:05:09 +03:00
}
function renderActiveConnections(view, sessions) {
2020-07-11 11:56:57 +01:00
let html = '';
2018-10-23 01:05:09 +03:00
DashboardPage.sessionsList = sessions;
2020-07-11 11:56:57 +01:00
const parentElement = view.querySelector('.activeDevices');
const cardElem = parentElement.querySelector('.card');
2019-01-10 21:37:02 +01:00
if (cardElem) {
2020-05-04 12:44:12 +02:00
cardElem.classList.add('deadSession');
2019-01-10 21:37:02 +01:00
}
2020-07-11 11:56:57 +01:00
for (let i = 0, length = sessions.length; i < length; i++) {
const session = sessions[i];
const rowId = 'session' + session.Id;
const elem = view.querySelector('#' + rowId);
2019-01-10 21:37:02 +01:00
if (elem) {
DashboardPage.updateSession(elem, session);
} else {
2020-07-11 11:56:57 +01:00
const nowPlayingItem = session.NowPlayingItem;
const className = 'scalableCard card activeSession backdropCard backdropCard-scalable';
2019-01-10 21:37:02 +01:00
html += '<div class="' + className + '" id="' + rowId + '">';
html += '<div class="cardBox visualCardBox">';
html += '<div class="cardScalable visualCardBox-cardScalable">';
html += '<div class="cardPadder cardPadder-backdrop"></div>';
html += '<div class="cardContent">';
2020-07-11 11:56:57 +01:00
const imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem);
2019-01-10 21:37:02 +01:00
if (imgUrl) {
html += '<div class="sessionNowPlayingContent sessionNowPlayingContent-withbackground"';
html += ' data-src="' + imgUrl + '" style="display:inline-block;background-image:url(\'' + imgUrl + "');\"></div>";
2019-01-10 21:37:02 +01:00
} else {
html += '<div class="sessionNowPlayingContent"></div>';
2019-01-10 21:37:02 +01:00
}
html += '<div class="sessionNowPlayingInnerContent">';
html += '<div class="sessionAppInfo">';
2020-07-11 11:56:57 +01:00
const clientImage = DashboardPage.getClientImage(session);
2019-01-10 21:37:02 +01:00
if (clientImage) {
html += clientImage;
}
html += '<div class="sessionAppName" style="display:inline-block;">';
2020-05-04 12:44:12 +02:00
html += '<div class="sessionDeviceName">' + session.DeviceName + '</div>';
html += '<div class="sessionAppSecondaryText">' + DashboardPage.getAppSecondaryText(session) + '</div>';
html += '</div>';
html += '</div>';
2019-01-10 21:37:02 +01:00
html += '<div class="sessionNowPlayingDetails">';
2020-07-11 11:56:57 +01:00
const nowPlayingName = DashboardPage.getNowPlayingName(session);
2019-01-11 17:10:05 +01:00
html += '<div class="sessionNowPlayingInfo" data-imgsrc="' + nowPlayingName.image + '">';
html += nowPlayingName.html;
2020-05-04 12:44:12 +02:00
html += '</div>';
html += '<div class="sessionNowPlayingTime">' + DashboardPage.getSessionNowPlayingTime(session) + '</div>';
html += '</div>';
2019-01-11 17:10:05 +01:00
if (nowPlayingItem && nowPlayingItem.RunTimeTicks) {
2020-07-11 11:56:57 +01:00
const percent = 100 * (session.PlayState.PositionTicks || 0) / nowPlayingItem.RunTimeTicks;
html += indicators.getProgressHtml(percent, {
2020-05-04 12:44:12 +02:00
containerClass: 'playbackProgress'
});
2019-09-05 22:30:16 -07:00
} else {
// need to leave the element in just in case the device starts playback
html += indicators.getProgressHtml(0, {
2020-05-04 12:44:12 +02:00
containerClass: 'playbackProgress hide'
});
2019-01-10 21:37:02 +01:00
}
if (session.TranscodingInfo && session.TranscodingInfo.CompletionPercentage) {
2020-07-11 11:56:57 +01:00
const percent = session.TranscodingInfo.CompletionPercentage.toFixed(1);
html += indicators.getProgressHtml(percent, {
2020-05-04 12:44:12 +02:00
containerClass: 'transcodingProgress'
});
2019-09-05 22:30:16 -07:00
} else {
// same issue as playbackProgress element above
html += indicators.getProgressHtml(0, {
2020-05-04 12:44:12 +02:00
containerClass: 'transcodingProgress hide'
});
2019-01-10 21:37:02 +01:00
}
2020-05-04 12:44:12 +02:00
html += '</div>';
html += '</div>';
html += '</div>';
2019-01-10 21:37:02 +01:00
html += '<div class="sessionCardFooter cardFooter">';
html += '<div class="sessionCardButtons flex align-items-center justify-content-center">';
2020-02-06 00:13:11 +09:00
2020-07-11 11:56:57 +01:00
let btnCssClass = session.ServerId && session.NowPlayingItem && session.SupportsRemoteControl ? '' : ' hide';
2020-04-25 10:00:20 +03:00
const playIcon = session.PlayState.IsPaused ? 'pause' : 'play_arrow';
2020-02-06 00:13:11 +09:00
2020-04-26 02:37:28 +03:00
html += '<button is="paper-icon-button-light" class="sessionCardButton btnSessionPlayPause paper-icon-button-light ' + btnCssClass + '"><span class="material-icons ' + playIcon + '"></span></button>';
html += '<button is="paper-icon-button-light" class="sessionCardButton btnSessionStop paper-icon-button-light ' + btnCssClass + '"><span class="material-icons stop"></span></button>';
2020-02-06 00:13:11 +09:00
2020-05-04 12:44:12 +02:00
btnCssClass = session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length ? '' : ' hide';
html += '<button is="paper-icon-button-light" class="sessionCardButton btnSessionInfo paper-icon-button-light ' + btnCssClass + '" title="' + globalize.translate('ViewPlaybackInfo') + '"><span class="material-icons info"></span></button>';
2020-02-06 00:13:11 +09:00
btnCssClass = session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1 && session.DeviceId !== ServerConnections.deviceId() ? '' : ' hide';
2020-05-04 12:44:12 +02:00
html += '<button is="paper-icon-button-light" class="sessionCardButton btnSessionSendMessage paper-icon-button-light ' + btnCssClass + '" title="' + globalize.translate('SendMessage') + '"><span class="material-icons message"></span></button>';
html += '</div>';
2020-02-06 00:13:11 +09:00
2019-01-10 21:37:02 +01:00
html += '<div class="sessionNowPlayingStreamInfo" style="padding:.5em 0 1em;">';
html += DashboardPage.getSessionNowPlayingStreamInfo(session);
2020-05-04 12:44:12 +02:00
html += '</div>';
2020-02-06 00:13:11 +09:00
2019-01-10 21:37:02 +01:00
html += '<div class="flex align-items-center justify-content-center">';
2020-07-11 11:56:57 +01:00
const userImage = DashboardPage.getUserImage(session);
html += userImage ? '<div class="activitylogUserPhoto" style="background-image:url(\'' + userImage + "');\"></div>" : '<div style="height:1.71em;"></div>';
2019-09-05 11:42:38 -07:00
html += '<div class="sessionUserName">';
html += DashboardPage.getUsersHtml(session);
2020-02-06 00:13:11 +09:00
2020-05-04 12:44:12 +02:00
html += '</div>';
html += '</div>';
html += '</div>';
html += '</div>';
html += '</div>';
2018-10-23 01:05:09 +03:00
}
}
2019-01-10 21:37:02 +01:00
2020-05-04 12:44:12 +02:00
parentElement.insertAdjacentHTML('beforeend', html);
2020-07-11 11:56:57 +01:00
const deadSessionElem = parentElement.querySelector('.deadSession');
2019-01-10 21:37:02 +01:00
if (deadSessionElem) {
deadSessionElem.parentNode.removeChild(deadSessionElem);
}
2018-10-23 01:05:09 +03:00
}
function renderRunningTasks(view, tasks) {
2020-07-11 11:56:57 +01:00
let html = '';
2019-01-10 22:10:45 +01:00
tasks = tasks.filter(function (task) {
2020-07-30 16:07:13 +02:00
if (task.State != 'Idle') {
2019-01-10 22:10:45 +01:00
return !task.IsHidden;
2019-01-10 21:37:02 +01:00
}
return false;
});
if (tasks.length) {
2020-05-04 12:44:12 +02:00
view.querySelector('.runningTasksContainer').classList.remove('hide');
2019-01-10 21:37:02 +01:00
} else {
2020-05-04 12:44:12 +02:00
view.querySelector('.runningTasksContainer').classList.add('hide');
2019-01-10 21:37:02 +01:00
}
2020-07-11 11:56:57 +01:00
for (let i = 0, length = tasks.length; i < length; i++) {
const task = tasks[i];
2020-05-04 12:44:12 +02:00
html += '<p>';
html += task.Name + '<br/>';
2020-05-04 12:44:12 +02:00
if (task.State === 'Running') {
2020-07-11 11:56:57 +01:00
const progress = (task.CurrentProgressPercentage || 0).toFixed(1);
2019-01-10 21:37:02 +01:00
html += '<progress max="100" value="' + progress + '" title="' + progress + '%">';
2020-05-04 12:44:12 +02:00
html += progress + '%';
html += '</progress>';
html += "<span style='color:#00a4dc;margin-left:5px;margin-right:5px;'>" + progress + '%</span>';
html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonStop') + '" onclick="DashboardPage.stopTask(this, \'' + task.Id + '\');" class="autoSize"><span class="material-icons cancel"></span></button>';
} else if (task.State === 'Cancelling') {
html += '<span style="color:#cc0000;">' + globalize.translate('LabelStopping') + '</span>';
2019-01-10 21:37:02 +01:00
}
2020-05-04 12:44:12 +02:00
html += '</p>';
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
2020-05-04 12:44:12 +02:00
view.querySelector('#divRunningTasks').innerHTML = html;
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
window.DashboardPage = {
startInterval: function (apiClient) {
2020-05-04 12:44:12 +02:00
apiClient.sendMessage('SessionsStart', '0,1500');
apiClient.sendMessage('ScheduledTasksInfoStart', '0,1000');
2019-01-10 21:37:02 +01:00
},
stopInterval: function (apiClient) {
2020-05-04 12:44:12 +02:00
apiClient.sendMessage('SessionsStop');
apiClient.sendMessage('ScheduledTasksInfoStop');
2019-01-10 21:37:02 +01:00
},
getSessionNowPlayingStreamInfo: function (session) {
2020-07-11 11:56:57 +01:00
let html = '';
let showTranscodingInfo = false;
const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
2020-05-04 12:44:12 +02:00
if (displayPlayMethod === 'DirectStream') {
html += globalize.translate('DirectStreaming');
} else if (displayPlayMethod === 'Transcode') {
html += globalize.translate('Transcoding');
2020-04-09 01:15:43 +08:00
if (session.TranscodingInfo && session.TranscodingInfo.Framerate) {
2020-05-04 12:44:12 +02:00
html += ' (' + session.TranscodingInfo.Framerate + ' fps)';
2020-04-09 01:15:43 +08:00
}
2019-01-11 17:10:05 +01:00
showTranscodingInfo = true;
2020-05-04 12:44:12 +02:00
} else if (displayPlayMethod === 'DirectPlay') {
html += globalize.translate('DirectPlaying');
2019-01-11 17:10:05 +01:00
}
2019-01-11 17:10:05 +01:00
if (showTranscodingInfo) {
2020-07-11 11:56:57 +01:00
const line = [];
2019-01-10 21:37:02 +01:00
if (session.TranscodingInfo) {
if (session.TranscodingInfo.Bitrate) {
if (session.TranscodingInfo.Bitrate > 1e6) {
2020-05-04 12:44:12 +02:00
line.push((session.TranscodingInfo.Bitrate / 1e6).toFixed(1) + ' Mbps');
2019-01-10 21:37:02 +01:00
} else {
2020-05-04 12:44:12 +02:00
line.push(Math.floor(session.TranscodingInfo.Bitrate / 1e3) + ' Kbps');
2019-01-10 21:37:02 +01:00
}
}
if (session.TranscodingInfo.Container) {
line.push(session.TranscodingInfo.Container);
}
if (session.TranscodingInfo.VideoCodec) {
line.push(session.TranscodingInfo.VideoCodec);
}
if (session.TranscodingInfo.AudioCodec && session.TranscodingInfo.AudioCodec != session.TranscodingInfo.Container) {
line.push(session.TranscodingInfo.AudioCodec);
}
}
if (line.length) {
2020-05-04 12:44:12 +02:00
html += ' - ' + line.join(' ');
2019-01-10 21:37:02 +01:00
}
}
2019-09-05 11:42:38 -07:00
return html;
2019-01-10 21:37:02 +01:00
},
getSessionNowPlayingTime: function (session) {
2020-07-11 11:56:57 +01:00
const nowPlayingItem = session.NowPlayingItem;
let html = '';
2019-01-10 21:37:02 +01:00
if (nowPlayingItem) {
if (session.PlayState.PositionTicks) {
html += datetime.getDisplayRunningTime(session.PlayState.PositionTicks);
} else {
2020-05-04 12:44:12 +02:00
html += '0:00';
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
2020-05-04 12:44:12 +02:00
html += ' / ';
2019-01-10 21:37:02 +01:00
if (nowPlayingItem && nowPlayingItem.RunTimeTicks) {
html += datetime.getDisplayRunningTime(nowPlayingItem.RunTimeTicks);
} else {
2020-05-04 12:44:12 +02:00
html += '0:00';
2019-01-10 21:37:02 +01:00
}
}
return html;
},
getAppSecondaryText: function (session) {
2020-05-04 12:44:12 +02:00
return session.Client + ' ' + session.ApplicationVersion;
2019-01-10 21:37:02 +01:00
},
getNowPlayingName: function (session) {
2020-07-11 11:56:57 +01:00
let imgUrl = '';
const nowPlayingItem = session.NowPlayingItem;
2020-04-02 19:59:44 +02:00
// FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix
// how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences
2019-01-10 21:37:02 +01:00
if (!nowPlayingItem) {
return {
2020-08-14 08:46:34 +02:00
html: globalize.translate('LastSeen', formatDistanceToNow(Date.parse(session.LastActivityDate), localeWithSuffix)),
2018-10-23 01:05:09 +03:00
image: imgUrl
};
2019-01-10 21:37:02 +01:00
}
2020-07-11 11:56:57 +01:00
let topText = itemHelper.getDisplayName(nowPlayingItem);
let bottomText = '';
2019-01-10 21:37:02 +01:00
if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) {
bottomText = topText;
topText = nowPlayingItem.Artists[0];
} else {
if (nowPlayingItem.SeriesName || nowPlayingItem.Album) {
bottomText = topText;
topText = nowPlayingItem.SeriesName || nowPlayingItem.Album;
2019-01-10 22:10:45 +01:00
} else if (nowPlayingItem.ProductionYear) {
bottomText = nowPlayingItem.ProductionYear;
2019-01-10 21:37:02 +01:00
}
}
if (nowPlayingItem.ImageTags && nowPlayingItem.ImageTags.Logo) {
imgUrl = ApiClient.getScaledImageUrl(nowPlayingItem.Id, {
2018-10-23 01:05:09 +03:00
tag: nowPlayingItem.ImageTags.Logo,
2020-03-11 21:31:04 +01:00
maxHeight: 24,
maxWidth: 130,
2020-05-04 12:44:12 +02:00
type: 'Logo'
2019-01-10 21:37:02 +01:00
});
2019-09-05 11:42:38 -07:00
} else if (nowPlayingItem.ParentLogoImageTag) {
imgUrl = ApiClient.getScaledImageUrl(nowPlayingItem.ParentLogoItemId, {
tag: nowPlayingItem.ParentLogoImageTag,
2020-03-11 21:31:04 +01:00
maxHeight: 24,
maxWidth: 130,
2020-05-04 12:44:12 +02:00
type: 'Logo'
2019-09-05 11:42:38 -07:00
});
2019-01-10 21:37:02 +01:00
}
if (imgUrl) {
topText = '<img src="' + imgUrl + '" style="max-height:24px;max-width:130px;" />';
}
return {
2020-05-04 12:44:12 +02:00
html: bottomText ? topText + '<br/>' + bottomText : topText,
2019-01-10 21:37:02 +01:00
image: imgUrl
};
},
getUsersHtml: function (session) {
2020-07-11 11:56:57 +01:00
const html = [];
2019-01-10 21:37:02 +01:00
if (session.UserId) {
html.push(session.UserName);
}
2020-07-11 11:56:57 +01:00
for (let i = 0, length = session.AdditionalUsers.length; i < length; i++) {
2019-01-10 22:10:45 +01:00
html.push(session.AdditionalUsers[i].UserName);
2019-01-10 21:37:02 +01:00
}
2020-05-04 12:44:12 +02:00
return html.join(', ');
2019-01-10 21:37:02 +01:00
},
getUserImage: function (session) {
if (session.UserId && session.UserPrimaryImageTag) {
return ApiClient.getUserImageUrl(session.UserId, {
2018-10-23 01:05:09 +03:00
tag: session.UserPrimaryImageTag,
2020-05-04 12:44:12 +02:00
type: 'Primary'
2019-01-10 21:37:02 +01:00
});
}
return null;
},
updateSession: function (row, session) {
2020-05-04 12:44:12 +02:00
row.classList.remove('deadSession');
2020-07-11 11:56:57 +01:00
const nowPlayingItem = session.NowPlayingItem;
2019-01-10 21:37:02 +01:00
if (nowPlayingItem) {
2020-05-04 12:44:12 +02:00
row.classList.add('playingSession');
2019-01-10 21:37:02 +01:00
} else {
2020-05-04 12:44:12 +02:00
row.classList.remove('playingSession');
2019-01-10 21:37:02 +01:00
}
if (session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1) {
2020-05-04 12:44:12 +02:00
row.querySelector('.btnSessionSendMessage').classList.remove('hide');
2019-01-10 21:37:02 +01:00
} else {
2020-05-04 12:44:12 +02:00
row.querySelector('.btnSessionSendMessage').classList.add('hide');
2019-01-10 21:37:02 +01:00
}
if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo) {
2020-05-04 12:44:12 +02:00
row.querySelector('.btnSessionInfo').classList.remove('hide');
2019-01-10 21:37:02 +01:00
} else {
2020-05-04 12:44:12 +02:00
row.querySelector('.btnSessionInfo').classList.add('hide');
2019-01-10 21:37:02 +01:00
}
2020-07-11 11:56:57 +01:00
const btnSessionPlayPause = row.querySelector('.btnSessionPlayPause');
if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl) {
2020-05-04 12:44:12 +02:00
btnSessionPlayPause.classList.remove('hide');
row.querySelector('.btnSessionStop').classList.remove('hide');
2019-01-10 21:37:02 +01:00
} else {
2020-05-04 12:44:12 +02:00
btnSessionPlayPause.classList.add('hide');
row.querySelector('.btnSessionStop').classList.add('hide');
2019-01-10 21:37:02 +01:00
}
2020-05-04 12:44:12 +02:00
const btnSessionPlayPauseIcon = btnSessionPlayPause.querySelector('.material-icons');
btnSessionPlayPauseIcon.classList.remove('play_arrow', 'pause');
btnSessionPlayPauseIcon.classList.add(session.PlayState && session.PlayState.IsPaused ? 'play_arrow' : 'pause');
2019-01-10 21:37:02 +01:00
2020-05-04 12:44:12 +02:00
row.querySelector('.sessionNowPlayingStreamInfo').innerHTML = DashboardPage.getSessionNowPlayingStreamInfo(session);
row.querySelector('.sessionNowPlayingTime').innerHTML = DashboardPage.getSessionNowPlayingTime(session);
row.querySelector('.sessionUserName').innerHTML = DashboardPage.getUsersHtml(session);
row.querySelector('.sessionAppSecondaryText').innerHTML = DashboardPage.getAppSecondaryText(session);
2020-07-11 11:56:57 +01:00
const nowPlayingName = DashboardPage.getNowPlayingName(session);
const nowPlayingInfoElem = row.querySelector('.sessionNowPlayingInfo');
2019-01-10 21:37:02 +01:00
2020-05-04 12:44:12 +02:00
if (!(nowPlayingName.image && nowPlayingName.image == nowPlayingInfoElem.getAttribute('data-imgsrc'))) {
2019-01-10 21:37:02 +01:00
nowPlayingInfoElem.innerHTML = nowPlayingName.html;
2020-05-04 12:44:12 +02:00
nowPlayingInfoElem.setAttribute('data-imgsrc', nowPlayingName.image || '');
2019-01-10 21:37:02 +01:00
}
2020-07-11 11:56:57 +01:00
const playbackProgressElem = row.querySelector('.playbackProgress');
2019-09-05 22:30:16 -07:00
if (nowPlayingItem && nowPlayingItem.RunTimeTicks) {
2020-07-11 11:56:57 +01:00
const percent = 100 * (session.PlayState.PositionTicks || 0) / nowPlayingItem.RunTimeTicks;
playbackProgressElem.outerHTML = indicators.getProgressHtml(percent, {
2020-05-04 12:44:12 +02:00
containerClass: 'playbackProgress'
});
2019-09-05 22:30:16 -07:00
} else {
playbackProgressElem.outerHTML = indicators.getProgressHtml(0, {
2020-05-04 12:44:12 +02:00
containerClass: 'playbackProgress hide'
});
2019-01-10 21:37:02 +01:00
}
2020-07-11 11:56:57 +01:00
const transcodingProgress = row.querySelector('.transcodingProgress');
2019-01-10 21:37:02 +01:00
if (session.TranscodingInfo && session.TranscodingInfo.CompletionPercentage) {
2020-07-11 11:56:57 +01:00
const percent = session.TranscodingInfo.CompletionPercentage.toFixed(1);
transcodingProgress.outerHTML = indicators.getProgressHtml(percent, {
2020-05-04 12:44:12 +02:00
containerClass: 'transcodingProgress'
});
2019-01-10 21:37:02 +01:00
} else {
transcodingProgress.outerHTML = indicators.getProgressHtml(0, {
2020-05-04 12:44:12 +02:00
containerClass: 'transcodingProgress hide'
});
2019-01-10 21:37:02 +01:00
}
2020-07-11 11:56:57 +01:00
const imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem) || '';
const imgElem = row.querySelector('.sessionNowPlayingContent');
2019-01-10 21:37:02 +01:00
2020-05-04 12:44:12 +02:00
if (imgUrl != imgElem.getAttribute('data-src')) {
imgElem.style.backgroundImage = imgUrl ? "url('" + imgUrl + "')" : '';
imgElem.setAttribute('data-src', imgUrl);
2019-01-10 21:37:02 +01:00
if (imgUrl) {
2020-05-04 12:44:12 +02:00
imgElem.classList.add('sessionNowPlayingContent-withbackground');
2019-01-10 21:37:02 +01:00
} else {
2020-05-04 12:44:12 +02:00
imgElem.classList.remove('sessionNowPlayingContent-withbackground');
2019-01-10 21:37:02 +01:00
}
}
},
getClientImage: function (connection) {
2020-07-11 11:56:57 +01:00
const iconUrl = imageHelper.getDeviceIcon(connection);
return "<img src='" + iconUrl + "' />";
2019-01-10 21:37:02 +01:00
},
getNowPlayingImageUrl: function (item) {
2020-03-13 09:22:08 +01:00
/* Screen width is multiplied by 0.2, as the there is currently no way to get the width of
elements that aren't created yet. */
2019-01-10 21:37:02 +01:00
if (item && item.BackdropImageTags && item.BackdropImageTags.length) {
return ApiClient.getScaledImageUrl(item.Id, {
2020-03-09 21:02:08 +01:00
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
2020-05-04 12:44:12 +02:00
type: 'Backdrop',
2018-10-23 01:05:09 +03:00
tag: item.BackdropImageTags[0]
});
2019-01-10 21:37:02 +01:00
}
if (item && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
return ApiClient.getScaledImageUrl(item.ParentBackdropItemId, {
2020-03-09 21:02:08 +01:00
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
2020-05-04 12:44:12 +02:00
type: 'Backdrop',
2018-10-23 01:05:09 +03:00
tag: item.ParentBackdropImageTags[0]
});
2019-01-10 21:37:02 +01:00
}
if (item && item.BackdropImageTag) {
return ApiClient.getScaledImageUrl(item.BackdropItemId, {
2020-03-09 21:02:08 +01:00
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
2020-05-04 12:44:12 +02:00
type: 'Backdrop',
2018-10-23 01:05:09 +03:00
tag: item.BackdropImageTag
});
2019-01-10 21:37:02 +01:00
}
2020-07-11 11:56:57 +01:00
const imageTags = (item || {}).ImageTags || {};
2019-01-10 21:37:02 +01:00
if (item && imageTags.Thumb) {
return ApiClient.getScaledImageUrl(item.Id, {
2020-03-09 21:02:08 +01:00
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
2020-05-04 12:44:12 +02:00
type: 'Thumb',
2018-10-23 01:05:09 +03:00
tag: imageTags.Thumb
2019-01-10 21:37:02 +01:00
});
}
if (item && item.ParentThumbImageTag) {
return ApiClient.getScaledImageUrl(item.ParentThumbItemId, {
2020-03-09 21:02:08 +01:00
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
2020-05-04 12:44:12 +02:00
type: 'Thumb',
2018-10-23 01:05:09 +03:00
tag: item.ParentThumbImageTag
2019-01-10 21:37:02 +01:00
});
}
if (item && item.ThumbImageTag) {
return ApiClient.getScaledImageUrl(item.ThumbItemId, {
2020-03-09 21:02:08 +01:00
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
2020-05-04 12:44:12 +02:00
type: 'Thumb',
2018-10-23 01:05:09 +03:00
tag: item.ThumbImageTag
2019-01-10 21:37:02 +01:00
});
}
if (item && imageTags.Primary) {
return ApiClient.getScaledImageUrl(item.Id, {
2020-03-09 21:02:08 +01:00
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
2020-05-04 12:44:12 +02:00
type: 'Primary',
2018-10-23 01:05:09 +03:00
tag: imageTags.Primary
2019-01-10 21:37:02 +01:00
});
}
if (item && item.PrimaryImageTag) {
return ApiClient.getScaledImageUrl(item.PrimaryImageItemId, {
2020-03-09 21:02:08 +01:00
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
2020-05-04 12:44:12 +02:00
type: 'Primary',
2018-10-23 01:05:09 +03:00
tag: item.PrimaryImageTag
2019-01-10 21:37:02 +01:00
});
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
if (item && item.AlbumPrimaryImageTag) {
return ApiClient.getScaledImageUrl(item.AlbumId, {
2020-03-09 21:02:08 +01:00
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
2020-05-04 12:44:12 +02:00
type: 'Primary',
tag: item.AlbumPrimaryImageTag
});
}
2019-01-10 21:37:02 +01:00
return null;
},
2020-05-04 12:44:12 +02:00
systemUpdateTaskKey: 'SystemUpdateTask',
2019-01-10 21:37:02 +01:00
stopTask: function (btn, id) {
2020-07-11 11:56:57 +01:00
const page = dom.parentWithClass(btn, 'page');
2019-01-10 21:37:02 +01:00
ApiClient.stopScheduledTask(id).then(function () {
pollForInfo(page, ApiClient);
});
},
restart: function (btn) {
2020-08-14 08:46:34 +02:00
import('../../components/confirm/confirm').then(({default: confirm}) => {
2019-01-10 21:37:02 +01:00
confirm({
2020-08-27 09:28:09 +09:00
title: globalize.translate('Restart'),
2020-05-04 12:44:12 +02:00
text: globalize.translate('MessageConfirmRestart'),
2020-08-27 09:28:09 +09:00
confirmText: globalize.translate('Restart'),
2020-05-04 12:44:12 +02:00
primary: 'delete'
2019-01-10 21:37:02 +01:00
}).then(function () {
2020-07-11 11:56:57 +01:00
const page = dom.parentWithClass(btn, 'page');
2020-05-04 12:44:12 +02:00
page.querySelector('#btnRestartServer').disabled = true;
page.querySelector('#btnShutdown').disabled = true;
ApiClient.restartServer();
2019-01-10 21:37:02 +01:00
});
});
},
shutdown: function (btn) {
2020-08-14 08:46:34 +02:00
import('../../components/confirm/confirm').then(({default: confirm}) => {
2019-01-10 21:37:02 +01:00
confirm({
2020-08-13 21:23:51 +09:00
title: globalize.translate('ButtonShutdown'),
2020-05-04 12:44:12 +02:00
text: globalize.translate('MessageConfirmShutdown'),
confirmText: globalize.translate('ButtonShutdown'),
primary: 'delete'
2019-01-10 21:37:02 +01:00
}).then(function () {
2020-07-11 11:56:57 +01:00
const page = dom.parentWithClass(btn, 'page');
2020-05-04 12:44:12 +02:00
page.querySelector('#btnRestartServer').disabled = true;
page.querySelector('#btnShutdown').disabled = true;
2019-01-10 21:37:02 +01:00
ApiClient.shutdownServer();
});
});
}
};
export default function (view, params) {
2019-01-10 22:10:45 +01:00
function onRestartRequired(evt, apiClient) {
console.debug('onRestartRequired not implemented', evt, apiClient);
2019-01-10 21:37:02 +01:00
}
2018-10-23 01:05:09 +03:00
2019-01-10 22:10:45 +01:00
function onServerShuttingDown(evt, apiClient) {
console.debug('onServerShuttingDown not implemented', evt, apiClient);
2019-01-10 21:37:02 +01:00
}
2018-10-23 01:05:09 +03:00
2019-01-10 22:10:45 +01:00
function onServerRestarting(evt, apiClient) {
console.debug('onServerRestarting not implemented', evt, apiClient);
2019-01-10 21:37:02 +01:00
}
2018-10-23 01:05:09 +03:00
2019-01-10 22:10:45 +01:00
function onPackageInstalling(evt, apiClient) {
2019-01-10 21:37:02 +01:00
if (apiClient.serverId() === serverId) {
pollForInfo(view, apiClient, true);
reloadSystemInfo(view, apiClient);
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
}
2018-10-23 01:05:09 +03:00
2019-01-10 22:10:45 +01:00
function onPackageInstallationCompleted(evt, apiClient) {
2019-01-10 21:37:02 +01:00
if (apiClient.serverId() === serverId) {
pollForInfo(view, apiClient, true);
reloadSystemInfo(view, apiClient);
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
}
2018-10-23 01:05:09 +03:00
2019-01-10 22:10:45 +01:00
function onSessionsUpdate(evt, apiClient, info) {
2019-01-10 21:37:02 +01:00
if (apiClient.serverId() === serverId) {
renderInfo(view, info);
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
}
2019-01-10 22:10:45 +01:00
function onScheduledTasksUpdate(evt, apiClient, info) {
2019-01-10 21:37:02 +01:00
if (apiClient.serverId() === serverId) {
renderRunningTasks(view, info);
}
}
2020-07-11 11:56:57 +01:00
const serverId = ApiClient.serverId();
2020-05-04 12:44:12 +02:00
view.querySelector('.activeDevices').addEventListener('click', onActiveDevicesClick);
view.addEventListener('viewshow', function () {
2020-07-11 11:56:57 +01:00
const page = this;
const apiClient = ApiClient;
2019-01-10 21:37:02 +01:00
if (apiClient) {
loading.show();
pollForInfo(page, apiClient);
DashboardPage.startInterval(apiClient);
2020-09-08 02:05:02 -04:00
Events.on(serverNotifications, 'RestartRequired', onRestartRequired);
Events.on(serverNotifications, 'ServerShuttingDown', onServerShuttingDown);
Events.on(serverNotifications, 'ServerRestarting', onServerRestarting);
Events.on(serverNotifications, 'PackageInstalling', onPackageInstalling);
Events.on(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted);
Events.on(serverNotifications, 'Sessions', onSessionsUpdate);
Events.on(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate);
2019-01-10 21:37:02 +01:00
DashboardPage.lastAppUpdateCheck = null;
reloadSystemInfo(page, ApiClient);
if (!page.userActivityLog) {
page.userActivityLog = new ActivityLog({
serverId: ApiClient.serverId(),
2020-05-04 12:44:12 +02:00
element: page.querySelector('.userActivityItems')
2019-01-10 21:37:02 +01:00
});
}
2020-05-04 12:44:12 +02:00
if (ApiClient.isMinServerVersion('3.4.1.25')) {
2019-01-10 21:37:02 +01:00
if (!page.serverActivityLog) {
page.serverActivityLog = new ActivityLog({
2018-10-23 01:05:09 +03:00
serverId: ApiClient.serverId(),
2020-05-04 12:44:12 +02:00
element: page.querySelector('.serverActivityItems')
2019-01-10 21:37:02 +01:00
});
}
2018-10-23 01:05:09 +03:00
}
2019-01-10 21:37:02 +01:00
refreshActiveRecordings(view, apiClient);
loading.hide();
}
2020-10-12 12:29:57 +09:00
taskButton({
mode: 'on',
taskKey: 'RefreshLibrary',
button: page.querySelector('.btnRefresh')
});
2019-01-10 21:37:02 +01:00
});
2020-05-04 12:44:12 +02:00
view.addEventListener('viewbeforehide', function () {
2020-07-11 11:56:57 +01:00
const apiClient = ApiClient;
2020-10-12 12:29:57 +09:00
const page = this;
2020-09-08 02:05:02 -04:00
Events.off(serverNotifications, 'RestartRequired', onRestartRequired);
Events.off(serverNotifications, 'ServerShuttingDown', onServerShuttingDown);
Events.off(serverNotifications, 'ServerRestarting', onServerRestarting);
Events.off(serverNotifications, 'PackageInstalling', onPackageInstalling);
Events.off(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted);
Events.off(serverNotifications, 'Sessions', onSessionsUpdate);
Events.off(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate);
2019-01-10 21:37:02 +01:00
if (apiClient) {
DashboardPage.stopInterval(apiClient);
}
2020-10-12 12:29:57 +09:00
taskButton({
mode: 'off',
taskKey: 'RefreshLibrary',
button: page.querySelector('.btnRefresh')
});
2019-01-10 21:37:02 +01:00
});
2020-05-04 12:44:12 +02:00
view.addEventListener('viewdestroy', function () {
2020-07-11 11:56:57 +01:00
const page = this;
const userActivityLog = page.userActivityLog;
2019-01-10 21:37:02 +01:00
if (userActivityLog) {
userActivityLog.destroy();
}
2020-07-11 11:56:57 +01:00
const serverActivityLog = page.serverActivityLog;
2019-01-10 21:37:02 +01:00
if (serverActivityLog) {
serverActivityLog.destroy();
}
});
}
/* eslint-enable indent */