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

Merge branch 'master' into es6-subtitlesettings

This commit is contained in:
Dmitry Lyzo 2020-06-10 11:06:38 +03:00 committed by GitHub
commit 17f04b8042
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
146 changed files with 5890 additions and 3909 deletions

View file

@ -1,9 +1,20 @@
define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-button-light', 'formDialogStyle'], function (dialogHelper, datetime, globalize) {
'use strict';
/* eslint-disable indent */
/**
* Module for controlling user parental control from.
* @module components/accessSchedule/accessSchedule
*/
import dialogHelper from 'dialogHelper';
import datetime from 'datetime';
import globalize from 'globalize';
import 'emby-select';
import 'paper-icon-button-light';
import 'formDialogStyle';
function getDisplayTime(hours) {
var minutes = 0;
var pct = hours % 1;
let minutes = 0;
const pct = hours % 1;
if (pct) {
minutes = parseInt(60 * pct);
@ -13,25 +24,25 @@ define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-butt
}
function populateHours(context) {
var html = '';
let html = '';
for (var i = 0; i < 24; i++) {
html += '<option value="' + i + '">' + getDisplayTime(i) + '</option>';
for (let i = 0; i < 24; i++) {
html += `<option value="${i}">${getDisplayTime(i)}</option>`;
}
html += '<option value="24">' + getDisplayTime(0) + '</option>';
html += `<option value="24">${getDisplayTime(0)}</option>`;
context.querySelector('#selectStart').innerHTML = html;
context.querySelector('#selectEnd').innerHTML = html;
}
function loadSchedule(context, schedule) {
context.querySelector('#selectDay').value = schedule.DayOfWeek || 'Sunday';
context.querySelector('#selectStart').value = schedule.StartHour || 0;
context.querySelector('#selectEnd').value = schedule.EndHour || 0;
function loadSchedule(context, {DayOfWeek, StartHour, EndHour}) {
context.querySelector('#selectDay').value = DayOfWeek || 'Sunday';
context.querySelector('#selectStart').value = StartHour || 0;
context.querySelector('#selectEnd').value = EndHour || 0;
}
function submitSchedule(context, options) {
var updatedSchedule = {
const updatedSchedule = {
DayOfWeek: context.querySelector('#selectDay').value,
StartHour: context.querySelector('#selectStart').value,
EndHour: context.querySelector('#selectEnd').value
@ -46,44 +57,42 @@ define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-butt
dialogHelper.close(context);
}
return {
show: function (options) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'components/accessSchedule/accessSchedule.template.html', true);
xhr.onload = function (e) {
var template = this.response;
var dlg = dialogHelper.createDialog({
removeOnClose: true,
size: 'small'
});
dlg.classList.add('formDialog');
var html = '';
html += globalize.translateDocument(template);
dlg.innerHTML = html;
populateHours(dlg);
loadSchedule(dlg, options.schedule);
dialogHelper.open(dlg);
dlg.addEventListener('close', function () {
if (dlg.submitted) {
resolve(options.schedule);
} else {
reject();
}
});
dlg.querySelector('.btnCancel').addEventListener('click', function (e) {
dialogHelper.close(dlg);
});
dlg.querySelector('form').addEventListener('submit', function (e) {
submitSchedule(dlg, options);
e.preventDefault();
return false;
});
};
xhr.send();
export function show(options) {
return new Promise((resolve, reject) => {
// TODO: remove require
require(['text!./components/accessSchedule/accessSchedule.template.html'], template => {
const dlg = dialogHelper.createDialog({
removeOnClose: true,
size: 'small'
});
dlg.classList.add('formDialog');
let html = '';
html += globalize.translateDocument(template);
dlg.innerHTML = html;
populateHours(dlg);
loadSchedule(dlg, options.schedule);
dialogHelper.open(dlg);
dlg.addEventListener('close', () => {
if (dlg.submitted) {
resolve(options.schedule);
} else {
reject();
}
});
dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg);
});
dlg.querySelector('form').addEventListener('submit', event => {
submitSchedule(dlg, options);
event.preventDefault();
return false;
});
});
}
};
});
});
}
/* eslint-enable indent */
export default {
show: show
};

View file

@ -16,15 +16,8 @@ function getOffsets(elems) {
return results;
}
let box;
for (let elem of elems) {
// Support: BlackBerry 5, iOS 3 (original iPhone)
// If we don't have gBCR, just use 0,0 rather than error
if (elem.getBoundingClientRect) {
box = elem.getBoundingClientRect();
} else {
box = { top: 0, left: 0 };
}
let box = elem.getBoundingClientRect();
results.push({
top: box.top,
@ -153,7 +146,9 @@ export function show(options) {
}
if (layoutManager.tv) {
html += '<button is="paper-icon-button-light" class="btnCloseActionSheet hide-mouse-idle-tv" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
html += `<button is="paper-icon-button-light" class="btnCloseActionSheet hide-mouse-idle-tv" tabindex="-1">
<span class="material-icons arrow_back"></span>
</button>`;
}
// If any items have an icon, give them all an icon just to make sure they're all lined up evenly
@ -216,7 +211,7 @@ export function show(options) {
itemIcon = icons[i];
if (itemIcon) {
html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons ' + itemIcon + '"></span>';
html += `<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons ${itemIcon}"></span>`;
} else if (renderIcon && !center) {
html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons check" style="visibility:hidden;"></span>';
}
@ -228,13 +223,13 @@ export function show(options) {
html += '</div>';
if (item.secondaryText) {
html += '<div class="listItemBodyText secondary">' + item.secondaryText + '</div>';
html += `<div class="listItemBodyText secondary">${item.secondaryText}</div>`;
}
html += '</div>';
if (item.asideText) {
html += '<div class="listItemAside actionSheetItemAsideText">' + item.asideText + '</div>';
html += `<div class="listItemAside actionSheetItemAsideText">${item.asideText}</div>`;
}
html += '</button>';
@ -242,7 +237,7 @@ export function show(options) {
if (options.showCancel) {
html += '<div class="buttons">';
html += '<button is="emby-button" type="button" class="btnCloseActionSheet">' + globalize.translate('ButtonCancel') + '</button>';
html += `<button is="emby-button" type="button" class="btnCloseActionSheet">${globalize.translate('ButtonCancel')}</button>`;
html += '</div>';
}
html += '</div>';

View file

@ -34,10 +34,14 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
html += '</div>';
if (entry.Overview) {
html += '<button type="button" is="paper-icon-button-light" class="btnEntryInfo" data-id="' + entry.Id + '" title="' + globalize.translate('Info') + '"><span class="material-icons info"></span></button>';
html += `<button type="button" is="paper-icon-button-light" class="btnEntryInfo" data-id="${entry.Id}" title="${globalize.translate('Info')}">
<span class="material-icons info"></span>
</button>`;
}
return html += '</div>';
html += '</div>';
return html;
}
function renderList(elem, apiClient, result, startIndex, limit) {

View file

@ -26,11 +26,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
connectionManager.connect({
enableAutoLogin: appSettings.enableAutoLogin()
}).then(function (result) {
handleConnectionResult(result, loading);
handleConnectionResult(result);
});
}
function handleConnectionResult(result, loading) {
function handleConnectionResult(result) {
switch (result.State) {
case 'SignedIn':
loading.hide();
@ -246,13 +246,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
}
if (setQuality) {
var quality = 100;
var quality;
var type = options.type || 'Primary';
if (browser.tv || browser.slow) {
// TODO: wtf
if (browser.chrome) {
// webp support
quality = type === 'Primary' ? 40 : 50;
@ -384,7 +382,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
if (firstResult.State !== 'SignedIn' && !route.anonymous) {
handleConnectionResult(firstResult, loading);
handleConnectionResult(firstResult);
return;
}
}
@ -463,7 +461,6 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
return Promise.resolve();
}
var isHandlingBackToDefault;
var isDummyBackToHome;
function loadContent(ctx, route, html, request) {
@ -589,8 +586,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
path = '/' + path;
}
var baseRoute = baseUrl();
path = path.replace(baseRoute, '');
path = path.replace(baseUrl(), '');
if (currentRouteInfo && currentRouteInfo.path === path) {
// can't use this with home right now due to the back menu
@ -621,10 +617,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
}
function showItem(item, serverId, options) {
// TODO: Refactor this so it only gets items, not strings.
if (typeof (item) === 'string') {
var apiClient = serverId ? connectionManager.getApiClient(serverId) : connectionManager.currentApiClient();
apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (item) {
appRouter.showItem(item, options);
apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (itemObject) {
appRouter.showItem(itemObject, options);
});
} else {
if (arguments.length === 2) {

View file

@ -5,7 +5,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
var disableHlsVideoAudioCodecs = [];
if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) {
if (browser.edge || browser.msie) {
if (browser.edge) {
disableHlsVideoAudioCodecs.push('mp3');
}
@ -93,18 +93,36 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
function getDeviceName() {
var deviceName;
deviceName = browser.tizen ? 'Samsung Smart TV' : browser.web0s ? 'LG Smart TV' : browser.operaTv ? 'Opera TV' : browser.xboxOne ? 'Xbox One' : browser.ps4 ? 'Sony PS4' : browser.chrome ? 'Chrome' : browser.edge ? 'Edge' : browser.firefox ? 'Firefox' : browser.msie ? 'Internet Explorer' : browser.opera ? 'Opera' : browser.safari ? 'Safari' : 'Web Browser';
if (browser.tizen) {
deviceName = 'Samsung Smart TV';
} else if (browser.web0s) {
deviceName = 'LG Smart TV';
} else if (browser.operaTv) {
deviceName = 'Opera TV';
} else if (browser.xboxOne) {
deviceName = 'Xbox One';
} else if (browser.ps4) {
deviceName = 'Sony PS4';
} else if (browser.chrome) {
deviceName = 'Chrome';
} else if (browser.edge) {
deviceName = 'Edge';
} else if (browser.firefox) {
deviceName = 'Firefox';
} else if (browser.opera) {
deviceName = 'Opera';
} else if (browser.safari) {
deviceName = 'Safari';
} else {
deviceName = 'Web Browser';
}
if (browser.ipad) {
deviceName += ' iPad';
} else {
if (browser.iphone) {
deviceName += ' iPhone';
} else {
if (browser.android) {
deviceName += ' Android';
}
}
} else if (browser.iphone) {
deviceName += ' iPhone';
} else if (browser.android) {
deviceName += ' Android';
}
return deviceName;
@ -267,7 +285,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
if (enabled) features.push('multiserver');
});
if (!browser.orsay && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) {
if (!browser.orsay && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) {
features.push('subtitleappearancesettings');
}

View file

@ -1,56 +0,0 @@
define(['connectionManager'], function (connectionManager) {
return function () {
var self = this;
self.name = 'Backdrop ScreenSaver';
self.type = 'screensaver';
self.id = 'backdropscreensaver';
self.supportsAnonymous = false;
var currentSlideshow;
self.show = function () {
var query = {
ImageTypes: 'Backdrop',
EnableImageTypes: 'Backdrop',
IncludeItemTypes: 'Movie,Series,MusicArtist',
SortBy: 'Random',
Recursive: true,
Fields: 'Taglines',
ImageTypeLimit: 1,
StartIndex: 0,
Limit: 200
};
var apiClient = connectionManager.currentApiClient();
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (result) {
if (result.Items.length) {
require(['slideshow'], function (slideshow) {
var newSlideShow = new slideshow({
showTitle: true,
cover: true,
items: result.Items
});
newSlideShow.show();
currentSlideshow = newSlideShow;
});
}
});
};
self.hide = function () {
if (currentSlideshow) {
currentSlideshow.hide();
currentSlideshow = null;
}
};
};
});

View file

@ -503,94 +503,49 @@ import 'programStyles';
const primaryImageAspectRatio = item.PrimaryImageAspectRatio;
let forceName = false;
let imgUrl = null;
let imgTag = null;
let coverImage = false;
let uiAspect = null;
let imgType = null;
let itemId = null;
if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Thumb',
maxWidth: width,
tag: item.ImageTags.Thumb
});
imgType = 'Thumb';
imgTag = item.ImageTags.Thumb;
} else if ((options.preferBanner || shape === 'banner') && item.ImageTags && item.ImageTags.Banner) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Banner',
maxWidth: width,
tag: item.ImageTags.Banner
});
imgType = 'Banner';
imgTag = item.ImageTags.Banner;
} else if (options.preferDisc && item.ImageTags && item.ImageTags.Disc) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Disc',
maxWidth: width,
tag: item.ImageTags.Disc
});
imgType = 'Disc';
imgTag = item.ImageTags.Disc;
} else if (options.preferLogo && item.ImageTags && item.ImageTags.Logo) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Logo',
maxWidth: width,
tag: item.ImageTags.Logo
});
imgType = 'Logo';
imgTag = item.ImageTags.Logo;
} else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) {
imgUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, {
type: 'Logo',
maxWidth: width,
tag: item.ParentLogoImageTag
});
imgType = 'Logo';
imgTag = item.ParentLogoImageTag;
itemId = item.ParentLogoItemId;
} else if (options.preferThumb && item.SeriesThumbImageTag && options.inheritThumb !== false) {
imgUrl = apiClient.getScaledImageUrl(item.SeriesId, {
type: 'Thumb',
maxWidth: width,
tag: item.SeriesThumbImageTag
});
imgType = 'Thumb';
imgTag = item.SeriesThumbImageTag;
itemId = item.SeriesId;
} else if (options.preferThumb && item.ParentThumbItemId && options.inheritThumb !== false && item.MediaType !== 'Photo') {
imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, {
type: 'Thumb',
maxWidth: width,
tag: item.ParentThumbImageTag
});
imgType = 'Thumb';
imgTag = item.ParentThumbImageTag;
itemId = item.ParentThumbItemId;
} else if (options.preferThumb && item.BackdropImageTags && item.BackdropImageTags.length) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Backdrop',
maxWidth: width,
tag: item.BackdropImageTags[0]
});
imgType = 'Backdrop';
imgTag = item.BackdropImageTags[0];
forceName = true;
} else if (options.preferThumb && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false && item.Type === 'Episode') {
imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, {
type: 'Backdrop',
maxWidth: width,
tag: item.ParentBackdropImageTags[0]
});
imgType = 'Backdrop';
imgTag = item.ParentBackdropImageTags[0];
itemId = item.ParentBackdropItemId;
} else if (item.ImageTags && item.ImageTags.Primary) {
imgType = 'Primary';
imgTag = item.ImageTags.Primary;
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Primary',
maxHeight: height,
maxWidth: width,
tag: item.ImageTags.Primary
});
if (options.preferThumb && options.showTitle !== false) {
forceName = true;
}
@ -603,16 +558,11 @@ import 'programStyles';
}
} else if (item.PrimaryImageTag) {
imgType = 'Primary';
imgTag = item.PrimaryImageTag;
itemId = item.PrimaryImageItemId;
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
imgUrl = apiClient.getScaledImageUrl(item.PrimaryImageItemId || item.Id || item.ItemId, {
type: 'Primary',
maxHeight: height,
maxWidth: width,
tag: item.PrimaryImageTag
});
if (options.preferThumb && options.showTitle !== false) {
forceName = true;
}
@ -624,30 +574,19 @@ import 'programStyles';
}
}
} else if (item.ParentPrimaryImageTag) {
imgUrl = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, {
type: 'Primary',
maxWidth: width,
tag: item.ParentPrimaryImageTag
});
imgType = 'Primary';
imgTag = item.ParentPrimaryImageTag;
itemId = item.ParentPrimaryImageItemId;
} else if (item.SeriesPrimaryImageTag) {
imgUrl = apiClient.getScaledImageUrl(item.SeriesId, {
type: 'Primary',
maxWidth: width,
tag: item.SeriesPrimaryImageTag
});
imgType = 'Primary';
imgTag = item.SeriesPrimaryImageTag;
itemId = item.SeriesId;
} else if (item.AlbumId && item.AlbumPrimaryImageTag) {
imgType = 'Primary';
imgTag = item.AlbumPrimaryImageTag;
itemId = item.AlbumId;
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
imgUrl = apiClient.getScaledImageUrl(item.AlbumId, {
type: 'Primary',
maxHeight: height,
maxWidth: width,
tag: item.AlbumPrimaryImageTag
});
if (primaryImageAspectRatio) {
uiAspect = getDesiredAspect(shape);
if (uiAspect) {
@ -655,57 +594,46 @@ import 'programStyles';
}
}
} else if (item.Type === 'Season' && item.ImageTags && item.ImageTags.Thumb) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Thumb',
maxWidth: width,
tag: item.ImageTags.Thumb
});
imgType = 'Thumb';
imgTag = item.ImageTags.Thumb;
} else if (item.BackdropImageTags && item.BackdropImageTags.length) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Backdrop',
maxWidth: width,
tag: item.BackdropImageTags[0]
});
imgType = 'Backdrop';
imgTag = item.BackdropImageTags[0];
} else if (item.ImageTags && item.ImageTags.Thumb) {
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Thumb',
maxWidth: width,
tag: item.ImageTags.Thumb
});
imgType = 'Thumb';
imgTag = item.ImageTags.Thumb;
} else if (item.SeriesThumbImageTag && options.inheritThumb !== false) {
imgUrl = apiClient.getScaledImageUrl(item.SeriesId, {
type: 'Thumb',
maxWidth: width,
tag: item.SeriesThumbImageTag
});
imgType = 'Thumb';
imgTag = item.SeriesThumbImageTag;
itemId = item.SeriesId;
} else if (item.ParentThumbItemId && options.inheritThumb !== false) {
imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, {
type: 'Thumb',
maxWidth: width,
tag: item.ParentThumbImageTag
});
imgType = 'Thumb';
imgTag = item.ParentThumbImageTag;
itemId = item.ParentThumbItemId;
} else if (item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false) {
imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, {
type: 'Backdrop',
maxWidth: width,
tag: item.ParentBackdropImageTags[0]
});
imgType = 'Backdrop';
imgTag = item.ParentBackdropImageTags[0];
itemId = item.ParentBackdropItemId;
}
if (!itemId) {
itemId = item.Id;
}
if (imgTag && imgType) {
imgUrl = apiClient.getScaledImageUrl(itemId, {
type: imgType,
maxHeight: height,
maxWidth: width,
tag: imgTag
});
}
let blurHashes = options.imageBlurhashes || item.ImageBlurHashes || {};
return {
imgUrl: imgUrl,
blurhash: (blurHashes[imgType] || {})[imgTag],
forceName: forceName,
coverImage: coverImage
};
@ -1321,6 +1249,7 @@ import 'programStyles';
const imgInfo = getCardImageUrl(item, apiClient, options, shape);
const imgUrl = imgInfo.imgUrl;
const blurhash = imgInfo.blurhash;
const forceName = imgInfo.forceName;
@ -1445,15 +1374,20 @@ import 'programStyles';
cardContentClass += ' cardContent-shadow';
}
let blurhashAttrib = '';
if (blurhash && blurhash.length > 0) {
blurhashAttrib = 'data-blurhash="' + blurhash + '"';
}
if (layoutManager.tv) {
// Don't use the IMG tag with safari because it puts a white border around it
cardImageContainerOpen = imgUrl ? ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + ' lazy" data-src="' + imgUrl + '">') : ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + '">');
cardImageContainerOpen = imgUrl ? ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + ' lazy" data-src="' + imgUrl + '" ' + blurhashAttrib + '>') : ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + '">');
cardImageContainerClose = '</div>';
} else {
// Don't use the IMG tag with safari because it puts a white border around it
cardImageContainerOpen = imgUrl ? ('<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction lazy" data-src="' + imgUrl + '">') : ('<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction">');
cardImageContainerOpen = imgUrl ? ('<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction lazy" data-src="' + imgUrl + '" ' + blurhashAttrib + '>') : ('<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction">');
cardImageContainerClose = '</button>';
}

View file

@ -1,13 +1,23 @@
define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browser'], function (datetime, imageLoader, connectionManager, layoutManager, browser) {
'use strict';
/* eslint-disable indent */
var enableFocusTransform = !browser.slow && !browser.edge;
/**
* Module for building cards from item data.
* @module components/cardBuilder/chaptercardbuilder
*/
function buildChapterCardsHtml(item, chapters, options) {
import datetime from 'datetime';
import imageLoader from 'imageLoader';
import connectionManager from 'connectionManager';
import layoutManager from 'layoutManager';
import browser from 'browser';
const enableFocusTransform = !browser.slow && !browser.edge;
function buildChapterCardsHtml(item, chapters, options) {
// TODO move card creation code to Card component
var className = 'card itemAction chapterCard';
let className = 'card itemAction chapterCard';
if (layoutManager.tv) {
className += ' show-focus';
@ -17,12 +27,12 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse
}
}
var mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || [];
var videoStream = mediaStreams.filter(function (i) {
return i.Type === 'Video';
const mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || [];
const videoStream = mediaStreams.filter(({Type}) => {
return Type === 'Video';
})[0] || {};
var shape = (options.backdropShape || 'backdrop');
let shape = (options.backdropShape || 'backdrop');
if (videoStream.Width && videoStream.Height) {
@ -31,24 +41,24 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse
}
}
className += ' ' + shape + 'Card';
className += ` ${shape}Card`;
if (options.block || options.rows) {
className += ' block';
}
var html = '';
var itemsInRow = 0;
let html = '';
let itemsInRow = 0;
var apiClient = connectionManager.getApiClient(item.ServerId);
const apiClient = connectionManager.getApiClient(item.ServerId);
for (var i = 0, length = chapters.length; i < length; i++) {
for (let i = 0, length = chapters.length; i < length; i++) {
if (options.rows && itemsInRow === 0) {
html += '<div class="cardColumn">';
}
var chapter = chapters[i];
const chapter = chapters[i];
html += buildChapterCard(item, apiClient, chapter, i, options, className, shape);
itemsInRow++;
@ -62,50 +72,50 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse
return html;
}
function getImgUrl(item, chapter, index, maxWidth, apiClient) {
function getImgUrl({Id}, {ImageTag}, index, maxWidth, apiClient) {
if (chapter.ImageTag) {
if (ImageTag) {
return apiClient.getScaledImageUrl(item.Id, {
return apiClient.getScaledImageUrl(Id, {
maxWidth: maxWidth * 2,
tag: chapter.ImageTag,
tag: ImageTag,
type: 'Chapter',
index: index
index
});
}
return null;
}
function buildChapterCard(item, apiClient, chapter, index, options, className, shape) {
function buildChapterCard(item, apiClient, chapter, index, {width, coverImage}, className, shape) {
var imgUrl = getImgUrl(item, chapter, index, options.width || 400, apiClient);
const imgUrl = getImgUrl(item, chapter, index, width || 400, apiClient);
var cardImageContainerClass = 'cardContent cardContent-shadow cardImageContainer chapterCardImageContainer';
if (options.coverImage) {
let cardImageContainerClass = 'cardContent cardContent-shadow cardImageContainer chapterCardImageContainer';
if (coverImage) {
cardImageContainerClass += ' coveredImage';
}
var dataAttributes = ' data-action="play" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-positionticks="' + chapter.StartPositionTicks + '"';
var cardImageContainer = imgUrl ? ('<div class="' + cardImageContainerClass + ' lazy" data-src="' + imgUrl + '">') : ('<div class="' + cardImageContainerClass + '">');
const dataAttributes = ` data-action="play" data-isfolder="${item.IsFolder}" data-id="${item.Id}" data-serverid="${item.ServerId}" data-type="${item.Type}" data-mediatype="${item.MediaType}" data-positionticks="${chapter.StartPositionTicks}"`;
let cardImageContainer = imgUrl ? (`<div class="${cardImageContainerClass} lazy" data-src="${imgUrl}">`) : (`<div class="${cardImageContainerClass}">`);
if (!imgUrl) {
cardImageContainer += '<span class="material-icons cardImageIcon local_movies"></span>';
}
var nameHtml = '';
nameHtml += '<div class="cardText">' + chapter.Name + '</div>';
nameHtml += '<div class="cardText">' + datetime.getDisplayRunningTime(chapter.StartPositionTicks) + '</div>';
let nameHtml = '';
nameHtml += `<div class="cardText">${chapter.Name}</div>`;
nameHtml += `<div class="cardText">${datetime.getDisplayRunningTime(chapter.StartPositionTicks)}</div>`;
var cardBoxCssClass = 'cardBox';
var cardScalableClass = 'cardScalable';
const cardBoxCssClass = 'cardBox';
const cardScalableClass = 'cardScalable';
var html = '<button type="button" class="' + className + '"' + dataAttributes + '><div class="' + cardBoxCssClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder-' + shape + '"></div>' + cardImageContainer + '</div><div class="innerCardFooter">' + nameHtml + '</div></div></div></button>';
const html = `<button type="button" class="${className}"${dataAttributes}><div class="${cardBoxCssClass}"><div class="${cardScalableClass}"><div class="cardPadder-${shape}"></div>${cardImageContainer}</div><div class="innerCardFooter">${nameHtml}</div></div></div></button>`;
return html;
}
function buildChapterCards(item, chapters, options) {
export function buildChapterCards(item, chapters, options) {
if (options.parentContainer) {
// Abort if the container has been disposed
@ -121,15 +131,16 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse
}
}
var html = buildChapterCardsHtml(item, chapters, options);
const html = buildChapterCardsHtml(item, chapters, options);
options.itemsContainer.innerHTML = html;
imageLoader.lazyChildren(options.itemsContainer);
}
return {
buildChapterCards: buildChapterCards
};
/* eslint-enable indent */
export default {
buildChapterCards: buildChapterCards
};
});

View file

@ -1,7 +1,13 @@
define(['cardBuilder'], function (cardBuilder) {
'use strict';
/* eslint-disable indent */
function buildPeopleCards(items, options) {
/**
* Module for building cards from item data.
* @module components/cardBuilder/peoplecardbuilder
*/
import cardBuilder from 'cardBuilder';
export function buildPeopleCards(items, options) {
options = Object.assign(options || {}, {
cardLayout: false,
@ -15,8 +21,8 @@ define(['cardBuilder'], function (cardBuilder) {
cardBuilder.buildCards(items, options);
}
return {
buildPeopleCards: buildPeopleCards
};
/* eslint-enable indent */
});
export default {
buildPeopleCards: buildPeopleCards
};

View file

@ -1,234 +0,0 @@
define(['events'], function (events) {
'use strict';
// LinkParser
//
// https://github.com/ravisorg/LinkParser
//
// Locate and extract almost any URL within a string. Handles protocol-less domains, IPv4 and
// IPv6, unrecognised TLDs, and more.
//
// This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
// http://creativecommons.org/licenses/by-sa/4.0/
(function () {
// Original URL regex from the Android android.text.util.Linkify function, found here:
// http://stackoverflow.com/a/19696443
//
// However there were problems with it, most probably related to the fact it was
// written in 2007, and it's been highly modified.
//
// 1) I didn't like the fact that it was tied to specific TLDs, since new ones
// are being added all the time it wouldn't be reasonable to expect developer to
// be continually updating their regular expressions.
//
// 2) It didn't allow unicode characters in the domains which are now allowed in
// many languages, (including some IDN TLDs). Again these are constantly being
// added to and it doesn't seem reasonable to hard-code them. Note this ended up
// not being possible in standard JS due to the way it handles multibyte strings.
// It is possible using XRegExp, however a big performance hit results. Disabled
// for now.
//
// 3) It didn't allow for IPv6 hostnames
// IPv6 regex from http://stackoverflow.com/a/17871737
//
// 4) It was very poorly commented
//
// 5) It wasn't as smart as it could have been about what should be part of a
// URL and what should be part of human language.
var protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)';
var credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters)
+ "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters)
+ '\\@)';
// IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452
// by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License
// http://intermapper.com/
var ipv6 = '('
+ '(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))'
+ '|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
+ '|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
+ '|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ ')(%.+)?';
var ipv4 = '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])';
// This would have been a lot cleaner if JS RegExp supported conditionals...
var linkRegExpString =
// begin match for protocol / username / password / host
'(?:'
// ============================
// If we have a recognized protocol at the beginning of the URL, we're
// more relaxed about what we accept, because we assume the user wants
// this to be a URL, and we're not accidentally matching human language
+ protocols + '?'
// optional username:password@
+ credentials + '?'
// IP address (both v4 and v6)
+ '(?:'
// IPv6
+ ipv6
// IPv4
+ '|' + ipv4
+ ')'
// end match for protocol / username / password / host
+ ')'
// optional port number
+ '(?:\\:\\d{1,5})?'
// plus optional path and query params (no unicode allowed here?)
+ '(?:'
+ '\\/(?:'
// some characters we'll accept because it's unlikely human language
// would use them after a URL unless they were part of the url
+ '(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])'
+ '|(?:\\%[a-f0-9]{2})'
// some characters are much more likely to be used AFTER a url and
// were not intended to be included in the url itself. Mostly end
// of sentence type things. It's also likely that the URL would
// still work if any of these characters were missing from the end
// because we parsed it incorrectly. For these characters to be accepted
// they must be followed by another character that we're reasonably
// sure is part of the url
+ "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))"
+ ')*'
+ '|\\b|\$'
+ ')';
// regex = XRegExp(regex,'gi');
var linkRegExp = RegExp(linkRegExpString, 'gi');
var protocolRegExp = RegExp('^' + protocols, 'i');
// if url doesn't begin with a known protocol, add http by default
function ensureProtocol(url) {
if (!url.match(protocolRegExp)) {
url = 'http://' + url;
}
return url;
}
// look for links in the text
var LinkParser = {
parse: function (text) {
var links = [];
var match;
// eslint-disable-next-line no-cond-assign
while (match = linkRegExp.exec(text)) {
console.debug(match);
var txt = match[0];
var pos = match.index;
var len = txt.length;
var url = ensureProtocol(text);
links.push({ 'pos': pos, 'text': txt, 'len': len, 'url': url });
}
return links;
}
};
window.LinkParser = LinkParser;
})();
var cache = {};
function isValidIpAddress(address) {
var links = LinkParser.parse(address);
return links.length == 1;
}
function isLocalIpAddress(address) {
address = address.toLowerCase();
if (address.indexOf('127.0.0.1') !== -1) {
return true;
}
if (address.indexOf('localhost') !== -1) {
return true;
}
return false;
}
function getServerAddress(apiClient) {
var serverAddress = apiClient.serverAddress();
if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) {
return Promise.resolve(serverAddress);
}
var cachedValue = getCachedValue(serverAddress);
if (cachedValue) {
return Promise.resolve(cachedValue);
}
return apiClient.getEndpointInfo().then(function (endpoint) {
if (endpoint.IsInNetwork) {
return apiClient.getPublicSystemInfo().then(function (info) {
var localAddress = info.LocalAddress;
if (!localAddress) {
console.debug('No valid local address returned, defaulting to external one');
localAddress = serverAddress;
}
addToCache(serverAddress, localAddress);
return localAddress;
});
} else {
addToCache(serverAddress, serverAddress);
return serverAddress;
}
});
}
function clearCache() {
cache = {};
}
function addToCache(key, value) {
cache[key] = {
value: value,
time: new Date().getTime()
};
}
function getCachedValue(key) {
var obj = cache[key];
if (obj && (new Date().getTime() - obj.time) < 180000) {
return obj.value;
}
return null;
}
events.on(ConnectionManager, 'localusersignedin', clearCache);
events.on(ConnectionManager, 'localusersignedout', clearCache);
return {
getServerAddress: getServerAddress
};
});

File diff suppressed because it is too large Load diff

View file

@ -181,7 +181,9 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
context.querySelector('#chkThemeSong').checked = userSettings.enableThemeSongs();
context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos();
context.querySelector('#chkFadein').checked = userSettings.enableFastFadein();
context.querySelector('#chkBlurhash').checked = userSettings.enableBlurhash();
context.querySelector('#chkBackdrops').checked = userSettings.enableBackdrops();
context.querySelector('#chkDetailsBanner').checked = userSettings.detailsBanner();
context.querySelector('#selectLanguage').value = userSettings.language() || '';
context.querySelector('.selectDateTimeLocale').value = userSettings.dateTimeLocale() || '';
@ -222,7 +224,9 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
userSettingsInstance.skin(context.querySelector('.selectSkin').value);
userSettingsInstance.enableFastFadein(context.querySelector('#chkFadein').checked);
userSettingsInstance.enableBlurhash(context.querySelector('#chkBlurhash').checked);
userSettingsInstance.enableBackdrops(context.querySelector('#chkBackdrops').checked);
userSettingsInstance.detailsBanner(context.querySelector('#chkDetailsBanner').checked);
if (user.Id === apiClient.getCurrentUserId()) {
skinManager.setTheme(userSettingsInstance.theme());

View file

@ -143,17 +143,33 @@
<select is="emby-select" class="selectSoundEffects" label="${LabelSoundEffects}"></select>
</div>
<div class="inputContainer inputContainer-withDescription fldFadein">
<div class="inputContainer inputContainer-withDescription">
<input is="emby-input" type="number" id="txtLibraryPageSize" pattern="[0-9]*" required="required" min="0" max="1000" step="1" label="${LabelLibraryPageSize}" />
<div class="fieldDescription">${LabelLibraryPageSizeHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldFadein">
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkFadein" />
<span>${EnableFastImageFadeIn}</span>
<span>${EnableFasterAnimations}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${EnableFastImageFadeInHelp}</div>
<div class="fieldDescription checkboxFieldDescription">${EnableFasterAnimationsHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkBlurhash" />
<span>${EnableBlurhash}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${EnableBlurhashHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkDetailsBanner" />
<span>${EnableDetailsBanner}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${EnableDetailsBannerHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldBackdrops hide">

View file

@ -1,21 +1,19 @@
define([], function () {
'use strict';
/* eslint-disable indent */
export function getFetchPromise(request) {
function getFetchPromise(request) {
var headers = request.headers || {};
const headers = request.headers || {};
if (request.dataType === 'json') {
headers.accept = 'application/json';
}
var fetchRequest = {
const fetchRequest = {
headers: headers,
method: request.type,
credentials: 'same-origin'
};
var contentType = request.contentType;
let contentType = request.contentType;
if (request.data) {
@ -33,12 +31,12 @@ define([], function () {
headers['Content-Type'] = contentType;
}
var url = request.url;
let url = request.url;
if (request.query) {
var paramString = paramsToString(request.query);
const paramString = paramsToString(request.query);
if (paramString) {
url += '?' + paramString;
url += `?${paramString}`;
}
}
@ -51,11 +49,11 @@ define([], function () {
function fetchWithTimeout(url, options, timeoutMs) {
console.debug('fetchWithTimeout: timeoutMs: ' + timeoutMs + ', url: ' + url);
console.debug(`fetchWithTimeout: timeoutMs: ${timeoutMs}, url: ${url}`);
return new Promise(function (resolve, reject) {
var timeout = setTimeout(reject, timeoutMs);
const timeout = setTimeout(reject, timeoutMs);
options = options || {};
options.credentials = 'same-origin';
@ -63,50 +61,47 @@ define([], function () {
fetch(url, options).then(function (response) {
clearTimeout(timeout);
console.debug('fetchWithTimeout: succeeded connecting to url: ' + url);
console.debug(`fetchWithTimeout: succeeded connecting to url: ${url}`);
resolve(response);
}, function (error) {
clearTimeout(timeout);
console.debug('fetchWithTimeout: timed out connecting to url: ' + url);
console.debug(`fetchWithTimeout: timed out connecting to url: ${url}`);
reject();
reject(error);
});
});
}
/**
* @param params {Record<string, string | number | boolean>}
* @returns {string} Query string
*/
function paramsToString(params) {
var values = [];
for (var key in params) {
var value = params[key];
if (value !== null && value !== undefined && value !== '') {
values.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
}
}
return values.join('&');
return Object.entries(params)
// eslint-disable-next-line no-unused-vars
.filter(([_, v]) => v !== null && v !== undefined && v !== '')
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&');
}
function ajax(request) {
export function ajax(request) {
if (!request) {
throw new Error('Request cannot be null');
}
request.headers = request.headers || {};
console.debug('requesting url: ' + request.url);
console.debug(`requesting url: ${request.url}`);
return getFetchPromise(request).then(function (response) {
console.debug('response status: ' + response.status + ', url: ' + request.url);
console.debug(`response status: ${response.status}, url: ${request.url}`);
if (response.status < 400) {
if (request.dataType === 'json' || request.headers.accept === 'application/json') {
return response.json();
} else if (request.dataType === 'text' || (response.headers.get('Content-Type') || '').toLowerCase().indexOf('text/') === 0) {
} else if (request.dataType === 'text' || (response.headers.get('Content-Type') || '').toLowerCase().startsWith('text/')) {
return response.text();
} else {
return response;
@ -115,12 +110,8 @@ define([], function () {
return Promise.reject(response);
}
}, function (err) {
console.error('request failed to url: ' + request.url);
console.error(`request failed to url: ${request.url}`);
throw err;
});
}
return {
getFetchPromise: getFetchPromise,
ajax: ajax
};
});
/* eslint-enable indent */

View file

@ -1,21 +1,28 @@
define(['dom', 'dialogHelper', 'globalize', 'connectionManager', 'events', 'browser', 'require', 'emby-checkbox', 'emby-collapse', 'css!./style'], function (dom, dialogHelper, globalize, connectionManager, events, browser, require) {
'use strict';
import dom from 'dom';
import dialogHelper from 'dialogHelper';
import globalize from 'globalize';
import connectionManager from 'connectionManager';
import events from 'events';
import 'emby-checkbox';
import 'emby-collapse';
import 'css!./style.css';
/* eslint-disable indent */
function renderOptions(context, selector, cssClass, items, isCheckedFn) {
var elem = context.querySelector(selector);
const elem = context.querySelector(selector);
if (items.length) {
elem.classList.remove('hide');
} else {
elem.classList.add('hide');
}
var html = '';
let html = '';
html += '<div class="checkboxList">';
html += items.map(function (filter) {
var itemHtml = '';
var checkedHtml = isCheckedFn(filter) ? ' checked' : '';
let itemHtml = '';
const checkedHtml = isCheckedFn(filter) ? 'checked' : '';
itemHtml += '<label>';
itemHtml += '<input is="emby-checkbox" type="checkbox"' + checkedHtml + ' data-filter="' + filter + '" class="' + cssClass + '"/>';
itemHtml += '<span>' + filter + '</span>';
itemHtml += `<input is="emby-checkbox" type="checkbox" ${checkedHtml} data-filter="${filter}" class="${cssClass}"/>`;
itemHtml += `<span>${filter}</span>`;
itemHtml += '</label>';
return itemHtml;
}).join('');
@ -24,21 +31,24 @@ define(['dom', 'dialogHelper', 'globalize', 'connectionManager', 'events', 'brow
}
function renderFilters(context, result, query) {
if (result.Tags) {
result.Tags.length = Math.min(result.Tags.length, 50);
}
renderOptions(context, '.genreFilters', 'chkGenreFilter', result.Genres, function (i) {
var delimeter = '|';
return (delimeter + (query.Genres || '') + delimeter).indexOf(delimeter + i + delimeter) != -1;
const delimeter = '|';
return (delimeter + (query.Genres || '') + delimeter).includes(delimeter + i + delimeter);
});
renderOptions(context, '.officialRatingFilters', 'chkOfficialRatingFilter', result.OfficialRatings, function (i) {
var delimeter = '|';
return (delimeter + (query.OfficialRatings || '') + delimeter).indexOf(delimeter + i + delimeter) != -1;
const delimeter = '|';
return (delimeter + (query.OfficialRatings || '') + delimeter).includes(delimeter + i + delimeter);
});
renderOptions(context, '.tagFilters', 'chkTagFilter', result.Tags, function (i) {
var delimeter = '|';
return (delimeter + (query.Tags || '') + delimeter).indexOf(delimeter + i + delimeter) != -1;
const delimeter = '|';
return (delimeter + (query.Tags || '') + delimeter).includes(delimeter + i + delimeter);
});
renderOptions(context, '.yearFilters', 'chkYearFilter', result.Years, function (i) {
var delimeter = ',';
return (delimeter + (query.Years || '') + delimeter).indexOf(delimeter + i + delimeter) != -1;
const delimeter = ',';
return (delimeter + (query.Years || '') + delimeter).includes(delimeter + i + delimeter);
});
}
@ -52,59 +62,58 @@ define(['dom', 'dialogHelper', 'globalize', 'connectionManager', 'events', 'brow
});
}
/**
* @param context {HTMLDivElement} Dialog
* @param options {any} Options
*/
function updateFilterControls(context, options) {
var elems;
var i;
var length;
var query = options.query;
const query = options.query;
if (options.mode == 'livetvchannels') {
context.querySelector('.chkFavorite').checked = query.IsFavorite == true;
context.querySelector('.chkLikes').checked = query.IsLiked == true;
context.querySelector('.chkDislikes').checked = query.IsDisliked == true;
if (options.mode === 'livetvchannels') {
context.querySelector('.chkFavorite').checked = query.IsFavorite === true;
context.querySelector('.chkLikes').checked = query.IsLiked === true;
context.querySelector('.chkDislikes').checked = query.IsDisliked === true;
} else {
elems = context.querySelectorAll('.chkStandardFilter');
for (i = 0, length = elems.length; i < length; i++) {
var chkStandardFilter = elems[i];
var filters = ',' + (query.Filters || '');
var filterName = chkStandardFilter.getAttribute('data-filter');
chkStandardFilter.checked = filters.indexOf(',' + filterName) != -1;
for (const elem of context.querySelectorAll('.chkStandardFilter')) {
const filters = `,${query.Filters || ''}`;
const filterName = elem.getAttribute('data-filter');
elem.checked = filters.includes(`,${filterName}`);
}
}
elems = context.querySelectorAll('.chkVideoTypeFilter');
for (i = 0, length = elems.length; i < length; i++) {
var chkVideoTypeFilter = elems[i];
var filters = ',' + (query.VideoTypes || '');
var filterName = chkVideoTypeFilter.getAttribute('data-filter');
chkVideoTypeFilter.checked = filters.indexOf(',' + filterName) != -1;
for (const elem of context.querySelectorAll('.chkVideoTypeFilter')) {
const filters = `,${query.VideoTypes || ''}`;
const filterName = elem.getAttribute('data-filter');
elem.checked = filters.includes(`,${filterName}`);
}
context.querySelector('.chk3DFilter').checked = query.Is3D == true;
context.querySelector('.chkHDFilter').checked = query.IsHD == true;
context.querySelector('.chk4KFilter').checked = query.Is4K == true;
context.querySelector('.chkSDFilter').checked = query.IsHD == true;
context.querySelector('#chkSubtitle').checked = query.HasSubtitles == true;
context.querySelector('#chkTrailer').checked = query.HasTrailer == true;
context.querySelector('#chkThemeSong').checked = query.HasThemeSong == true;
context.querySelector('#chkThemeVideo').checked = query.HasThemeVideo == true;
context.querySelector('#chkSpecialFeature').checked = query.HasSpecialFeature == true;
context.querySelector('#chkSpecialEpisode').checked = query.ParentIndexNumber == 0;
context.querySelector('#chkMissingEpisode').checked = query.IsMissing == true;
context.querySelector('#chkFutureEpisode').checked = query.IsUnaired == true;
for (i = 0, length = elems.length; i < length; i++) {
var chkStatus = elems[i];
var filters = ',' + (query.SeriesStatus || '');
var filterName = chkStatus.getAttribute('data-filter');
chkStatus.checked = filters.indexOf(',' + filterName) != -1;
context.querySelector('.chk3DFilter').checked = query.Is3D === true;
context.querySelector('.chkHDFilter').checked = query.IsHD === true;
context.querySelector('.chk4KFilter').checked = query.Is4K === true;
context.querySelector('.chkSDFilter').checked = query.IsHD === true;
context.querySelector('#chkSubtitle').checked = query.HasSubtitles === true;
context.querySelector('#chkTrailer').checked = query.HasTrailer === true;
context.querySelector('#chkThemeSong').checked = query.HasThemeSong === true;
context.querySelector('#chkThemeVideo').checked = query.HasThemeVideo === true;
context.querySelector('#chkSpecialFeature').checked = query.HasSpecialFeature === true;
context.querySelector('#chkSpecialEpisode').checked = query.ParentIndexNumber === 0;
context.querySelector('#chkMissingEpisode').checked = query.IsMissing === true;
context.querySelector('#chkFutureEpisode').checked = query.IsUnaired === true;
for (const elem of context.querySelectorAll('.chkStatus')) {
const filters = `,${query.SeriesStatus || ''}`;
const filterName = elem.getAttribute('data-filter');
elem.checked = filters.includes(`,${filterName}`);
}
}
/**
* @param instance {FilterDialog} An instance of FilterDialog
*/
function triggerChange(instance) {
events.trigger(instance, 'filterchange');
}
function setVisibility(context, options) {
if (options.mode == 'livetvchannels' || options.mode == 'albums' || options.mode == 'artists' || options.mode == 'albumartists' || options.mode == 'songs') {
if (options.mode === 'livetvchannels' || options.mode === 'albums' || options.mode === 'artists' || options.mode === 'albumartists' || options.mode === 'songs') {
hideByClass(context, 'videoStandard');
}
@ -115,263 +124,287 @@ define(['dom', 'dialogHelper', 'globalize', 'connectionManager', 'events', 'brow
context.querySelector('.yearFilters').classList.remove('hide');
}
if (options.mode == 'movies' || options.mode == 'episodes') {
if (options.mode === 'movies' || options.mode === 'episodes') {
context.querySelector('.videoTypeFilters').classList.remove('hide');
}
if (options.mode == 'movies' || options.mode == 'series' || options.mode == 'episodes') {
if (options.mode === 'movies' || options.mode === 'series' || options.mode === 'episodes') {
context.querySelector('.features').classList.remove('hide');
}
if (options.mode == 'series') {
if (options.mode === 'series') {
context.querySelector('.seriesStatus').classList.remove('hide');
}
if (options.mode == 'episodes') {
if (options.mode === 'episodes') {
showByClass(context, 'episodeFilter');
}
}
function showByClass(context, className) {
var elems = context.querySelectorAll('.' + className);
for (var i = 0, length = elems.length; i < length; i++) {
elems[i].classList.remove('hide');
for (const elem of context.querySelectorAll(`.${className}`)) {
elem.classList.remove('hide');
}
}
function hideByClass(context, className) {
var elems = context.querySelectorAll('.' + className);
for (var i = 0, length = elems.length; i < length; i++) {
elems[i].classList.add('hide');
for (const elem of context.querySelectorAll(`.${className}`)) {
elem.classList.add('hide');
}
}
function enableDynamicFilters(mode) {
return mode == 'movies' || mode == 'series' || mode == 'albums' || mode == 'albumartists' || mode == 'artists' || mode == 'songs' || mode == 'episodes';
return mode === 'movies' || mode === 'series' || mode === 'albums' || mode === 'albumartists' || mode === 'artists' || mode === 'songs' || mode === 'episodes';
}
return function (options) {
function onFavoriteChange() {
var query = options.query;
query.StartIndex = 0;
query.IsFavorite = !!this.checked || null;
triggerChange(self);
class FilterDialog {
constructor(options) {
/**
* @private
*/
this.options = options;
}
function onStandardFilterChange() {
var query = options.query;
var filterName = this.getAttribute('data-filter');
var filters = query.Filters || '';
filters = (',' + filters).replace(',' + filterName, '').substring(1);
/**
* @private
*/
onFavoriteChange(elem) {
const query = this.options.query;
query.StartIndex = 0;
query.IsFavorite = !!elem.checked || null;
triggerChange(this);
}
if (this.checked) {
filters = filters ? filters + ',' + filterName : filterName;
/**
* @private
*/
onStandardFilterChange(elem) {
const query = this.options.query;
const filterName = elem.getAttribute('data-filter');
let filters = query.Filters || '';
filters = (`,${filters}`).replace(`,${filterName}`, '').substring(1);
if (elem.checked) {
filters = filters ? `${filters},${filterName}` : filterName;
}
query.StartIndex = 0;
query.Filters = filters;
triggerChange(self);
triggerChange(this);
}
function onVideoTypeFilterChange() {
var query = options.query;
var filterName = this.getAttribute('data-filter');
var filters = query.VideoTypes || '';
filters = (',' + filters).replace(',' + filterName, '').substring(1);
/**
* @private
*/
onVideoTypeFilterChange(elem) {
const query = this.options.query;
const filterName = elem.getAttribute('data-filter');
let filters = query.VideoTypes || '';
filters = (`,${filters}`).replace(`,${filterName}`, '').substring(1);
if (this.checked) {
filters = filters ? filters + ',' + filterName : filterName;
if (elem.checked) {
filters = filters ? `${filters},${filterName}` : filterName;
}
query.StartIndex = 0;
query.VideoTypes = filters;
triggerChange(self);
triggerChange(this);
}
function onStatusChange() {
var query = options.query;
var filterName = this.getAttribute('data-filter');
var filters = query.SeriesStatus || '';
filters = (',' + filters).replace(',' + filterName, '').substring(1);
/**
* @private
*/
onStatusChange(elem) {
const query = this.options.query;
const filterName = elem.getAttribute('data-filter');
let filters = query.SeriesStatus || '';
filters = (`,${filters}`).replace(`,${filterName}`, '').substring(1);
if (this.checked) {
filters = filters ? filters + ',' + filterName : filterName;
if (elem.checked) {
filters = filters ? `${filters},${filterName}` : filterName;
}
query.SeriesStatus = filters;
query.StartIndex = 0;
triggerChange(self);
triggerChange(this);
}
function bindEvents(context) {
var elems;
var i;
var length;
var query = options.query;
/**
* @param context {HTMLDivElement} The dialog
*/
bindEvents(context) {
const query = this.options.query;
if (options.mode == 'livetvchannels') {
elems = context.querySelectorAll('.chkFavorite');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('change', onFavoriteChange);
if (this.options.mode === 'livetvchannels') {
for (const elem of context.querySelectorAll('.chkFavorite')) {
elem.addEventListener('change', () => this.onFavoriteChange(elem));
}
context.querySelector('.chkLikes').addEventListener('change', function () {
const chkLikes = context.querySelector('.chkLikes');
chkLikes.addEventListener('change', () => {
query.StartIndex = 0;
query.IsLiked = this.checked ? true : null;
triggerChange(self);
query.IsLiked = chkLikes.checked ? true : null;
triggerChange(this);
});
context.querySelector('.chkDislikes').addEventListener('change', function () {
const chkDislikes = context.querySelector('.chkDislikes');
chkDislikes.addEventListener('change', () => {
query.StartIndex = 0;
query.IsDisliked = this.checked ? true : null;
triggerChange(self);
query.IsDisliked = chkDislikes.checked ? true : null;
triggerChange(this);
});
} else {
elems = context.querySelectorAll('.chkStandardFilter');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('change', onStandardFilterChange);
for (const elem of context.querySelectorAll('.chkStandardFilter')) {
elem.addEventListener('change', () => this.onStandardFilterChange(elem));
}
}
elems = context.querySelectorAll('.chkVideoTypeFilter');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('change', onVideoTypeFilterChange);
for (const elem of context.querySelectorAll('.chkVideoTypeFilter')) {
elem.addEventListener('change', () => this.onVideoTypeFilterChange(elem));
}
context.querySelector('.chk3DFilter').addEventListener('change', function () {
const chk3DFilter = context.querySelector('.chk3DFilter');
chk3DFilter.addEventListener('change', () => {
query.StartIndex = 0;
query.Is3D = this.checked ? true : null;
triggerChange(self);
query.Is3D = chk3DFilter.checked ? true : null;
triggerChange(this);
});
context.querySelector('.chk4KFilter').addEventListener('change', function () {
const chk4KFilter = context.querySelector('.chk4KFilter');
chk4KFilter.addEventListener('change', () => {
query.StartIndex = 0;
query.Is4K = this.checked ? true : null;
triggerChange(self);
query.Is4K = chk4KFilter.checked ? true : null;
triggerChange(this);
});
context.querySelector('.chkHDFilter').addEventListener('change', function () {
const chkHDFilter = context.querySelector('.chkHDFilter');
chkHDFilter.addEventListener('change', () => {
query.StartIndex = 0;
query.IsHD = this.checked ? true : null;
triggerChange(self);
query.IsHD = chkHDFilter.checked ? true : null;
triggerChange(this);
});
context.querySelector('.chkSDFilter').addEventListener('change', function () {
const chkSDFilter = context.querySelector('.chkSDFilter');
chkSDFilter.addEventListener('change', () => {
query.StartIndex = 0;
query.IsHD = this.checked ? false : null;
triggerChange(self);
query.IsHD = chkSDFilter.checked ? false : null;
triggerChange(this);
});
elems = context.querySelectorAll('.chkStatus');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('change', onStatusChange);
for (const elem of context.querySelectorAll('.chkStatus')) {
elem.addEventListener('change', () => this.onStatusChange(elem));
}
context.querySelector('#chkTrailer').addEventListener('change', function () {
const chkTrailer = context.querySelector('#chkTrailer');
chkTrailer.addEventListener('change', () => {
query.StartIndex = 0;
query.HasTrailer = this.checked ? true : null;
triggerChange(self);
query.HasTrailer = chkTrailer.checked ? true : null;
triggerChange(this);
});
context.querySelector('#chkThemeSong').addEventListener('change', function () {
const chkThemeSong = context.querySelector('#chkThemeSong');
chkThemeSong.addEventListener('change', () => {
query.StartIndex = 0;
query.HasThemeSong = this.checked ? true : null;
triggerChange(self);
query.HasThemeSong = chkThemeSong.checked ? true : null;
triggerChange(this);
});
context.querySelector('#chkSpecialFeature').addEventListener('change', function () {
const chkSpecialFeature = context.querySelector('#chkSpecialFeature');
chkSpecialFeature.addEventListener('change', () => {
query.StartIndex = 0;
query.HasSpecialFeature = this.checked ? true : null;
triggerChange(self);
query.HasSpecialFeature = chkSpecialFeature.checked ? true : null;
triggerChange(this);
});
context.querySelector('#chkThemeVideo').addEventListener('change', function () {
const chkThemeVideo = context.querySelector('#chkThemeVideo');
chkThemeVideo.addEventListener('change', () => {
query.StartIndex = 0;
query.HasThemeVideo = this.checked ? true : null;
triggerChange(self);
query.HasThemeVideo = chkThemeVideo.checked ? true : null;
triggerChange(this);
});
context.querySelector('#chkMissingEpisode').addEventListener('change', function () {
const chkMissingEpisode = context.querySelector('#chkMissingEpisode');
chkMissingEpisode.addEventListener('change', () => {
query.StartIndex = 0;
query.IsMissing = this.checked ? true : false;
triggerChange(self);
query.IsMissing = !!chkMissingEpisode.checked;
triggerChange(this);
});
context.querySelector('#chkSpecialEpisode').addEventListener('change', function () {
const chkSpecialEpisode = context.querySelector('#chkSpecialEpisode');
chkSpecialEpisode.addEventListener('change', () => {
query.StartIndex = 0;
query.ParentIndexNumber = this.checked ? 0 : null;
triggerChange(self);
query.ParentIndexNumber = chkSpecialEpisode.checked ? 0 : null;
triggerChange(this);
});
context.querySelector('#chkFutureEpisode').addEventListener('change', function () {
const chkFutureEpisode = context.querySelector('#chkFutureEpisode');
chkFutureEpisode.addEventListener('change', () => {
query.StartIndex = 0;
if (this.checked) {
if (chkFutureEpisode.checked) {
query.IsUnaired = true;
query.IsVirtualUnaired = null;
} else {
query.IsUnaired = null;
query.IsVirtualUnaired = false;
}
triggerChange(self);
triggerChange(this);
});
context.querySelector('#chkSubtitle').addEventListener('change', function () {
const chkSubtitle = context.querySelector('#chkSubtitle');
chkSubtitle.addEventListener('change', () => {
query.StartIndex = 0;
query.HasSubtitles = this.checked ? true : null;
triggerChange(self);
query.HasSubtitles = chkSubtitle.checked ? true : null;
triggerChange(this);
});
context.addEventListener('change', function (e) {
var chkGenreFilter = dom.parentWithClass(e.target, 'chkGenreFilter');
context.addEventListener('change', (e) => {
const chkGenreFilter = dom.parentWithClass(e.target, 'chkGenreFilter');
if (chkGenreFilter) {
var filterName = chkGenreFilter.getAttribute('data-filter');
var filters = query.Genres || '';
var delimiter = '|';
const filterName = chkGenreFilter.getAttribute('data-filter');
let filters = query.Genres || '';
const delimiter = '|';
filters = (delimiter + filters).replace(delimiter + filterName, '').substring(1);
if (chkGenreFilter.checked) {
filters = filters ? (filters + delimiter + filterName) : filterName;
}
query.StartIndex = 0;
query.Genres = filters;
triggerChange(self);
triggerChange(this);
return;
}
var chkTagFilter = dom.parentWithClass(e.target, 'chkTagFilter');
const chkTagFilter = dom.parentWithClass(e.target, 'chkTagFilter');
if (chkTagFilter) {
var filterName = chkTagFilter.getAttribute('data-filter');
var filters = query.Tags || '';
var delimiter = '|';
const filterName = chkTagFilter.getAttribute('data-filter');
let filters = query.Tags || '';
const delimiter = '|';
filters = (delimiter + filters).replace(delimiter + filterName, '').substring(1);
if (chkTagFilter.checked) {
filters = filters ? (filters + delimiter + filterName) : filterName;
}
query.StartIndex = 0;
query.Tags = filters;
triggerChange(self);
triggerChange(this);
return;
}
var chkYearFilter = dom.parentWithClass(e.target, 'chkYearFilter');
const chkYearFilter = dom.parentWithClass(e.target, 'chkYearFilter');
if (chkYearFilter) {
var filterName = chkYearFilter.getAttribute('data-filter');
var filters = query.Years || '';
var delimiter = ',';
const filterName = chkYearFilter.getAttribute('data-filter');
let filters = query.Years || '';
const delimiter = ',';
filters = (delimiter + filters).replace(delimiter + filterName, '').substring(1);
if (chkYearFilter.checked) {
filters = filters ? (filters + delimiter + filterName) : filterName;
}
query.StartIndex = 0;
query.Years = filters;
triggerChange(self);
triggerChange(this);
return;
}
var chkOfficialRatingFilter = dom.parentWithClass(e.target, 'chkOfficialRatingFilter');
const chkOfficialRatingFilter = dom.parentWithClass(e.target, 'chkOfficialRatingFilter');
if (chkOfficialRatingFilter) {
var filterName = chkOfficialRatingFilter.getAttribute('data-filter');
var filters = query.OfficialRatings || '';
var delimiter = '|';
const filterName = chkOfficialRatingFilter.getAttribute('data-filter');
let filters = query.OfficialRatings || '';
const delimiter = '|';
filters = (delimiter + filters).replace(delimiter + filterName, '').substring(1);
if (chkOfficialRatingFilter.checked) {
filters = filters ? (filters + delimiter + filterName) : filterName;
}
query.StartIndex = 0;
query.OfficialRatings = filters;
triggerChange(self);
return;
triggerChange(this);
}
});
}
var self = this;
self.show = function () {
return new Promise(function (resolve, reject) {
require(['text!./filterdialog.template.html'], function (template) {
var dlg = dialogHelper.createDialog({
show() {
return import('text!./filterdialog.template.html').then(({default: template}) => {
return new Promise((resolve) => {
const dlg = dialogHelper.createDialog({
removeOnClose: true,
modal: false
});
@ -380,18 +413,21 @@ define(['dom', 'dialogHelper', 'globalize', 'connectionManager', 'events', 'brow
dlg.classList.add('formDialog');
dlg.classList.add('filterDialog');
dlg.innerHTML = globalize.translateDocument(template);
setVisibility(dlg, options);
setVisibility(dlg, this.options);
dialogHelper.open(dlg);
dlg.addEventListener('close', resolve);
updateFilterControls(dlg, options);
bindEvents(dlg);
if (enableDynamicFilters(options.mode)) {
updateFilterControls(dlg, this.options);
this.bindEvents(dlg);
if (enableDynamicFilters(this.options.mode)) {
dlg.classList.add('dynamicFilterDialog');
var apiClient = connectionManager.getApiClient(options.serverId);
loadDynamicFilters(dlg, apiClient, apiClient.getCurrentUserId(), options.query);
const apiClient = connectionManager.getApiClient(this.options.serverId);
loadDynamicFilters(dlg, apiClient, apiClient.getCurrentUserId(), this.options.query);
}
});
});
};
};
});
}
}
/* eslint-enable indent */
export default FilterDialog;

View file

@ -229,7 +229,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
var options = {
Limit: limit,
Fields: 'PrimaryImageAspectRatio,BasicSyncInfo',
Fields: 'PrimaryImageAspectRatio,BasicSyncInfo,Path',
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb',
ParentId: parentId
@ -667,7 +667,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
var apiClient = connectionManager.getApiClient(serverId);
return apiClient.getNextUpEpisodes({
Limit: enableScrollX() ? 24 : 15,
Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo',
Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo,Path',
UserId: apiClient.getCurrentUserId(),
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb',

View file

@ -1,545 +0,0 @@
define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelper'], function (events, browser, require, appHost, appSettings, htmlMediaHelper) {
'use strict';
function getDefaultProfile() {
return new Promise(function (resolve, reject) {
require(['browserdeviceprofile'], function (profileBuilder) {
resolve(profileBuilder({}));
});
});
}
var fadeTimeout;
function fade(instance, elem, startingVolume) {
instance._isFadingOut = true;
// Need to record the starting volume on each pass rather than querying elem.volume
// This is due to iOS safari not allowing volume changes and always returning the system volume value
var newVolume = Math.max(0, startingVolume - 0.15);
console.debug('fading volume to ' + newVolume);
elem.volume = newVolume;
if (newVolume <= 0) {
instance._isFadingOut = false;
return Promise.resolve();
}
return new Promise(function (resolve, reject) {
cancelFadeTimeout();
fadeTimeout = setTimeout(function () {
fade(instance, elem, newVolume).then(resolve, reject);
}, 100);
});
}
function cancelFadeTimeout() {
var timeout = fadeTimeout;
if (timeout) {
clearTimeout(timeout);
fadeTimeout = null;
}
}
function supportsFade() {
if (browser.tv) {
// Not working on tizen.
// We could possibly enable on other tv's, but all smart tv browsers tend to be pretty primitive
return false;
}
return true;
}
function requireHlsPlayer(callback) {
require(['hlsjs'], function (hls) {
window.Hls = hls;
callback();
});
}
function enableHlsPlayer(url, item, mediaSource, mediaType) {
if (!htmlMediaHelper.enableHlsJsPlayer(mediaSource.RunTimeTicks, mediaType)) {
return Promise.reject();
}
if (url.indexOf('.m3u8') !== -1) {
return Promise.resolve();
}
// issue head request to get content type
return new Promise(function (resolve, reject) {
require(['fetchHelper'], function (fetchHelper) {
fetchHelper.ajax({
url: url,
type: 'HEAD'
}).then(function (response) {
var contentType = (response.headers.get('Content-Type') || '').toLowerCase();
if (contentType === 'application/x-mpegurl') {
resolve();
} else {
reject();
}
}, reject);
});
});
}
function HtmlAudioPlayer() {
var self = this;
self.name = 'Html Audio Player';
self.type = 'mediaplayer';
self.id = 'htmlaudioplayer';
// Let any players created by plugins take priority
self.priority = 1;
self.play = function (options) {
self._started = false;
self._timeUpdated = false;
self._currentTime = null;
var elem = createMediaElement();
return setCurrentSrc(elem, options);
};
function setCurrentSrc(elem, options) {
elem.removeEventListener('error', onError);
unBindEvents(elem);
bindEvents(elem);
var val = options.url;
console.debug('playing url: ' + val);
// Convert to seconds
var seconds = (options.playerStartPositionTicks || 0) / 10000000;
if (seconds) {
val += '#t=' + seconds;
}
htmlMediaHelper.destroyHlsPlayer(self);
self._currentPlayOptions = options;
var crossOrigin = htmlMediaHelper.getCrossOriginValue(options.mediaSource);
if (crossOrigin) {
elem.crossOrigin = crossOrigin;
}
return enableHlsPlayer(val, options.item, options.mediaSource, 'Audio').then(function () {
return new Promise(function (resolve, reject) {
requireHlsPlayer(function () {
var hls = new Hls({
manifestLoadingTimeOut: 20000,
xhrSetup: function(xhr, url) {
xhr.withCredentials = true;
}
//appendErrorMaxRetry: 6,
//debug: true
});
hls.loadSource(val);
hls.attachMedia(elem);
htmlMediaHelper.bindEventsToHlsPlayer(self, hls, elem, onError, resolve, reject);
self._hlsPlayer = hls;
self._currentSrc = val;
});
});
}, function () {
elem.autoplay = true;
// Safari will not send cookies without this
elem.crossOrigin = 'use-credentials';
return htmlMediaHelper.applySrc(elem, val, options).then(function () {
self._currentSrc = val;
return htmlMediaHelper.playWithPromise(elem, onError);
});
});
}
function bindEvents(elem) {
elem.addEventListener('timeupdate', onTimeUpdate);
elem.addEventListener('ended', onEnded);
elem.addEventListener('volumechange', onVolumeChange);
elem.addEventListener('pause', onPause);
elem.addEventListener('playing', onPlaying);
elem.addEventListener('play', onPlay);
elem.addEventListener('waiting', onWaiting);
}
function unBindEvents(elem) {
elem.removeEventListener('timeupdate', onTimeUpdate);
elem.removeEventListener('ended', onEnded);
elem.removeEventListener('volumechange', onVolumeChange);
elem.removeEventListener('pause', onPause);
elem.removeEventListener('playing', onPlaying);
elem.removeEventListener('play', onPlay);
elem.removeEventListener('waiting', onWaiting);
}
self.stop = function (destroyPlayer) {
cancelFadeTimeout();
var elem = self._mediaElement;
var src = self._currentSrc;
if (elem && src) {
if (!destroyPlayer || !supportsFade()) {
elem.pause();
htmlMediaHelper.onEndedInternal(self, elem, onError);
if (destroyPlayer) {
self.destroy();
}
return Promise.resolve();
}
var originalVolume = elem.volume;
return fade(self, elem, elem.volume).then(function () {
elem.pause();
elem.volume = originalVolume;
htmlMediaHelper.onEndedInternal(self, elem, onError);
if (destroyPlayer) {
self.destroy();
}
});
}
return Promise.resolve();
};
self.destroy = function () {
unBindEvents(self._mediaElement);
};
function createMediaElement() {
var elem = self._mediaElement;
if (elem) {
return elem;
}
elem = document.querySelector('.mediaPlayerAudio');
if (!elem) {
elem = document.createElement('audio');
elem.classList.add('mediaPlayerAudio');
elem.classList.add('hide');
document.body.appendChild(elem);
}
elem.volume = htmlMediaHelper.getSavedVolume();
self._mediaElement = elem;
return elem;
}
function onEnded() {
htmlMediaHelper.onEndedInternal(self, this, onError);
}
function onTimeUpdate() {
// Get the player position + the transcoding offset
var time = this.currentTime;
// Don't trigger events after user stop
if (!self._isFadingOut) {
self._currentTime = time;
events.trigger(self, 'timeupdate');
}
}
function onVolumeChange() {
if (!self._isFadingOut) {
htmlMediaHelper.saveVolume(this.volume);
events.trigger(self, 'volumechange');
}
}
function onPlaying(e) {
if (!self._started) {
self._started = true;
this.removeAttribute('controls');
htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks);
}
events.trigger(self, 'playing');
}
function onPlay(e) {
events.trigger(self, 'unpause');
}
function onPause() {
events.trigger(self, 'pause');
}
function onWaiting() {
events.trigger(self, 'waiting');
}
function onError() {
var errorCode = this.error ? (this.error.code || 0) : 0;
var errorMessage = this.error ? (this.error.message || '') : '';
console.error('media element error: ' + errorCode.toString() + ' ' + errorMessage);
var type;
switch (errorCode) {
case 1:
// MEDIA_ERR_ABORTED
// This will trigger when changing media while something is playing
return;
case 2:
// MEDIA_ERR_NETWORK
type = 'network';
break;
case 3:
// MEDIA_ERR_DECODE
if (self._hlsPlayer) {
htmlMediaHelper.handleHlsJsMediaError(self);
return;
} else {
type = 'mediadecodeerror';
}
break;
case 4:
// MEDIA_ERR_SRC_NOT_SUPPORTED
type = 'medianotsupported';
break;
default:
// seeing cases where Edge is firing error events with no error code
// example is start playing something, then immediately change src to something else
return;
}
htmlMediaHelper.onErrorInternal(self, type);
}
}
HtmlAudioPlayer.prototype.currentSrc = function () {
return this._currentSrc;
};
HtmlAudioPlayer.prototype.canPlayMediaType = function (mediaType) {
return (mediaType || '').toLowerCase() === 'audio';
};
HtmlAudioPlayer.prototype.getDeviceProfile = function (item) {
if (appHost.getDeviceProfile) {
return appHost.getDeviceProfile(item);
}
return getDefaultProfile();
};
// Save this for when playback stops, because querying the time at that point might return 0
HtmlAudioPlayer.prototype.currentTime = function (val) {
var mediaElement = this._mediaElement;
if (mediaElement) {
if (val != null) {
mediaElement.currentTime = val / 1000;
return;
}
var currentTime = this._currentTime;
if (currentTime) {
return currentTime * 1000;
}
return (mediaElement.currentTime || 0) * 1000;
}
};
HtmlAudioPlayer.prototype.duration = function (val) {
var mediaElement = this._mediaElement;
if (mediaElement) {
var duration = mediaElement.duration;
if (htmlMediaHelper.isValidDuration(duration)) {
return duration * 1000;
}
}
return null;
};
HtmlAudioPlayer.prototype.seekable = function () {
var mediaElement = this._mediaElement;
if (mediaElement) {
var seekable = mediaElement.seekable;
if (seekable && seekable.length) {
var start = seekable.start(0);
var end = seekable.end(0);
if (!htmlMediaHelper.isValidDuration(start)) {
start = 0;
}
if (!htmlMediaHelper.isValidDuration(end)) {
end = 0;
}
return (end - start) > 0;
}
return false;
}
};
HtmlAudioPlayer.prototype.getBufferedRanges = function () {
var mediaElement = this._mediaElement;
if (mediaElement) {
return htmlMediaHelper.getBufferedRanges(this, mediaElement);
}
return [];
};
HtmlAudioPlayer.prototype.pause = function () {
var mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.pause();
}
};
// This is a retry after error
HtmlAudioPlayer.prototype.resume = function () {
var mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.play();
}
};
HtmlAudioPlayer.prototype.unpause = function () {
var mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.play();
}
};
HtmlAudioPlayer.prototype.paused = function () {
var mediaElement = this._mediaElement;
if (mediaElement) {
return mediaElement.paused;
}
return false;
};
HtmlAudioPlayer.prototype.setPlaybackRate = function (value) {
var mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.playbackRate = value;
}
};
HtmlAudioPlayer.prototype.getPlaybackRate = function () {
var mediaElement = this._mediaElement;
if (mediaElement) {
return mediaElement.playbackRate;
}
return null;
};
HtmlAudioPlayer.prototype.setVolume = function (val) {
var mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.volume = val / 100;
}
};
HtmlAudioPlayer.prototype.getVolume = function () {
var mediaElement = this._mediaElement;
if (mediaElement) {
return Math.min(Math.round(mediaElement.volume * 100), 100);
}
};
HtmlAudioPlayer.prototype.volumeUp = function () {
this.setVolume(Math.min(this.getVolume() + 2, 100));
};
HtmlAudioPlayer.prototype.volumeDown = function () {
this.setVolume(Math.max(this.getVolume() - 2, 0));
};
HtmlAudioPlayer.prototype.setMute = function (mute) {
var mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.muted = mute;
}
};
HtmlAudioPlayer.prototype.isMuted = function () {
var mediaElement = this._mediaElement;
if (mediaElement) {
return mediaElement.muted;
}
return false;
};
HtmlAudioPlayer.prototype.destroy = function () {
};
var supportedFeatures;
function getSupportedFeatures() {
var list = [];
var audio = document.createElement('audio');
if (typeof audio.playbackRate === 'number') {
list.push('PlaybackRate');
}
return list;
}
HtmlAudioPlayer.prototype.supports = function (feature) {
if (!supportedFeatures) {
supportedFeatures = getSupportedFeatures();
}
return supportedFeatures.indexOf(feature) !== -1;
};
return HtmlAudioPlayer;
});

File diff suppressed because it is too large Load diff

View file

@ -1,71 +0,0 @@
.videoPlayerContainer {
position: fixed !important;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
background: #000 !important;
}
.videoPlayerContainer-onTop {
z-index: 1000;
}
.videoPlayerContainer .libassjs-canvas-parent {
order: -1;
}
video::-webkit-media-controls {
display: none !important;
}
.htmlvideoplayer {
margin: 0 !important;
padding: 0 !important;
width: 100%;
height: 100%;
}
.htmlvideoplayer::cue {
background-color: transparent;
text-shadow: 0.14em 0.14em 0.14em rgba(0, 0, 0, 1);
-webkit-font-smoothing: antialiased;
font-family: inherit;
}
.htmlvideoplayer-moveupsubtitles::-webkit-media-text-track-display {
/* style the text itself */
margin-top: -2em;
}
.videoSubtitles {
position: fixed;
bottom: 10%;
text-align: center;
left: 0;
right: 0;
color: #fff;
font-size: 170%;
}
.videoSubtitlesInner {
max-width: 70%;
background-color: rgba(0, 0, 0, 0.8);
padding: 0.25em;
margin: auto;
display: inline-block;
}
@keyframes htmlvideoplayer-zoomin {
from {
transform: scale3d(0.2, 0.2, 0.2);
opacity: 0.6;
}
to {
transform: none;
opacity: initial;
}
}

View file

@ -203,9 +203,9 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image
html += '<div class="cardContent">';
if (layoutManager.tv || !appHost.supports('externallinks')) {
html += '<div class="cardImageContainer lazy" data-src="' + getDisplayUrl(image.Url, apiClient) + '" style="background-position:center bottom;"></div>';
html += '<div class="cardImageContainer lazy" data-src="' + getDisplayUrl(image.Url, apiClient) + '" style="background-position:center center;background-size:contain;"></div>';
} else {
html += '<a is="emby-linkbutton" target="_blank" href="' + getDisplayUrl(image.Url, apiClient) + '" class="button-link cardImageContainer lazy" data-src="' + getDisplayUrl(image.Url, apiClient) + '" style="background-position:center bottom;"></a>';
html += '<a is="emby-linkbutton" target="_blank" href="' + getDisplayUrl(image.Url, apiClient) + '" class="button-link cardImageContainer lazy" data-src="' + getDisplayUrl(image.Url, apiClient) + '" style="background-position:center center;background-size:contain"></a>';
}
html += '</div>';

View file

@ -132,7 +132,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager',
var imageUrl = getImageUrl(currentItem, apiClient, image.ImageType, image.ImageIndex, { maxWidth: imageSize });
html += '<div class="cardImageContainer" style="background-image:url(\'' + imageUrl + '\');background-position:center bottom;"></div>';
html += '<div class="cardImageContainer" style="background-image:url(\'' + imageUrl + '\');background-position:center center;background-size:contain;"></div>';
html += '</div>';
html += '</div>';

View file

@ -1,5 +1,6 @@
import * as lazyLoader from 'lazyLoader';
import * as userSettings from 'userSettings';
import * as blurhash from 'blurhash';
import 'css!./style';
/* eslint-disable indent */
@ -11,47 +12,111 @@ import 'css!./style';
fillImageElement(elem, source);
}
async function itemBlurhashing(target, blurhashstr) {
if (blurhash.isBlurhashValid(blurhashstr)) {
// Although the default values recommended by Blurhash developers is 32x32, a size of 18x18 seems to be the sweet spot for us,
// improving the performance and reducing the memory usage, while retaining almost full blur quality.
// Lower values had more visible pixelation
let width = 18;
let height = 18;
let pixels;
try {
pixels = blurhash.decode(blurhashstr, width, height);
} catch (err) {
console.error('Blurhash decode error: ', err);
target.classList.add('non-blurhashable');
return;
}
let canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext('2d');
let imgData = ctx.createImageData(width, height);
imgData.data.set(pixels);
ctx.putImageData(imgData, 0, 0);
let child = target.appendChild(canvas);
child.classList.add('blurhash-canvas');
child.style.opacity = 1;
if (userSettings.enableFastFadein()) {
child.classList.add('lazy-blurhash-fadein-fast');
} else {
child.classList.add('lazy-blurhash-fadein');
}
target.classList.add('blurhashed');
target.removeAttribute('data-blurhash');
}
}
function switchCanvas(elem) {
let child = elem.getElementsByClassName('blurhash-canvas')[0];
if (child) {
child.style.opacity = elem.getAttribute('data-src') ? 1 : 0;
}
}
export function fillImage(entry) {
if (!entry) {
throw new Error('entry cannot be null');
}
let target = entry.target;
var source = undefined;
if (entry.target) {
source = entry.target.getAttribute('data-src');
if (target) {
source = target.getAttribute('data-src');
var blurhashstr = target.getAttribute('data-blurhash');
} else {
source = entry;
}
if (userSettings.enableBlurhash()) {
if (!target.classList.contains('blurhashed', 'non-blurhashable') && blurhashstr) {
itemBlurhashing(target, blurhashstr);
} else if (!blurhashstr && !target.classList.contains('blurhashed')) {
target.classList.add('non-blurhashable');
}
}
if (entry.intersectionRatio > 0) {
if (source) fillImageElement(entry.target, source);
if (source) fillImageElement(target, source);
} else if (!source) {
emptyImageElement(entry.target);
emptyImageElement(target);
}
}
function fillImageElement(elem, url) {
if (url === undefined) {
throw new Error('url cannot be undefined');
throw new TypeError('url cannot be undefined');
}
let preloaderImg = new Image();
preloaderImg.src = url;
// This is necessary here, so changing blurhash settings without reloading the page works
if (!userSettings.enableBlurhash() || elem.classList.contains('non-blurhashable')) {
elem.classList.add('lazy-hidden');
}
preloaderImg.addEventListener('load', () => {
if (elem.tagName !== 'IMG') {
elem.style.backgroundImage = "url('" + url + "')";
} else {
elem.setAttribute('src', url);
}
if (userSettings.enableFastFadein()) {
elem.classList.add('lazy-image-fadein-fast');
} else {
elem.classList.add('lazy-image-fadein');
}
elem.removeAttribute('data-src');
if (elem.classList.contains('non-blurhashable') || !userSettings.enableBlurhash()) {
elem.classList.remove('lazy-hidden');
if (userSettings.enableFastFadein()) {
elem.classList.add('lazy-image-fadein-fast');
} else {
elem.classList.add('lazy-image-fadein');
}
} else {
switchCanvas(elem);
}
});
}
@ -65,11 +130,14 @@ import 'css!./style';
url = elem.getAttribute('src');
elem.setAttribute('src', '');
}
elem.setAttribute('data-src', url);
elem.classList.remove('lazy-image-fadein-fast');
elem.classList.remove('lazy-image-fadein');
if (elem.classList.contains('non-blurhashable') || !userSettings.enableBlurhash()) {
elem.classList.remove('lazy-image-fadein-fast', 'lazy-image-fadein');
elem.classList.add('lazy-hidden');
} else {
switchCanvas(elem);
}
}
export function lazyChildren(elem) {

View file

@ -1,13 +1,32 @@
.cardImageContainer.lazy {
opacity: 0;
}
.cardImageContainer.lazy.lazy-image-fadein {
.lazy-image-fadein {
opacity: 1;
transition: opacity 0.7s;
}
.cardImageContainer.lazy.lazy-image-fadein-fast {
.lazy-image-fadein-fast {
opacity: 1;
transition: opacity 0.2s;
}
.lazy-hidden {
opacity: 0;
}
.lazy-blurhash-fadein-fast {
transition: opacity 0.2s;
}
.lazy-blurhash-fadein {
transition: opacity 0.7s;
}
.blurhash-canvas {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100;
}

View file

@ -120,7 +120,12 @@ define(['globalize', 'dom', 'emby-checkbox', 'emby-select', 'emby-input'], funct
html += plugin.Name;
html += '</h3>';
html += '</div>';
index > 0 ? html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonUp') + '" class="btnSortableMoveUp btnSortable" data-pluginindex="' + index + '"><span class="material-icons keyboard_arrow_up"></span></button>' : plugins.length > 1 && (html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonDown') + '" class="btnSortableMoveDown btnSortable" data-pluginindex="' + index + '"><span class="material-icons keyboard_arrow_down"></span></button>'), html += '</div>';
if (index > 0) {
html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonUp') + '" class="btnSortableMoveUp btnSortable" data-pluginindex="' + index + '"><span class="material-icons keyboard_arrow_up"></span></button>';
} else if (plugins.length > 1) {
html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonDown') + '" class="btnSortableMoveDown btnSortable" data-pluginindex="' + index + '"><span class="material-icons keyboard_arrow_down"></span></button>';
}
html += '</div>';
});
html += '</div>';

View file

@ -79,7 +79,7 @@
<div class="checkboxContainer checkboxContainer-withDescription chkAutomaticallyGroupSeriesContainer hide advanced">
<label>
<input type="checkbox" is="emby-checkbox" class="chkAutomaticallyGroupSeries" checked />
<input type="checkbox" is="emby-checkbox" class="chkAutomaticallyGroupSeries" />
<span>${OptionAutomaticallyGroupSeries}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${OptionAutomaticallyGroupSeriesHelp}</div>

View file

@ -70,6 +70,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
function getImageUrl(item, width) {
var apiClient = connectionManager.getApiClient(item.ServerId);
let itemId;
var options = {
maxWidth: width * 2,
@ -77,45 +78,45 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
};
if (item.ImageTags && item.ImageTags.Primary) {
options.tag = item.ImageTags.Primary;
return apiClient.getScaledImageUrl(item.Id, options);
itemId = item.Id;
}
if (item.AlbumId && item.AlbumPrimaryImageTag) {
options.tag = item.AlbumPrimaryImageTag;
return apiClient.getScaledImageUrl(item.AlbumId, options);
itemId = item.AlbumId;
} else if (item.SeriesId && item.SeriesPrimaryImageTag) {
options.tag = item.SeriesPrimaryImageTag;
return apiClient.getScaledImageUrl(item.SeriesId, options);
itemId = item.SeriesId;
} else if (item.ParentPrimaryImageTag) {
options.tag = item.ParentPrimaryImageTag;
return apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, options);
itemId = item.ParentPrimaryImageItemId;
}
let blurHashes = item.ImageBlurHashes || {};
let blurhashstr = (blurHashes[options.type] || {})[options.tag];
return null;
if (itemId) {
return { url: apiClient.getScaledImageUrl(itemId, options), blurhash: blurhashstr };
}
}
function getChannelImageUrl(item, width) {
var apiClient = connectionManager.getApiClient(item.ServerId);
var options = {
maxWidth: width * 2,
type: 'Primary'
};
if (item.ChannelId && item.ChannelPrimaryImageTag) {
options.tag = item.ChannelPrimaryImageTag;
return apiClient.getScaledImageUrl(item.ChannelId, options);
}
let blurHashes = item.ImageBlurHashes || {};
let blurhashstr = (blurHashes[options.type])[options.tag];
return null;
if (item.ChannelId) {
return { url: apiClient.getScaledImageUrl(item.ChannelId, options), blurhash: blurhashstr };
}
}
function getTextLinesHtml(textlines, isLargeStyle) {
@ -268,8 +269,10 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
}
if (options.image !== false) {
var imgUrl = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth);
var imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage';
let imgData = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth);
let imgUrl = imgData.url;
let blurhash = imgData.blurhash;
let imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage';
if (isLargeStyle && layoutManager.tv) {
imageClass += ' listItemImage-large-tv';
@ -283,8 +286,13 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
var imageAction = playOnImageClick ? 'resume' : action;
let blurhashAttrib = '';
if (blurhash && blurhash.length > 0) {
blurhashAttrib = 'data-blurhash="' + blurhash + '"';
}
if (imgUrl) {
html += '<div data-action="' + imageAction + '" class="' + imageClass + ' lazy" data-src="' + imgUrl + '" item-icon>';
html += '<div data-action="' + imageAction + '" class="' + imageClass + ' lazy" data-src="' + imgUrl + '" ' + blurhashAttrib + ' item-icon>';
} else {
html += '<div class="' + imageClass + '">';
}

View file

@ -1,192 +0,0 @@
define(['pluginManager'], function (pluginManager) {
return function () {
var self = this;
self.name = 'Logo ScreenSaver';
self.type = 'screensaver';
self.id = 'logoscreensaver';
self.supportsAnonymous = true;
var interval;
function animate() {
var animations = [
bounceInLeft,
bounceInRight,
swing,
tada,
wobble,
rotateIn,
rotateOut
];
var elem = document.querySelector('.logoScreenSaverImage');
if (elem && elem.animate) {
var random = getRandomInt(0, animations.length - 1);
animations[random](elem, 1);
}
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function bounceInLeft(elem, iterations) {
var keyframes = [
{ transform: 'translate3d(-3000px, 0, 0)', opacity: '0', offset: 0 },
{ transform: 'translate3d(25px, 0, 0)', opacity: '1', offset: 0.6 },
{ transform: 'translate3d(-100px, 0, 0)', offset: 0.75 },
{ transform: 'translate3d(5px, 0, 0)', offset: 0.9 },
{ transform: 'none', opacity: '1', offset: 1 }];
var timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' };
return elem.animate(keyframes, timing);
}
function bounceInRight(elem, iterations) {
var keyframes = [
{ transform: 'translate3d(3000px, 0, 0)', opacity: '0', offset: 0 },
{ transform: 'translate3d(-25px, 0, 0)', opacity: '1', offset: 0.6 },
{ transform: 'translate3d(100px, 0, 0)', offset: 0.75 },
{ transform: 'translate3d(-5px, 0, 0)', offset: 0.9 },
{ transform: 'none', opacity: '1', offset: 1 }];
var timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' };
return elem.animate(keyframes, timing);
}
function shake(elem, iterations) {
var keyframes = [
{ transform: 'translate3d(0, 0, 0)', offset: 0 },
{ transform: 'translate3d(-10px, 0, 0)', offset: 0.1 },
{ transform: 'translate3d(10px, 0, 0)', offset: 0.2 },
{ transform: 'translate3d(-10px, 0, 0)', offset: 0.3 },
{ transform: 'translate3d(10px, 0, 0)', offset: 0.4 },
{ transform: 'translate3d(-10px, 0, 0)', offset: 0.5 },
{ transform: 'translate3d(10px, 0, 0)', offset: 0.6 },
{ transform: 'translate3d(-10px, 0, 0)', offset: 0.7 },
{ transform: 'translate3d(10px, 0, 0)', offset: 0.8 },
{ transform: 'translate3d(-10px, 0, 0)', offset: 0.9 },
{ transform: 'translate3d(0, 0, 0)', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function swing(elem, iterations) {
var keyframes = [
{ transform: 'translate(0%)', offset: 0 },
{ transform: 'rotate3d(0, 0, 1, 15deg)', offset: 0.2 },
{ transform: 'rotate3d(0, 0, 1, -10deg)', offset: 0.4 },
{ transform: 'rotate3d(0, 0, 1, 5deg)', offset: 0.6 },
{ transform: 'rotate3d(0, 0, 1, -5deg)', offset: 0.8 },
{ transform: 'rotate3d(0, 0, 1, 0deg)', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function tada(elem, iterations) {
var keyframes = [
{ transform: 'scale3d(1, 1, 1)', offset: 0 },
{ transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.1 },
{ transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.2 },
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.3 },
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.4 },
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.5 },
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.6 },
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.7 },
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.8 },
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.9 },
{ transform: 'scale3d(1, 1, 1)', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function wobble(elem, iterations) {
var keyframes = [
{ transform: 'translate(0%)', offset: 0 },
{ transform: 'translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)', offset: 0.15 },
{ transform: 'translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)', offset: 0.45 },
{ transform: 'translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)', offset: 0.6 },
{ transform: 'translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)', offset: 0.75 },
{ transform: 'translateX(0%)', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function rotateIn(elem, iterations) {
var transformOrigin = elem.style['transform-origin'];
var keyframes = [{ transform: 'rotate3d(0, 0, 1, -200deg)', opacity: '0', transformOrigin: 'center', offset: 0 },
{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function rotateOut(elem, iterations) {
var transformOrigin = elem.style['transform-origin'];
var keyframes = [{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 0 },
{ transform: 'rotate3d(0, 0, 1, 200deg)', opacity: '0', transformOrigin: 'center', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function fadeOut(elem, iterations) {
var keyframes = [
{ opacity: '1', offset: 0 },
{ opacity: '0', offset: 1 }];
var timing = { duration: 400, iterations: iterations };
return elem.animate(keyframes, timing);
}
function stopInterval() {
if (interval) {
clearInterval(interval);
interval = null;
}
}
self.show = function () {
require(['css!' + pluginManager.mapPath(self, 'style.css')], function () {
var elem = document.querySelector('.logoScreenSaver');
if (!elem) {
elem = document.createElement('div');
elem.classList.add('logoScreenSaver');
document.body.appendChild(elem);
elem.innerHTML = '<img class="logoScreenSaverImage" src="assets/img/banner-light.png" />';
}
stopInterval();
interval = setInterval(animate, 3000);
});
};
self.hide = function () {
stopInterval();
var elem = document.querySelector('.logoScreenSaver');
if (elem) {
var onAnimationFinish = function () {
elem.parentNode.removeChild(elem);
};
if (elem.animate) {
var animation = fadeOut(elem, 1);
animation.onfinish = onAnimationFinish;
} else {
onAnimationFinish();
}
}
};
};
});

View file

@ -1,18 +0,0 @@
.logoScreenSaver {
background: #101010;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
text-align: center;
}
.logoScreenSaverImage {
height: 120px;
position: absolute;
top: 50%;
margin-top: -60px;
margin-left: -197px;
}

View file

@ -273,7 +273,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater
}
}
if (item.RunTimeTicks && item.Type !== 'Series' && item.Type !== 'Program' && !showFolderRuntime && options.runtime !== false) {
if (item.RunTimeTicks && item.Type !== 'Series' && item.Type !== 'Program' && item.Type !== 'Book' && !showFolderRuntime && options.runtime !== false) {
if (item.Type === 'Audio') {

View file

@ -46,7 +46,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir
function showNonPersistentNotification(title, options, timeoutMs) {
try {
var notif = new Notification(title, options);
var notif = new Notification(title, options); /* eslint-disable-line compat/compat */
if (notif.show) {
notif.show();

View file

@ -1,52 +0,0 @@
define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackManager', 'appRouter', 'appSettings', 'connectionManager'], function (browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager) {
'use strict';
function PhotoPlayer() {
var self = this;
self.name = 'Photo Player';
self.type = 'mediaplayer';
self.id = 'photoplayer';
// Let any players created by plugins take priority
self.priority = 1;
}
PhotoPlayer.prototype.play = function (options) {
return new Promise(function (resolve, reject) {
require(['slideshow'], function (slideshow) {
var index = options.startIndex || 0;
var apiClient = connectionManager.currentApiClient();
apiClient.getCurrentUser().then(function(result) {
var newSlideShow = new slideshow({
showTitle: false,
cover: false,
items: options.items,
startIndex: index,
interval: 11000,
interactive: true,
user: result
});
newSlideShow.show();
resolve();
});
});
});
};
PhotoPlayer.prototype.canPlayMediaType = function (mediaType) {
return (mediaType || '').toLowerCase() === 'photo';
};
return PhotoPlayer;
});

View file

@ -1,171 +1,171 @@
define(['events', 'playbackManager', 'dom', 'browser', 'css!./iconosd', 'material-icons'], function (events, playbackManager, dom, browser) {
'use strict';
import events from 'events';
import playbackManager from 'playbackManager';
import dom from 'dom';
import browser from 'browser';
import 'css!./iconosd';
import 'material-icons';
var currentPlayer;
var osdElement;
var iconElement;
var progressElement;
var currentPlayer;
var osdElement;
var iconElement;
var progressElement;
var enableAnimation;
var enableAnimation;
function getOsdElementHtml() {
var html = '';
function getOsdElementHtml() {
var html = '';
html += '<span class="material-icons iconOsdIcon brightness_high"></span>';
html += '<span class="material-icons iconOsdIcon brightness_high"></span>';
html += '<div class="iconOsdProgressOuter"><div class="iconOsdProgressInner brightnessOsdProgressInner"></div></div>';
html += '<div class="iconOsdProgressOuter"><div class="iconOsdProgressInner brightnessOsdProgressInner"></div></div>';
return html;
return html;
}
function ensureOsdElement() {
var elem = osdElement;
if (!elem) {
enableAnimation = browser.supportsCssAnimation();
elem = document.createElement('div');
elem.classList.add('hide');
elem.classList.add('iconOsd');
elem.classList.add('iconOsd-hidden');
elem.classList.add('brightnessOsd');
elem.innerHTML = getOsdElementHtml();
iconElement = elem.querySelector('.material-icons');
progressElement = elem.querySelector('.iconOsdProgressInner');
document.body.appendChild(elem);
osdElement = elem;
}
}
function ensureOsdElement() {
function onHideComplete() {
this.classList.add('hide');
}
var elem = osdElement;
if (!elem) {
var hideTimeout;
function showOsd() {
enableAnimation = browser.supportsCssAnimation();
clearHideTimeout();
elem = document.createElement('div');
elem.classList.add('hide');
elem.classList.add('iconOsd');
elem.classList.add('iconOsd-hidden');
elem.classList.add('brightnessOsd');
elem.innerHTML = getOsdElementHtml();
var elem = osdElement;
iconElement = elem.querySelector('.material-icons');
progressElement = elem.querySelector('.iconOsdProgressInner');
document.body.appendChild(elem);
osdElement = elem;
}
}
function onHideComplete() {
this.classList.add('hide');
}
var hideTimeout;
function showOsd() {
clearHideTimeout();
var elem = osdElement;
dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
});
elem.classList.remove('hide');
// trigger reflow
void elem.offsetWidth;
requestAnimationFrame(function () {
elem.classList.remove('iconOsd-hidden');
hideTimeout = setTimeout(hideOsd, 3000);
});
}
function clearHideTimeout() {
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
}
function hideOsd() {
clearHideTimeout();
var elem = osdElement;
if (elem) {
if (enableAnimation) {
// trigger reflow
void elem.offsetWidth;
requestAnimationFrame(function () {
elem.classList.add('iconOsd-hidden');
dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
});
});
} else {
onHideComplete.call(elem);
}
}
}
function setIcon(iconElement, icon) {
iconElement.classList.remove('brightness_high');
iconElement.classList.remove('brightness_medium');
iconElement.classList.remove('brightness_low');
iconElement.classList.add(icon);
}
function updateElementsFromPlayer(brightness) {
if (iconElement) {
if (brightness >= 80) {
setIcon(iconElement, 'brightness_high');
} else if (brightness >= 20) {
setIcon(iconElement, 'brightness_medium');
} else {
setIcon(iconElement, 'brightness_low');
}
}
if (progressElement) {
progressElement.style.width = (brightness || 0) + '%';
}
}
function releaseCurrentPlayer() {
var player = currentPlayer;
if (player) {
events.off(player, 'brightnesschange', onBrightnessChanged);
events.off(player, 'playbackstop', hideOsd);
currentPlayer = null;
}
}
function onBrightnessChanged(e) {
var player = this;
ensureOsdElement();
updateElementsFromPlayer(playbackManager.getBrightness(player));
showOsd();
}
function bindToPlayer(player) {
if (player === currentPlayer) {
return;
}
releaseCurrentPlayer();
currentPlayer = player;
if (!player) {
return;
}
hideOsd();
events.on(player, 'brightnesschange', onBrightnessChanged);
events.on(player, 'playbackstop', hideOsd);
}
events.on(playbackManager, 'playerchange', function () {
bindToPlayer(playbackManager.getCurrentPlayer());
dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
});
bindToPlayer(playbackManager.getCurrentPlayer());
elem.classList.remove('hide');
// trigger reflow
void elem.offsetWidth;
requestAnimationFrame(function () {
elem.classList.remove('iconOsd-hidden');
hideTimeout = setTimeout(hideOsd, 3000);
});
}
function clearHideTimeout() {
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
}
function hideOsd() {
clearHideTimeout();
var elem = osdElement;
if (elem) {
if (enableAnimation) {
// trigger reflow
void elem.offsetWidth;
requestAnimationFrame(function () {
elem.classList.add('iconOsd-hidden');
dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
});
});
} else {
onHideComplete.call(elem);
}
}
}
function setIcon(iconElement, icon) {
iconElement.classList.remove('brightness_high', 'brightness_medium', 'brightness_low');
iconElement.classList.add(icon);
}
function updateElementsFromPlayer(brightness) {
if (iconElement) {
if (brightness >= 80) {
setIcon(iconElement, 'brightness_high');
} else if (brightness >= 20) {
setIcon(iconElement, 'brightness_medium');
} else {
setIcon(iconElement, 'brightness_low');
}
}
if (progressElement) {
progressElement.style.width = (brightness || 0) + '%';
}
}
function releaseCurrentPlayer() {
var player = currentPlayer;
if (player) {
events.off(player, 'brightnesschange', onBrightnessChanged);
events.off(player, 'playbackstop', hideOsd);
currentPlayer = null;
}
}
function onBrightnessChanged(e) {
var player = this;
ensureOsdElement();
updateElementsFromPlayer(playbackManager.getBrightness(player));
showOsd();
}
function bindToPlayer(player) {
if (player === currentPlayer) {
return;
}
releaseCurrentPlayer();
currentPlayer = player;
if (!player) {
return;
}
hideOsd();
events.on(player, 'brightnesschange', onBrightnessChanged);
events.on(player, 'playbackstop', hideOsd);
}
events.on(playbackManager, 'playerchange', function () {
bindToPlayer(playbackManager.getCurrentPlayer());
});
bindToPlayer(playbackManager.getCurrentPlayer());

View file

@ -1,88 +0,0 @@
define(['connectionManager', 'globalize', 'userSettings', 'apphost'], function (connectionManager, globalize, userSettings, appHost) {
'use strict';
function getRequirePromise(deps) {
return new Promise(function (resolve, reject) {
require(deps, resolve);
});
}
// https://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php
function getWeek(date) {
var d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
var dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
var yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
}
function showMessage(text, userSettingsKey, appHostFeature) {
if (appHost.supports(appHostFeature)) {
return Promise.resolve();
}
var now = new Date();
userSettingsKey += now.getFullYear() + '-w' + getWeek(now);
if (userSettings.get(userSettingsKey, false) === '1') {
return Promise.resolve();
}
return new Promise(function (resolve, reject) {
userSettings.set(userSettingsKey, '1', false);
require(['alert'], function (alert) {
return alert(text).then(resolve, resolve);
});
});
}
function showBlurayMessage() {
return showMessage(globalize.translate('UnsupportedPlayback'), 'blurayexpirementalinfo', 'nativeblurayplayback');
}
function showDvdMessage() {
return showMessage(globalize.translate('UnsupportedPlayback'), 'dvdexpirementalinfo', 'nativedvdplayback');
}
function showIsoMessage() {
return showMessage(globalize.translate('UnsupportedPlayback'), 'isoexpirementalinfo', 'nativeisoplayback');
}
function ExpirementalPlaybackWarnings() {
this.name = 'Experimental playback warnings';
this.type = 'preplayintercept';
this.id = 'expirementalplaybackwarnings';
}
ExpirementalPlaybackWarnings.prototype.intercept = function (options) {
var item = options.item;
if (!item) {
return Promise.resolve();
}
if (item.VideoType === 'Iso') {
return showIsoMessage();
}
if (item.VideoType === 'BluRay') {
return showBlurayMessage();
}
if (item.VideoType === 'Dvd') {
return showDvdMessage();
}
return Promise.resolve();
};
return ExpirementalPlaybackWarnings;
});

View file

@ -119,6 +119,7 @@ import connectionManager from 'connectionManager';
const canSeek = playState.CanSeek || false;
if ('mediaSession' in navigator) {
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.metadata = new MediaMetadata({
title: title,
artist: artist,
@ -179,6 +180,7 @@ import connectionManager from 'connectionManager';
function hideMediaControls() {
if ('mediaSession' in navigator) {
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.metadata = null;
} else {
window.NativeShell.hideMediaSession();
@ -210,26 +212,32 @@ import connectionManager from 'connectionManager';
}
if ('mediaSession' in navigator) {
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('previoustrack', function () {
execute('previousTrack');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('nexttrack', function () {
execute('nextTrack');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('play', function () {
execute('unpause');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('pause', function () {
execute('pause');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('seekbackward', function () {
execute('rewind');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('seekforward', function () {
execute('fastForward');
});

View file

@ -1,86 +1,82 @@
define([], function () {
'use strict';
export function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) {
function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) {
var topItem = nowPlayingItem;
var bottomItem = null;
var topText = nowPlayingItem.Name;
var topItem = nowPlayingItem;
var bottomItem = null;
var topText = nowPlayingItem.Name;
if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType === 'Audio') {
topItem = {
Id: nowPlayingItem.AlbumId,
Name: nowPlayingItem.Album,
Type: 'MusicAlbum',
IsFolder: true
};
}
if (nowPlayingItem.MediaType === 'Video') {
if (nowPlayingItem.IndexNumber != null) {
topText = nowPlayingItem.IndexNumber + ' - ' + topText;
}
if (nowPlayingItem.ParentIndexNumber != null) {
topText = nowPlayingItem.ParentIndexNumber + '.' + topText;
}
}
var bottomText = '';
if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) {
bottomItem = {
Id: nowPlayingItem.ArtistItems[0].Id,
Name: nowPlayingItem.ArtistItems[0].Name,
Type: 'MusicArtist',
IsFolder: true
};
bottomText = nowPlayingItem.ArtistItems.map(function (a) {
return a.Name;
}).join(', ');
} else if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) {
bottomText = nowPlayingItem.Artists.join(', ');
} else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) {
bottomText = topText;
topText = nowPlayingItem.SeriesName || nowPlayingItem.Album;
bottomItem = topItem;
if (nowPlayingItem.SeriesId) {
topItem = {
Id: nowPlayingItem.SeriesId,
Name: nowPlayingItem.SeriesName,
Type: 'Series',
IsFolder: true
};
} else {
topItem = null;
}
} else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) {
bottomText = nowPlayingItem.ProductionYear;
}
var list = [];
list.push({
text: topText,
item: topItem
});
if (bottomText) {
list.push({
text: bottomText,
item: bottomItem
});
}
return list;
if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType === 'Audio') {
topItem = {
Id: nowPlayingItem.AlbumId,
Name: nowPlayingItem.Album,
Type: 'MusicAlbum',
IsFolder: true
};
}
return {
getNowPlayingNames: getNowPlayingNames
};
});
if (nowPlayingItem.MediaType === 'Video') {
if (nowPlayingItem.IndexNumber != null) {
topText = nowPlayingItem.IndexNumber + ' - ' + topText;
}
if (nowPlayingItem.ParentIndexNumber != null) {
topText = nowPlayingItem.ParentIndexNumber + '.' + topText;
}
}
var bottomText = '';
if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) {
bottomItem = {
Id: nowPlayingItem.ArtistItems[0].Id,
Name: nowPlayingItem.ArtistItems[0].Name,
Type: 'MusicArtist',
IsFolder: true
};
bottomText = nowPlayingItem.ArtistItems.map(function (a) {
return a.Name;
}).join(', ');
} else if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) {
bottomText = nowPlayingItem.Artists.join(', ');
} else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) {
bottomText = topText;
topText = nowPlayingItem.SeriesName || nowPlayingItem.Album;
bottomItem = topItem;
if (nowPlayingItem.SeriesId) {
topItem = {
Id: nowPlayingItem.SeriesId,
Name: nowPlayingItem.SeriesName,
Type: 'Series',
IsFolder: true
};
} else {
topItem = null;
}
} else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) {
bottomText = nowPlayingItem.ProductionYear;
}
var list = [];
list.push({
text: topText,
item: topItem
});
if (bottomText) {
list.push({
text: bottomText,
item: bottomItem
});
}
return list;
}
export default {
getNowPlayingNames: getNowPlayingNames
};

View file

@ -1,50 +0,0 @@
define(['connectionManager', 'globalize'], function (connectionManager, globalize) {
'use strict';
function getRequirePromise(deps) {
return new Promise(function (resolve, reject) {
require(deps, resolve);
});
}
function showErrorMessage() {
return getRequirePromise(['alert']).then(function (alert) {
return alert(globalize.translate('MessagePlayAccessRestricted')).then(function () {
return Promise.reject();
});
});
}
function PlayAccessValidation() {
this.name = 'Playback validation';
this.type = 'preplayintercept';
this.id = 'playaccessvalidation';
this.order = -2;
}
PlayAccessValidation.prototype.intercept = function (options) {
var item = options.item;
if (!item) {
return Promise.resolve();
}
var serverId = item.ServerId;
if (!serverId) {
return Promise.resolve();
}
return connectionManager.getApiClient(serverId).getCurrentUser().then(function (user) {
if (user.Policy.EnableMediaPlayback) {
return Promise.resolve();
}
// reject but don't show an error message
if (!options.fullscreen) {
return Promise.reject();
}
return showErrorMessage();
});
};
return PlayAccessValidation;
});

View file

@ -1129,7 +1129,6 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
}
self.canPlay = function (item) {
var itemType = item.Type;
if (itemType === 'PhotoAlbum' || itemType === 'MusicGenre' || itemType === 'Season' || itemType === 'Series' || itemType === 'BoxSet' || itemType === 'MusicAlbum' || itemType === 'MusicArtist' || itemType === 'Playlist') {
@ -1143,7 +1142,6 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
}
if (itemType === 'Program') {
if (!item.EndDate || !item.StartDate) {
return false;
}
@ -1909,11 +1907,8 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
// Setting this to true may cause some incorrect sorting
Recursive: false,
SortBy: options.shuffle ? 'Random' : 'SortName',
MediaTypes: 'Photo,Video',
Limit: 500
MediaTypes: 'Photo,Video'
}).then(function (result) {
var items = result.Items;
var index = items.map(function (i) {
@ -2187,7 +2182,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
// Only used internally
self.getCurrentTicks = getCurrentTicks;
function playPhotos(items, options, user) {
function playOther(items, options, user) {
var playStartIndex = options.startIndex || 0;
var player = getPlayer(items[playStartIndex], options);
@ -2216,9 +2211,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
return Promise.reject();
}
if (firstItem.MediaType === 'Photo') {
if (firstItem.MediaType === 'Photo' || firstItem.MediaType === 'Book') {
return playPhotos(items, options, user);
return playOther(items, options, user);
}
var apiClient = connectionManager.getApiClient(firstItem.ServerId);

View file

@ -1,57 +1,57 @@
define(['playbackManager', 'layoutManager', 'events'], function (playbackManager, layoutManager, events) {
'use strict';
import playbackManager from 'playbackManager';
import layoutManager from 'layoutManager';
import events from 'events';
var orientationLocked;
var orientationLocked;
function onOrientationChangeSuccess() {
orientationLocked = true;
}
function onOrientationChangeSuccess() {
orientationLocked = true;
}
function onOrientationChangeError(err) {
orientationLocked = false;
console.error('error locking orientation: ' + err);
}
function onOrientationChangeError(err) {
orientationLocked = false;
console.error('error locking orientation: ' + err);
}
events.on(playbackManager, 'playbackstart', function (e, player, state) {
events.on(playbackManager, 'playbackstart', function (e, player, state) {
var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player);
var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player);
if (isLocalVideo && layoutManager.mobile) {
/* eslint-disable-next-line compat/compat */
var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || (screen.orientation && screen.orientation.lock);
if (isLocalVideo && layoutManager.mobile) {
/* eslint-disable-next-line compat/compat */
var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || (screen.orientation && screen.orientation.lock);
if (lockOrientation) {
if (lockOrientation) {
try {
var promise = lockOrientation('landscape');
if (promise.then) {
promise.then(onOrientationChangeSuccess, onOrientationChangeError);
} else {
// returns a boolean
orientationLocked = promise;
}
} catch (err) {
onOrientationChangeError(err);
try {
var promise = lockOrientation('landscape');
if (promise.then) {
promise.then(onOrientationChangeSuccess, onOrientationChangeError);
} else {
// returns a boolean
orientationLocked = promise;
}
} catch (err) {
onOrientationChangeError(err);
}
}
});
events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) {
if (orientationLocked && !playbackStopInfo.nextMediaType) {
/* eslint-disable-next-line compat/compat */
var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || (screen.orientation && screen.orientation.unlock);
if (unlockOrientation) {
try {
unlockOrientation();
} catch (err) {
console.error('error unlocking orientation: ' + err);
}
orientationLocked = false;
}
}
});
}
});
events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) {
if (orientationLocked && !playbackStopInfo.nextMediaType) {
/* eslint-disable-next-line compat/compat */
var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || (screen.orientation && screen.orientation.unlock);
if (unlockOrientation) {
try {
unlockOrientation();
} catch (err) {
console.error('error unlocking orientation: ' + err);
}
orientationLocked = false;
}
}
});

View file

@ -1,320 +1,325 @@
define(['appSettings', 'events', 'browser', 'loading', 'playbackManager', 'appRouter', 'globalize', 'apphost'], function (appSettings, events, browser, loading, playbackManager, appRouter, globalize, appHost) {
'use strict';
import appSettings from 'appSettings';
import events from 'events';
import browser from 'browser';
import loading from 'loading';
import playbackManager from 'playbackManager';
import appRouter from 'appRouter';
import globalize from 'globalize';
import appHost from 'apphost';
function mirrorItem(info, player) {
function mirrorItem(info, player) {
var item = info.item;
var item = info.item;
playbackManager.displayContent({
playbackManager.displayContent({
ItemName: item.Name,
ItemId: item.Id,
ItemType: item.Type,
Context: info.context
}, player);
}
ItemName: item.Name,
ItemId: item.Id,
ItemType: item.Type,
Context: info.context
}, player);
}
function mirrorIfEnabled(info) {
function mirrorIfEnabled(info) {
if (info && playbackManager.enableDisplayMirroring()) {
if (info && playbackManager.enableDisplayMirroring()) {
var getPlayerInfo = playbackManager.getPlayerInfo();
var getPlayerInfo = playbackManager.getPlayerInfo();
if (getPlayerInfo) {
if (!getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
mirrorItem(info, playbackManager.getCurrentPlayer());
}
if (getPlayerInfo) {
if (!getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
mirrorItem(info, playbackManager.getCurrentPlayer());
}
}
}
}
function emptyCallback() {
// avoid console logs about uncaught promises
function emptyCallback() {
// avoid console logs about uncaught promises
}
function getTargetSecondaryText(target) {
if (target.user) {
return target.user.Name;
}
function getTargetSecondaryText(target) {
return null;
}
if (target.user) {
function getIcon(target) {
return target.user.Name;
}
var deviceType = target.deviceType;
return null;
}
function getIcon(target) {
var deviceType = target.deviceType;
if (!deviceType && target.isLocalPlayer) {
if (browser.tv) {
deviceType = 'tv';
} else if (browser.mobile) {
deviceType = 'smartphone';
} else {
deviceType = 'desktop';
}
}
if (!deviceType) {
if (!deviceType && target.isLocalPlayer) {
if (browser.tv) {
deviceType = 'tv';
}
switch (deviceType) {
case 'smartphone':
return 'smartphone';
case 'tablet':
return 'tablet';
case 'tv':
return 'tv';
case 'cast':
return 'cast';
case 'desktop':
return 'computer';
default:
return 'tv';
}
}
function showPlayerSelection(button) {
var currentPlayerInfo = playbackManager.getPlayerInfo();
if (currentPlayerInfo) {
if (!currentPlayerInfo.isLocalPlayer) {
showActivePlayerMenu(currentPlayerInfo);
return;
}
}
var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null;
loading.show();
playbackManager.getTargets().then(function (targets) {
var menuItems = targets.map(function (t) {
var name = t.name;
if (t.appName && t.appName !== t.name) {
name += ' - ' + t.appName;
}
return {
name: name,
id: t.id,
selected: currentPlayerId === t.id,
secondaryText: getTargetSecondaryText(t),
icon: getIcon(t)
};
});
require(['actionsheet'], function (actionsheet) {
loading.hide();
var menuOptions = {
title: globalize.translate('HeaderPlayOn'),
items: menuItems,
positionTo: button,
resolveOnClick: true,
border: true
};
// Unfortunately we can't allow the url to change or chromecast will throw a security error
// Might be able to solve this in the future by moving the dialogs to hashbangs
if (!(!browser.chrome || appHost.supports('castmenuhashchange'))) {
menuOptions.enableHistory = false;
}
actionsheet.show(menuOptions).then(function (id) {
var target = targets.filter(function (t) {
return t.id === id;
})[0];
playbackManager.trySetActivePlayer(target.playerName, target);
mirrorIfEnabled();
}, emptyCallback);
});
});
}
function showActivePlayerMenu(playerInfo) {
require(['dialogHelper', 'dialog', 'emby-checkbox', 'emby-button'], function (dialogHelper) {
showActivePlayerMenuInternal(dialogHelper, playerInfo);
});
}
function disconnectFromPlayer(currentDeviceName) {
if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) {
require(['dialog'], function (dialog) {
var menuItems = [];
menuItems.push({
name: globalize.translate('Yes'),
id: 'yes'
});
menuItems.push({
name: globalize.translate('No'),
id: 'no'
});
dialog({
buttons: menuItems,
//positionTo: positionTo,
text: globalize.translate('ConfirmEndPlayerSession', currentDeviceName)
}).then(function (id) {
switch (id) {
case 'yes':
playbackManager.getCurrentPlayer().endSession();
playbackManager.setDefaultPlayerActive();
break;
case 'no':
playbackManager.setDefaultPlayerActive();
break;
default:
break;
}
});
});
} else if (browser.mobile) {
deviceType = 'smartphone';
} else {
playbackManager.setDefaultPlayerActive();
deviceType = 'desktop';
}
}
function showActivePlayerMenuInternal(dialogHelper, playerInfo) {
var html = '';
var dialogOptions = {
removeOnClose: true
};
dialogOptions.modal = false;
dialogOptions.entryAnimationDuration = 160;
dialogOptions.exitAnimationDuration = 160;
dialogOptions.autoFocus = false;
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('promptDialog');
var currentDeviceName = (playerInfo.deviceName || playerInfo.name);
html += '<div class="promptDialogContent" style="padding:1.5em;">';
html += '<h2 style="margin-top:.5em;">';
html += currentDeviceName;
html += '</h2>';
html += '<div>';
if (playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
html += '<label class="checkboxContainer">';
var checkedHtml = playbackManager.enableDisplayMirroring() ? ' checked' : '';
html += '<input type="checkbox" is="emby-checkbox" class="chkMirror"' + checkedHtml + '/>';
html += '<span>' + globalize.translate('EnableDisplayMirroring') + '</span>';
html += '</label>';
}
html += '</div>';
html += '<div style="margin-top:1em;display:flex;justify-content: flex-end;">';
html += '<button is="emby-button" type="button" class="button-flat btnRemoteControl promptDialogButton">' + globalize.translate('HeaderRemoteControl') + '</button>';
html += '<button is="emby-button" type="button" class="button-flat btnDisconnect promptDialogButton ">' + globalize.translate('Disconnect') + '</button>';
html += '<button is="emby-button" type="button" class="button-flat btnCancel promptDialogButton">' + globalize.translate('ButtonCancel') + '</button>';
html += '</div>';
html += '</div>';
dlg.innerHTML = html;
var chkMirror = dlg.querySelector('.chkMirror');
if (chkMirror) {
chkMirror.addEventListener('change', onMirrorChange);
}
var destination = '';
var btnRemoteControl = dlg.querySelector('.btnRemoteControl');
if (btnRemoteControl) {
btnRemoteControl.addEventListener('click', function () {
destination = 'nowplaying';
dialogHelper.close(dlg);
});
}
dlg.querySelector('.btnDisconnect').addEventListener('click', function () {
destination = 'disconnectFromPlayer';
dialogHelper.close(dlg);
});
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dialogHelper.close(dlg);
});
dialogHelper.open(dlg).then(function () {
if (destination === 'nowplaying') {
appRouter.showNowPlaying();
} else if (destination === 'disconnectFromPlayer') {
disconnectFromPlayer(currentDeviceName);
}
}, emptyCallback);
if (!deviceType) {
deviceType = 'tv';
}
function onMirrorChange() {
playbackManager.enableDisplayMirroring(this.checked);
switch (deviceType) {
case 'smartphone':
return 'smartphone';
case 'tablet':
return 'tablet';
case 'tv':
return 'tv';
case 'cast':
return 'cast';
case 'desktop':
return 'computer';
default:
return 'tv';
}
}
document.addEventListener('viewshow', function (e) {
export function show(button) {
var state = e.detail.state || {};
var item = state.item;
var currentPlayerInfo = playbackManager.getPlayerInfo();
if (item && item.ServerId) {
mirrorIfEnabled({
item: item
});
if (currentPlayerInfo) {
if (!currentPlayerInfo.isLocalPlayer) {
showActivePlayerMenu(currentPlayerInfo);
return;
}
});
}
events.on(appSettings, 'change', function (e, name) {
if (name === 'displaymirror') {
mirrorIfEnabled();
}
});
var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null;
events.on(playbackManager, 'pairing', function (e) {
loading.show();
});
loading.show();
events.on(playbackManager, 'paired', function (e) {
loading.hide();
});
playbackManager.getTargets().then(function (targets) {
events.on(playbackManager, 'pairerror', function (e) {
loading.hide();
});
var menuItems = targets.map(function (t) {
return {
show: showPlayerSelection
var name = t.name;
if (t.appName && t.appName !== t.name) {
name += ' - ' + t.appName;
}
return {
name: name,
id: t.id,
selected: currentPlayerId === t.id,
secondaryText: getTargetSecondaryText(t),
icon: getIcon(t)
};
});
require(['actionsheet'], function (actionsheet) {
loading.hide();
var menuOptions = {
title: globalize.translate('HeaderPlayOn'),
items: menuItems,
positionTo: button,
resolveOnClick: true,
border: true
};
// Unfortunately we can't allow the url to change or chromecast will throw a security error
// Might be able to solve this in the future by moving the dialogs to hashbangs
if (!(!browser.chrome || appHost.supports('castmenuhashchange'))) {
menuOptions.enableHistory = false;
}
actionsheet.show(menuOptions).then(function (id) {
var target = targets.filter(function (t) {
return t.id === id;
})[0];
playbackManager.trySetActivePlayer(target.playerName, target);
mirrorIfEnabled();
}, emptyCallback);
});
});
}
function showActivePlayerMenu(playerInfo) {
require(['dialogHelper', 'dialog', 'emby-checkbox', 'emby-button'], function (dialogHelper) {
showActivePlayerMenuInternal(dialogHelper, playerInfo);
});
}
function disconnectFromPlayer(currentDeviceName) {
if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) {
require(['dialog'], function (dialog) {
var menuItems = [];
menuItems.push({
name: globalize.translate('Yes'),
id: 'yes'
});
menuItems.push({
name: globalize.translate('No'),
id: 'no'
});
dialog({
buttons: menuItems,
//positionTo: positionTo,
text: globalize.translate('ConfirmEndPlayerSession', currentDeviceName)
}).then(function (id) {
switch (id) {
case 'yes':
playbackManager.getCurrentPlayer().endSession();
playbackManager.setDefaultPlayerActive();
break;
case 'no':
playbackManager.setDefaultPlayerActive();
break;
default:
break;
}
});
});
} else {
playbackManager.setDefaultPlayerActive();
}
}
function showActivePlayerMenuInternal(dialogHelper, playerInfo) {
var html = '';
var dialogOptions = {
removeOnClose: true
};
dialogOptions.modal = false;
dialogOptions.entryAnimationDuration = 160;
dialogOptions.exitAnimationDuration = 160;
dialogOptions.autoFocus = false;
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('promptDialog');
var currentDeviceName = (playerInfo.deviceName || playerInfo.name);
html += '<div class="promptDialogContent" style="padding:1.5em;">';
html += '<h2 style="margin-top:.5em;">';
html += currentDeviceName;
html += '</h2>';
html += '<div>';
if (playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
html += '<label class="checkboxContainer">';
var checkedHtml = playbackManager.enableDisplayMirroring() ? ' checked' : '';
html += '<input type="checkbox" is="emby-checkbox" class="chkMirror"' + checkedHtml + '/>';
html += '<span>' + globalize.translate('EnableDisplayMirroring') + '</span>';
html += '</label>';
}
html += '</div>';
html += '<div style="margin-top:1em;display:flex;justify-content: flex-end;">';
html += '<button is="emby-button" type="button" class="button-flat btnRemoteControl promptDialogButton">' + globalize.translate('HeaderRemoteControl') + '</button>';
html += '<button is="emby-button" type="button" class="button-flat btnDisconnect promptDialogButton ">' + globalize.translate('Disconnect') + '</button>';
html += '<button is="emby-button" type="button" class="button-flat btnCancel promptDialogButton">' + globalize.translate('ButtonCancel') + '</button>';
html += '</div>';
html += '</div>';
dlg.innerHTML = html;
var chkMirror = dlg.querySelector('.chkMirror');
if (chkMirror) {
chkMirror.addEventListener('change', onMirrorChange);
}
var destination = '';
var btnRemoteControl = dlg.querySelector('.btnRemoteControl');
if (btnRemoteControl) {
btnRemoteControl.addEventListener('click', function () {
destination = 'nowplaying';
dialogHelper.close(dlg);
});
}
dlg.querySelector('.btnDisconnect').addEventListener('click', function () {
destination = 'disconnectFromPlayer';
dialogHelper.close(dlg);
});
dlg.querySelector('.btnCancel').addEventListener('click', function () {
dialogHelper.close(dlg);
});
dialogHelper.open(dlg).then(function () {
if (destination === 'nowplaying') {
appRouter.showNowPlaying();
} else if (destination === 'disconnectFromPlayer') {
disconnectFromPlayer(currentDeviceName);
}
}, emptyCallback);
}
function onMirrorChange() {
playbackManager.enableDisplayMirroring(this.checked);
}
document.addEventListener('viewshow', function (e) {
var state = e.detail.state || {};
var item = state.item;
if (item && item.ServerId) {
mirrorIfEnabled({
item: item
});
return;
}
});
events.on(appSettings, 'change', function (e, name) {
if (name === 'displaymirror') {
mirrorIfEnabled();
}
});
events.on(playbackManager, 'pairing', function (e) {
loading.show();
});
events.on(playbackManager, 'paired', function (e) {
loading.hide();
});
events.on(playbackManager, 'pairerror', function (e) {
loading.hide();
});
export default {
show: show
};

View file

@ -1,270 +1,272 @@
define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'globalize', 'appSettings', 'qualityoptions'], function (connectionManager, actionsheet, datetime, playbackManager, globalize, appSettings, qualityoptions) {
'use strict';
import connectionManager from 'connectionManager';
import actionsheet from 'actionsheet';
import playbackManager from 'playbackManager';
import globalize from 'globalize';
import qualityoptions from 'qualityoptions';
function showQualityMenu(player, btn) {
function showQualityMenu(player, btn) {
var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
return stream.Type === 'Video';
})[0];
var videoWidth = videoStream ? videoStream.Width : null;
var videoHeight = videoStream ? videoStream.Height : null;
var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
return stream.Type === 'Video';
})[0];
var videoWidth = videoStream ? videoStream.Width : null;
var videoHeight = videoStream ? videoStream.Height : null;
var options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
videoWidth: videoWidth,
videoHeight: videoHeight,
enableAuto: true
});
var options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
videoWidth: videoWidth,
videoHeight: videoHeight,
enableAuto: true
});
var menuItems = options.map(function (o) {
var opt = {
name: o.name,
id: o.bitrate,
asideText: o.secondaryText
};
var menuItems = options.map(function (o) {
var opt = {
name: o.name,
id: o.bitrate,
asideText: o.secondaryText
};
if (o.selected) {
opt.selected = true;
}
if (o.selected) {
opt.selected = true;
}
return opt;
});
return opt;
});
var selectedId = options.filter(function (o) {
return o.selected;
});
var selectedId = options.filter(function (o) {
return o.selected;
});
selectedId = selectedId.length ? selectedId[0].bitrate : null;
selectedId = selectedId.length ? selectedId[0].bitrate : null;
return actionsheet.show({
items: menuItems,
positionTo: btn
}).then(function (id) {
var bitrate = parseInt(id);
if (bitrate !== selectedId) {
playbackManager.setMaxStreamingBitrate({
enableAutomaticBitrateDetection: bitrate ? false : true,
maxBitrate: bitrate
}, player);
}
});
return actionsheet.show({
items: menuItems,
positionTo: btn
}).then(function (id) {
var bitrate = parseInt(id);
if (bitrate !== selectedId) {
playbackManager.setMaxStreamingBitrate({
enableAutomaticBitrateDetection: bitrate ? false : true,
maxBitrate: bitrate
}, player);
}
});
}
function showRepeatModeMenu(player, btn) {
var menuItems = [];
var currentValue = playbackManager.getRepeatMode(player);
menuItems.push({
name: globalize.translate('RepeatAll'),
id: 'RepeatAll',
selected: currentValue === 'RepeatAll'
});
menuItems.push({
name: globalize.translate('RepeatOne'),
id: 'RepeatOne',
selected: currentValue === 'RepeatOne'
});
menuItems.push({
name: globalize.translate('None'),
id: 'RepeatNone',
selected: currentValue === 'RepeatNone'
});
return actionsheet.show({
items: menuItems,
positionTo: btn
}).then(function (mode) {
if (mode) {
playbackManager.setRepeatMode(mode, player);
}
});
}
function getQualitySecondaryText(player) {
var state = playbackManager.getPlayerState(player);
var isAutoEnabled = playbackManager.enableAutomaticBitrateDetection(player);
var currentMaxBitrate = playbackManager.getMaxStreamingBitrate(player);
var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
return stream.Type === 'Video';
})[0];
var videoWidth = videoStream ? videoStream.Width : null;
var videoHeight = videoStream ? videoStream.Height : null;
var options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
videoWidth: videoWidth,
videoHeight: videoHeight,
enableAuto: true
});
var menuItems = options.map(function (o) {
var opt = {
name: o.name,
id: o.bitrate,
asideText: o.secondaryText
};
if (o.selected) {
opt.selected = true;
}
return opt;
});
var selectedOption = options.filter(function (o) {
return o.selected;
});
if (!selectedOption.length) {
return null;
}
function showRepeatModeMenu(player, btn) {
var menuItems = [];
var currentValue = playbackManager.getRepeatMode(player);
selectedOption = selectedOption[0];
var text = selectedOption.name;
menuItems.push({
name: globalize.translate('RepeatAll'),
id: 'RepeatAll',
selected: currentValue === 'RepeatAll'
});
menuItems.push({
name: globalize.translate('RepeatOne'),
id: 'RepeatOne',
selected: currentValue === 'RepeatOne'
});
menuItems.push({
name: globalize.translate('None'),
id: 'RepeatNone',
selected: currentValue === 'RepeatNone'
});
return actionsheet.show({
items: menuItems,
positionTo: btn
}).then(function (mode) {
if (mode) {
playbackManager.setRepeatMode(mode, player);
}
});
if (selectedOption.autoText) {
if (state.PlayState && state.PlayState.PlayMethod !== 'Transcode') {
text += ' - Direct';
} else {
text += ' ' + selectedOption.autoText;
}
}
function getQualitySecondaryText(player) {
var state = playbackManager.getPlayerState(player);
var isAutoEnabled = playbackManager.enableAutomaticBitrateDetection(player);
var currentMaxBitrate = playbackManager.getMaxStreamingBitrate(player);
return text;
}
var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
return stream.Type === 'Video';
})[0];
function showAspectRatioMenu(player, btn) {
// each has a name and id
var currentId = playbackManager.getAspectRatio(player);
var menuItems = playbackManager.getSupportedAspectRatios(player).map(function (i) {
return {
id: i.id,
name: i.name,
selected: i.id === currentId
};
});
var videoWidth = videoStream ? videoStream.Width : null;
var videoHeight = videoStream ? videoStream.Height : null;
var options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
videoWidth: videoWidth,
videoHeight: videoHeight,
enableAuto: true
});
var menuItems = options.map(function (o) {
var opt = {
name: o.name,
id: o.bitrate,
asideText: o.secondaryText
};
if (o.selected) {
opt.selected = true;
}
return opt;
});
var selectedOption = options.filter(function (o) {
return o.selected;
});
if (!selectedOption.length) {
return null;
}
selectedOption = selectedOption[0];
var text = selectedOption.name;
if (selectedOption.autoText) {
if (state.PlayState && state.PlayState.PlayMethod !== 'Transcode') {
text += ' - Direct';
} else {
text += ' ' + selectedOption.autoText;
}
}
return text;
}
function showAspectRatioMenu(player, btn) {
// each has a name and id
var currentId = playbackManager.getAspectRatio(player);
var menuItems = playbackManager.getSupportedAspectRatios(player).map(function (i) {
return {
id: i.id,
name: i.name,
selected: i.id === currentId
};
});
return actionsheet.show({
items: menuItems,
positionTo: btn
}).then(function (id) {
if (id) {
playbackManager.setAspectRatio(id, player);
return Promise.resolve();
}
return Promise.reject();
});
}
function showWithUser(options, player, user) {
var supportedCommands = playbackManager.getSupportedCommands(player);
var mediaType = options.mediaType;
var menuItems = [];
if (supportedCommands.indexOf('SetAspectRatio') !== -1) {
var currentAspectRatioId = playbackManager.getAspectRatio(player);
var currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function (i) {
return i.id === currentAspectRatioId;
})[0];
menuItems.push({
name: globalize.translate('AspectRatio'),
id: 'aspectratio',
asideText: currentAspectRatio ? currentAspectRatio.name : null
});
}
if (user && user.Policy.EnableVideoPlaybackTranscoding) {
var secondaryQualityText = getQualitySecondaryText(player);
menuItems.push({
name: globalize.translate('Quality'),
id: 'quality',
asideText: secondaryQualityText
});
}
var repeatMode = playbackManager.getRepeatMode(player);
if (supportedCommands.indexOf('SetRepeatMode') !== -1 && playbackManager.currentMediaSource(player).RunTimeTicks) {
menuItems.push({
name: globalize.translate('RepeatMode'),
id: 'repeatmode',
asideText: repeatMode === 'RepeatNone' ? globalize.translate('None') : globalize.translate('' + repeatMode)
});
}
if (options.suboffset) {
menuItems.push({
name: globalize.translate('SubtitleOffset'),
id: 'suboffset',
asideText: null
});
}
if (options.stats) {
menuItems.push({
name: globalize.translate('PlaybackData'),
id: 'stats',
asideText: null
});
}
return actionsheet.show({
items: menuItems,
positionTo: options.positionTo
}).then(function (id) {
return handleSelectedOption(id, options, player);
});
}
function show(options) {
var player = options.player;
var currentItem = playbackManager.currentItem(player);
if (!currentItem || !currentItem.ServerId) {
return showWithUser(options, player, null);
}
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
return apiClient.getCurrentUser().then(function (user) {
return showWithUser(options, player, user);
});
}
function handleSelectedOption(id, options, player) {
switch (id) {
case 'quality':
return showQualityMenu(player, options.positionTo);
case 'aspectratio':
return showAspectRatioMenu(player, options.positionTo);
case 'repeatmode':
return showRepeatModeMenu(player, options.positionTo);
case 'stats':
if (options.onOption) {
options.onOption('stats');
}
return Promise.resolve();
case 'suboffset':
if (options.onOption) {
options.onOption('suboffset');
}
return Promise.resolve();
default:
break;
return actionsheet.show({
items: menuItems,
positionTo: btn
}).then(function (id) {
if (id) {
playbackManager.setAspectRatio(id, player);
return Promise.resolve();
}
return Promise.reject();
});
}
function showWithUser(options, player, user) {
var supportedCommands = playbackManager.getSupportedCommands(player);
var mediaType = options.mediaType;
var menuItems = [];
if (supportedCommands.indexOf('SetAspectRatio') !== -1) {
var currentAspectRatioId = playbackManager.getAspectRatio(player);
var currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function (i) {
return i.id === currentAspectRatioId;
})[0];
menuItems.push({
name: globalize.translate('AspectRatio'),
id: 'aspectratio',
asideText: currentAspectRatio ? currentAspectRatio.name : null
});
}
return {
show: show
};
});
if (user && user.Policy.EnableVideoPlaybackTranscoding) {
var secondaryQualityText = getQualitySecondaryText(player);
menuItems.push({
name: globalize.translate('Quality'),
id: 'quality',
asideText: secondaryQualityText
});
}
var repeatMode = playbackManager.getRepeatMode(player);
if (supportedCommands.indexOf('SetRepeatMode') !== -1 && playbackManager.currentMediaSource(player).RunTimeTicks) {
menuItems.push({
name: globalize.translate('RepeatMode'),
id: 'repeatmode',
asideText: repeatMode === 'RepeatNone' ? globalize.translate('None') : globalize.translate('' + repeatMode)
});
}
if (options.suboffset) {
menuItems.push({
name: globalize.translate('SubtitleOffset'),
id: 'suboffset',
asideText: null
});
}
if (options.stats) {
menuItems.push({
name: globalize.translate('PlaybackData'),
id: 'stats',
asideText: null
});
}
return actionsheet.show({
items: menuItems,
positionTo: options.positionTo
}).then(function (id) {
return handleSelectedOption(id, options, player);
});
}
export function show(options) {
var player = options.player;
var currentItem = playbackManager.currentItem(player);
if (!currentItem || !currentItem.ServerId) {
return showWithUser(options, player, null);
}
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
return apiClient.getCurrentUser().then(function (user) {
return showWithUser(options, player, user);
});
}
function handleSelectedOption(id, options, player) {
switch (id) {
case 'quality':
return showQualityMenu(player, options.positionTo);
case 'aspectratio':
return showAspectRatioMenu(player, options.positionTo);
case 'repeatmode':
return showRepeatModeMenu(player, options.positionTo);
case 'stats':
if (options.onOption) {
options.onOption('stats');
}
return Promise.resolve();
case 'suboffset':
if (options.onOption) {
options.onOption('suboffset');
}
return Promise.resolve();
default:
break;
}
return Promise.reject();
}
export default {
show: show
};

View file

@ -1,24 +1,20 @@
define([], function () {
'use strict';
export function getDisplayPlayMethod(session) {
function getDisplayPlayMethod(session) {
if (!session.NowPlayingItem) {
return null;
}
if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) {
return 'DirectStream';
} else if (session.PlayState.PlayMethod === 'Transcode') {
return 'Transcode';
} else if (session.PlayState.PlayMethod === 'DirectStream') {
return 'DirectPlay';
} else if (session.PlayState.PlayMethod === 'DirectPlay') {
return 'DirectPlay';
}
if (!session.NowPlayingItem) {
return null;
}
return {
getDisplayPlayMethod: getDisplayPlayMethod
};
});
if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) {
return 'DirectStream';
} else if (session.PlayState.PlayMethod === 'Transcode') {
return 'Transcode';
} else if (session.PlayState.PlayMethod === 'DirectStream') {
return 'DirectPlay';
} else if (session.PlayState.PlayMethod === 'DirectPlay') {
return 'DirectPlay';
}
}
export default {
getDisplayPlayMethod: getDisplayPlayMethod
};

View file

@ -1,47 +1,45 @@
define(['events', 'playbackManager'], function (events, playbackManager) {
'use strict';
import events from 'events';
import playbackManager from 'playbackManager';
function transferPlayback(oldPlayer, newPlayer) {
function transferPlayback(oldPlayer, newPlayer) {
const state = playbackManager.getPlayerState(oldPlayer);
const item = state.NowPlayingItem;
var state = playbackManager.getPlayerState(oldPlayer);
var item = state.NowPlayingItem;
if (!item) {
return;
}
var playState = state.PlayState || {};
var resumePositionTicks = playState.PositionTicks || 0;
playbackManager.stop(oldPlayer).then(function () {
playbackManager.play({
ids: [item.Id],
serverId: item.ServerId,
startPositionTicks: resumePositionTicks
}, newPlayer);
});
if (!item) {
return;
}
events.on(playbackManager, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) {
playbackManager.getPlaylist(oldPlayer).then(playlist => {
const playlistIds = playlist.map(x => x.Id);
const playState = state.PlayState || {};
const resumePositionTicks = playState.PositionTicks || 0;
const playlistIndex = playlistIds.indexOf(item.Id) || 0;
if (!oldPlayer || !newPlayer) {
return;
}
if (!oldPlayer.isLocalPlayer) {
console.debug('Skipping remote control autoplay because oldPlayer is not a local player');
return;
}
if (newPlayer.isLocalPlayer) {
console.debug('Skipping remote control autoplay because newPlayer is a local player');
return;
}
transferPlayback(oldPlayer, newPlayer);
playbackManager.stop(oldPlayer).then(() => {
playbackManager.play({
ids: playlistIds,
serverId: item.ServerId,
startPositionTicks: resumePositionTicks,
startIndex: playlistIndex
}, newPlayer);
});
});
}
events.on(playbackManager, 'playerchange', (e, newPlayer, newTarget, oldPlayer) => {
if (!oldPlayer || !newPlayer) {
return;
}
if (!oldPlayer.isLocalPlayer) {
console.debug('Skipping remote control autoplay because oldPlayer is not a local player');
return;
}
if (newPlayer.isLocalPlayer) {
console.debug('Skipping remote control autoplay because newPlayer is a local player');
return;
}
transferPlayback(oldPlayer, newPlayer);
});

View file

@ -1,159 +1,161 @@
define(['events', 'playbackManager', 'dom', 'browser', 'css!./iconosd', 'material-icons'], function (events, playbackManager, dom, browser) {
'use strict';
import events from 'events';
import playbackManager from 'playbackManager';
import dom from 'dom';
import browser from 'browser';
import 'css!./iconosd';
import 'material-icons';
var currentPlayer;
var osdElement;
var iconElement;
var progressElement;
var currentPlayer;
var osdElement;
var iconElement;
var progressElement;
var enableAnimation;
var enableAnimation;
function getOsdElementHtml() {
var html = '';
function getOsdElementHtml() {
var html = '';
html += '<span class="material-icons iconOsdIcon volume_up"></span>';
html += '<span class="material-icons iconOsdIcon volume_up"></span>';
html += '<div class="iconOsdProgressOuter"><div class="iconOsdProgressInner"></div></div>';
html += '<div class="iconOsdProgressOuter"><div class="iconOsdProgressInner"></div></div>';
return html;
return html;
}
function ensureOsdElement() {
var elem = osdElement;
if (!elem) {
enableAnimation = browser.supportsCssAnimation();
elem = document.createElement('div');
elem.classList.add('hide');
elem.classList.add('iconOsd');
elem.classList.add('iconOsd-hidden');
elem.classList.add('volumeOsd');
elem.innerHTML = getOsdElementHtml();
iconElement = elem.querySelector('.material-icons');
progressElement = elem.querySelector('.iconOsdProgressInner');
document.body.appendChild(elem);
osdElement = elem;
}
}
function ensureOsdElement() {
function onHideComplete() {
this.classList.add('hide');
}
var elem = osdElement;
if (!elem) {
var hideTimeout;
function showOsd() {
enableAnimation = browser.supportsCssAnimation();
clearHideTimeout();
elem = document.createElement('div');
elem.classList.add('hide');
elem.classList.add('iconOsd');
elem.classList.add('iconOsd-hidden');
elem.classList.add('volumeOsd');
elem.innerHTML = getOsdElementHtml();
var elem = osdElement;
iconElement = elem.querySelector('.material-icons');
progressElement = elem.querySelector('.iconOsdProgressInner');
document.body.appendChild(elem);
osdElement = elem;
}
}
function onHideComplete() {
this.classList.add('hide');
}
var hideTimeout;
function showOsd() {
clearHideTimeout();
var elem = osdElement;
dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
});
elem.classList.remove('hide');
// trigger reflow
void elem.offsetWidth;
requestAnimationFrame(function () {
elem.classList.remove('iconOsd-hidden');
hideTimeout = setTimeout(hideOsd, 3000);
});
}
function clearHideTimeout() {
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
}
function hideOsd() {
clearHideTimeout();
var elem = osdElement;
if (elem) {
if (enableAnimation) {
// trigger reflow
void elem.offsetWidth;
requestAnimationFrame(function () {
elem.classList.add('iconOsd-hidden');
dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
});
});
} else {
onHideComplete.call(elem);
}
}
}
function updatePlayerVolumeState(isMuted, volume) {
if (iconElement) {
iconElement.classList.remove('volume_off', 'volume_up');
iconElement.classList.add(isMuted ? 'volume_off' : 'volume_up');
}
if (progressElement) {
progressElement.style.width = (volume || 0) + '%';
}
}
function releaseCurrentPlayer() {
var player = currentPlayer;
if (player) {
events.off(player, 'volumechange', onVolumeChanged);
events.off(player, 'playbackstop', hideOsd);
currentPlayer = null;
}
}
function onVolumeChanged(e) {
var player = this;
ensureOsdElement();
updatePlayerVolumeState(player.isMuted(), player.getVolume());
showOsd();
}
function bindToPlayer(player) {
if (player === currentPlayer) {
return;
}
releaseCurrentPlayer();
currentPlayer = player;
if (!player) {
return;
}
hideOsd();
events.on(player, 'volumechange', onVolumeChanged);
events.on(player, 'playbackstop', hideOsd);
}
events.on(playbackManager, 'playerchange', function () {
bindToPlayer(playbackManager.getCurrentPlayer());
dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
});
bindToPlayer(playbackManager.getCurrentPlayer());
elem.classList.remove('hide');
// trigger reflow
void elem.offsetWidth;
requestAnimationFrame(function () {
elem.classList.remove('iconOsd-hidden');
hideTimeout = setTimeout(hideOsd, 3000);
});
}
function clearHideTimeout() {
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
}
function hideOsd() {
clearHideTimeout();
var elem = osdElement;
if (elem) {
if (enableAnimation) {
// trigger reflow
void elem.offsetWidth;
requestAnimationFrame(function () {
elem.classList.add('iconOsd-hidden');
dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, {
once: true
});
});
} else {
onHideComplete.call(elem);
}
}
}
function updatePlayerVolumeState(isMuted, volume) {
if (iconElement) {
iconElement.classList.remove('volume_off', 'volume_up');
iconElement.classList.add(isMuted ? 'volume_off' : 'volume_up');
}
if (progressElement) {
progressElement.style.width = (volume || 0) + '%';
}
}
function releaseCurrentPlayer() {
var player = currentPlayer;
if (player) {
events.off(player, 'volumechange', onVolumeChanged);
events.off(player, 'playbackstop', hideOsd);
currentPlayer = null;
}
}
function onVolumeChanged(e) {
var player = this;
ensureOsdElement();
updatePlayerVolumeState(player.isMuted(), player.getVolume());
showOsd();
}
function bindToPlayer(player) {
if (player === currentPlayer) {
return;
}
releaseCurrentPlayer();
currentPlayer = player;
if (!player) {
return;
}
hideOsd();
events.on(player, 'volumechange', onVolumeChanged);
events.on(player, 'playbackstop', hideOsd);
}
events.on(playbackManager, 'playerchange', function () {
bindToPlayer(playbackManager.getCurrentPlayer());
});
bindToPlayer(playbackManager.getCurrentPlayer());

View file

@ -58,7 +58,7 @@ define(['events', 'globalize'], function (events, globalize) {
return new Promise(function (resolve, reject) {
require([pluginSpec], (pluginFactory) => {
var plugin = new pluginFactory();
var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory();
// See if it's already installed
var existing = instance.pluginsList.filter(function (p) {

View file

@ -1,552 +0,0 @@
define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'], function (playbackManager, events, serverNotifications, connectionManager) {
'use strict';
function getActivePlayerId() {
var info = playbackManager.getPlayerInfo();
return info ? info.id : null;
}
function sendPlayCommand(apiClient, options, playType) {
var sessionId = getActivePlayerId();
var ids = options.ids || options.items.map(function (i) {
return i.Id;
});
var remoteOptions = {
ItemIds: ids.join(','),
PlayCommand: playType
};
if (options.startPositionTicks) {
remoteOptions.StartPositionTicks = options.startPositionTicks;
}
if (options.mediaSourceId) {
remoteOptions.MediaSourceId = options.mediaSourceId;
}
if (options.audioStreamIndex != null) {
remoteOptions.AudioStreamIndex = options.audioStreamIndex;
}
if (options.subtitleStreamIndex != null) {
remoteOptions.SubtitleStreamIndex = options.subtitleStreamIndex;
}
if (options.startIndex != null) {
remoteOptions.StartIndex = options.startIndex;
}
return apiClient.sendPlayCommand(sessionId, remoteOptions);
}
function sendPlayStateCommand(apiClient, command, options) {
var sessionId = getActivePlayerId();
apiClient.sendPlayStateCommand(sessionId, command, options);
}
function getCurrentApiClient(instance) {
var currentServerId = instance.currentServerId;
if (currentServerId) {
return connectionManager.getApiClient(currentServerId);
}
return connectionManager.currentApiClient();
}
function sendCommandByName(instance, name, options) {
var command = {
Name: name
};
if (options) {
command.Arguments = options;
}
instance.sendCommand(command);
}
function unsubscribeFromPlayerUpdates(instance) {
instance.isUpdating = true;
var apiClient = getCurrentApiClient(instance);
apiClient.sendMessage('SessionsStop');
if (instance.pollInterval) {
clearInterval(instance.pollInterval);
instance.pollInterval = null;
}
}
function processUpdatedSessions(instance, sessions, apiClient) {
var serverId = apiClient.serverId();
sessions.map(function (s) {
if (s.NowPlayingItem) {
s.NowPlayingItem.ServerId = serverId;
}
});
var currentTargetId = getActivePlayerId();
var session = sessions.filter(function (s) {
return s.Id === currentTargetId;
})[0];
if (session) {
normalizeImages(session, apiClient);
var eventNames = getChangedEvents(instance.lastPlayerData, session);
instance.lastPlayerData = session;
for (var i = 0, length = eventNames.length; i < length; i++) {
events.trigger(instance, eventNames[i], [session]);
}
} else {
instance.lastPlayerData = session;
playbackManager.setDefaultPlayerActive();
}
}
function getChangedEvents(state1, state2) {
var names = [];
if (!state1) {
names.push('statechange');
names.push('timeupdate');
names.push('pause');
return names;
}
// TODO: Trim these down to prevent the UI from over-refreshing
names.push('statechange');
names.push('timeupdate');
names.push('pause');
return names;
}
function onPollIntervalFired() {
var instance = this;
var apiClient = getCurrentApiClient(instance);
if (!apiClient.isMessageChannelOpen()) {
apiClient.getSessions().then(function (sessions) {
processUpdatedSessions(instance, sessions, apiClient);
});
}
}
function subscribeToPlayerUpdates(instance) {
instance.isUpdating = true;
var apiClient = getCurrentApiClient(instance);
apiClient.sendMessage('SessionsStart', '100,800');
if (instance.pollInterval) {
clearInterval(instance.pollInterval);
instance.pollInterval = null;
}
instance.pollInterval = setInterval(onPollIntervalFired.bind(instance), 5000);
}
function normalizeImages(state, apiClient) {
if (state && state.NowPlayingItem) {
var item = state.NowPlayingItem;
if (!item.ImageTags || !item.ImageTags.Primary) {
if (item.PrimaryImageTag) {
item.ImageTags = item.ImageTags || {};
item.ImageTags.Primary = item.PrimaryImageTag;
}
}
if (item.BackdropImageTag && item.BackdropItemId === item.Id) {
item.BackdropImageTags = [item.BackdropImageTag];
}
if (item.BackdropImageTag && item.BackdropItemId !== item.Id) {
item.ParentBackdropImageTags = [item.BackdropImageTag];
item.ParentBackdropItemId = item.BackdropItemId;
}
if (!item.ServerId) {
item.ServerId = apiClient.serverId();
}
}
}
function SessionPlayer() {
var self = this;
this.name = 'Remote Control';
this.type = 'mediaplayer';
this.isLocalPlayer = false;
this.id = 'remoteplayer';
events.on(serverNotifications, 'Sessions', function (e, apiClient, data) {
processUpdatedSessions(self, data, apiClient);
});
}
SessionPlayer.prototype.beginPlayerUpdates = function () {
this.playerListenerCount = this.playerListenerCount || 0;
if (this.playerListenerCount <= 0) {
this.playerListenerCount = 0;
subscribeToPlayerUpdates(this);
}
this.playerListenerCount++;
};
SessionPlayer.prototype.endPlayerUpdates = function () {
this.playerListenerCount = this.playerListenerCount || 0;
this.playerListenerCount--;
if (this.playerListenerCount <= 0) {
unsubscribeFromPlayerUpdates(this);
this.playerListenerCount = 0;
}
};
SessionPlayer.prototype.getPlayerState = function () {
return this.lastPlayerData || {};
};
SessionPlayer.prototype.getTargets = function () {
var apiClient = getCurrentApiClient(this);
var sessionQuery = {
ControllableByUserId: apiClient.getCurrentUserId()
};
if (apiClient) {
var name = this.name;
return apiClient.getSessions(sessionQuery).then(function (sessions) {
return sessions.filter(function (s) {
return s.DeviceId !== apiClient.deviceId();
}).map(function (s) {
return {
name: s.DeviceName,
deviceName: s.DeviceName,
deviceType: s.DeviceType,
id: s.Id,
playerName: name,
appName: s.Client,
playableMediaTypes: s.PlayableMediaTypes,
isLocalPlayer: false,
supportedCommands: s.SupportedCommands,
user: s.UserId ? {
Id: s.UserId,
Name: s.UserName,
PrimaryImageTag: s.UserPrimaryImageTag
} : null
};
});
});
} else {
return Promise.resolve([]);
}
};
SessionPlayer.prototype.sendCommand = function (command) {
var sessionId = getActivePlayerId();
var apiClient = getCurrentApiClient(this);
apiClient.sendCommand(sessionId, command);
};
SessionPlayer.prototype.play = function (options) {
options = Object.assign({}, options);
if (options.items) {
options.ids = options.items.map(function (i) {
return i.Id;
});
options.items = null;
}
return sendPlayCommand(getCurrentApiClient(this), options, 'PlayNow');
};
SessionPlayer.prototype.shuffle = function (item) {
sendPlayCommand(getCurrentApiClient(this), { ids: [item.Id] }, 'PlayShuffle');
};
SessionPlayer.prototype.instantMix = function (item) {
sendPlayCommand(getCurrentApiClient(this), { ids: [item.Id] }, 'PlayInstantMix');
};
SessionPlayer.prototype.queue = function (options) {
sendPlayCommand(getCurrentApiClient(this), options, 'PlayNext');
};
SessionPlayer.prototype.queueNext = function (options) {
sendPlayCommand(getCurrentApiClient(this), options, 'PlayLast');
};
SessionPlayer.prototype.canPlayMediaType = function (mediaType) {
mediaType = (mediaType || '').toLowerCase();
return mediaType === 'audio' || mediaType === 'video';
};
SessionPlayer.prototype.canQueueMediaType = function (mediaType) {
return this.canPlayMediaType(mediaType);
};
SessionPlayer.prototype.stop = function () {
sendPlayStateCommand(getCurrentApiClient(this), 'stop');
};
SessionPlayer.prototype.nextTrack = function () {
sendPlayStateCommand(getCurrentApiClient(this), 'nextTrack');
};
SessionPlayer.prototype.previousTrack = function () {
sendPlayStateCommand(getCurrentApiClient(this), 'previousTrack');
};
SessionPlayer.prototype.seek = function (positionTicks) {
sendPlayStateCommand(getCurrentApiClient(this), 'seek',
{
SeekPositionTicks: positionTicks
});
};
SessionPlayer.prototype.currentTime = function (val) {
if (val != null) {
return this.seek(val);
}
var state = this.lastPlayerData || {};
state = state.PlayState || {};
return state.PositionTicks;
};
SessionPlayer.prototype.duration = function () {
var state = this.lastPlayerData || {};
state = state.NowPlayingItem || {};
return state.RunTimeTicks;
};
SessionPlayer.prototype.paused = function () {
var state = this.lastPlayerData || {};
state = state.PlayState || {};
return state.IsPaused;
};
SessionPlayer.prototype.getVolume = function () {
var state = this.lastPlayerData || {};
state = state.PlayState || {};
return state.VolumeLevel;
};
SessionPlayer.prototype.isMuted = function () {
var state = this.lastPlayerData || {};
state = state.PlayState || {};
return state.IsMuted;
};
SessionPlayer.prototype.pause = function () {
sendPlayStateCommand(getCurrentApiClient(this), 'Pause');
};
SessionPlayer.prototype.unpause = function () {
sendPlayStateCommand(getCurrentApiClient(this), 'Unpause');
};
SessionPlayer.prototype.playPause = function () {
sendPlayStateCommand(getCurrentApiClient(this), 'PlayPause');
};
SessionPlayer.prototype.setMute = function (isMuted) {
if (isMuted) {
sendCommandByName(this, 'Mute');
} else {
sendCommandByName(this, 'Unmute');
}
};
SessionPlayer.prototype.toggleMute = function () {
sendCommandByName(this, 'ToggleMute');
};
SessionPlayer.prototype.setVolume = function (vol) {
sendCommandByName(this, 'SetVolume', {
Volume: vol
});
};
SessionPlayer.prototype.volumeUp = function () {
sendCommandByName(this, 'VolumeUp');
};
SessionPlayer.prototype.volumeDown = function () {
sendCommandByName(this, 'VolumeDown');
};
SessionPlayer.prototype.toggleFullscreen = function () {
sendCommandByName(this, 'ToggleFullscreen');
};
SessionPlayer.prototype.audioTracks = function () {
var state = this.lastPlayerData || {};
state = state.NowPlayingItem || {};
var streams = state.MediaStreams || [];
return streams.filter(function (s) {
return s.Type === 'Audio';
});
};
SessionPlayer.prototype.getAudioStreamIndex = function () {
var state = this.lastPlayerData || {};
state = state.PlayState || {};
return state.AudioStreamIndex;
};
SessionPlayer.prototype.playTrailers = function (item) {
sendCommandByName(this, 'PlayTrailers', {
ItemId: item.Id
});
};
SessionPlayer.prototype.setAudioStreamIndex = function (index) {
sendCommandByName(this, 'SetAudioStreamIndex', {
Index: index
});
};
SessionPlayer.prototype.subtitleTracks = function () {
var state = this.lastPlayerData || {};
state = state.NowPlayingItem || {};
var streams = state.MediaStreams || [];
return streams.filter(function (s) {
return s.Type === 'Subtitle';
});
};
SessionPlayer.prototype.getSubtitleStreamIndex = function () {
var state = this.lastPlayerData || {};
state = state.PlayState || {};
return state.SubtitleStreamIndex;
};
SessionPlayer.prototype.setSubtitleStreamIndex = function (index) {
sendCommandByName(this, 'SetSubtitleStreamIndex', {
Index: index
});
};
SessionPlayer.prototype.getMaxStreamingBitrate = function () {
};
SessionPlayer.prototype.setMaxStreamingBitrate = function (options) {
};
SessionPlayer.prototype.isFullscreen = function () {
};
SessionPlayer.prototype.toggleFullscreen = function () {
};
SessionPlayer.prototype.getRepeatMode = function () {
};
SessionPlayer.prototype.setRepeatMode = function (mode) {
sendCommandByName(this, 'SetRepeatMode', {
RepeatMode: mode
});
};
SessionPlayer.prototype.displayContent = function (options) {
sendCommandByName(this, 'DisplayContent', options);
};
SessionPlayer.prototype.isPlaying = function () {
var state = this.lastPlayerData || {};
return state.NowPlayingItem != null;
};
SessionPlayer.prototype.isPlayingVideo = function () {
var state = this.lastPlayerData || {};
state = state.NowPlayingItem || {};
return state.MediaType === 'Video';
};
SessionPlayer.prototype.isPlayingAudio = function () {
var state = this.lastPlayerData || {};
state = state.NowPlayingItem || {};
return state.MediaType === 'Audio';
};
SessionPlayer.prototype.getPlaylist = function () {
return Promise.resolve([]);
};
SessionPlayer.prototype.getCurrentPlaylistItemId = function () {
};
SessionPlayer.prototype.setCurrentPlaylistItem = function (playlistItemId) {
return Promise.resolve();
};
SessionPlayer.prototype.removeFromPlaylist = function (playlistItemIds) {
return Promise.resolve();
};
SessionPlayer.prototype.tryPair = function (target) {
return Promise.resolve();
};
return SessionPlayer;
});

View file

@ -57,7 +57,6 @@ define(['apphost', 'userSettings', 'browser', 'events', 'backdrop', 'globalize',
var selectedTheme;
for (var i = 0, length = themes.length; i < length; i++) {
var theme = themes[i];
if (theme[isDefaultProperty]) {
defaultTheme = theme;

View file

@ -1,6 +1,6 @@
/**
* Module that manages the SyncPlay feature.
* @module components/syncplay/syncPlayManager
* @module components/syncPlay/syncPlayManager
*/
import events from 'events';
@ -265,10 +265,9 @@ class SyncPlayManager {
events.on(player, 'timeupdate', this._onTimeUpdate);
events.on(player, 'playing', this._onPlaying);
events.on(player, 'waiting', this._onWaiting);
this.playbackRateSupported = player.supports('PlaybackRate');
// Save player current PlaybackRate value
if (this.playbackRateSupported) {
if (player.supports && player.supports('PlaybackRate')) {
this.localPlayerPlaybackRate = player.getPlaybackRate();
}
}

View file

@ -1,6 +1,6 @@
/**
* Module that manages time syncing with server.
* @module components/syncplay/timeSyncManager
* @module components/syncPlay/timeSyncManager
*/
import events from 'events';

View file

@ -21,14 +21,11 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
if (!newView.initComplete) {
newView.initComplete = true;
var controller;
if (typeof options.controllerFactory === 'function') {
// Use controller method
var controller = new options.controllerFactory(newView, eventDetail.detail.params);
} else if (typeof options.controllerFactory === 'object') {
// Use controller class
var controller = new options.controllerFactory.default(newView, eventDetail.detail.params);
controller = new options.controllerFactory(newView, eventDetail.detail.params);
} else if (options.controllerFactory && typeof options.controllerFactory.default === 'function') {
controller = new options.controllerFactory.default(newView, eventDetail.detail.params);
}
if (!options.controllerFactory || dispatchPageEvents) {

View file

@ -57,7 +57,7 @@ define(['require', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'conne
return new Promise(function (resolve, reject) {
require(['text!./viewsettings.template.html'], function (template) {
require(['text!./viewSettings.template.html'], function (template) {
var dialogOptions = {
removeOnClose: true,

View file

@ -1,409 +0,0 @@
define(['require', 'events', 'browser', 'appRouter', 'loading'], function (require, events, browser, appRouter, loading) {
'use strict';
/* globals YT */
function zoomIn(elem, iterations) {
var keyframes = [
{ transform: 'scale3d(.2, .2, .2) ', opacity: '.6', offset: 0 },
{ transform: 'none', opacity: '1', offset: 1 }
];
var timing = { duration: 240, iterations: iterations };
return elem.animate(keyframes, timing);
}
function createMediaElement(instance, options) {
return new Promise(function (resolve, reject) {
var dlg = document.querySelector('.youtubePlayerContainer');
if (!dlg) {
require(['css!./style'], function () {
loading.show();
var dlg = document.createElement('div');
dlg.classList.add('youtubePlayerContainer');
if (options.fullscreen) {
dlg.classList.add('onTop');
}
dlg.innerHTML = '<div id="player"></div>';
var videoElement = dlg.querySelector('#player');
document.body.insertBefore(dlg, document.body.firstChild);
instance.videoDialog = dlg;
if (options.fullscreen && dlg.animate && !browser.slow) {
zoomIn(dlg, 1).onfinish = function () {
resolve(videoElement);
};
} else {
resolve(videoElement);
}
});
} else {
resolve(dlg.querySelector('#player'));
}
});
}
function onVideoResize() {
var instance = this;
var player = instance.currentYoutubePlayer;
var dlg = instance.videoDialog;
if (player && dlg) {
player.setSize(dlg.offsetWidth, dlg.offsetHeight);
}
}
function clearTimeUpdateInterval(instance) {
if (instance.timeUpdateInterval) {
clearInterval(instance.timeUpdateInterval);
}
instance.timeUpdateInterval = null;
}
function onEndedInternal(instance) {
clearTimeUpdateInterval(instance);
var resizeListener = instance.resizeListener;
if (resizeListener) {
window.removeEventListener('resize', resizeListener);
window.removeEventListener('orientationChange', resizeListener);
instance.resizeListener = null;
}
var stopInfo = {
src: instance._currentSrc
};
events.trigger(instance, 'stopped', [stopInfo]);
instance._currentSrc = null;
if (instance.currentYoutubePlayer) {
instance.currentYoutubePlayer.destroy();
}
instance.currentYoutubePlayer = null;
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.playVideo();
}
function onTimeUpdate(e) {
events.trigger(this, 'timeupdate');
}
function onPlaying(instance, playOptions, resolve) {
if (!instance.started) {
instance.started = true;
resolve();
clearTimeUpdateInterval(instance);
instance.timeUpdateInterval = setInterval(onTimeUpdate.bind(instance), 500);
if (playOptions.fullscreen) {
appRouter.showVideoOsd().then(function () {
instance.videoDialog.classList.remove('onTop');
});
} else {
appRouter.setTransparency('backdrop');
instance.videoDialog.classList.remove('onTop');
}
require(['loading'], function (loading) {
loading.hide();
});
}
}
function setCurrentSrc(instance, elem, options) {
return new Promise(function (resolve, reject) {
require(['queryString'], function (queryString) {
instance._currentSrc = options.url;
var params = queryString.parse(options.url.split('?')[1]);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
window.onYouTubeIframeAPIReady = function () {
instance.currentYoutubePlayer = new YT.Player('player', {
height: instance.videoDialog.offsetHeight,
width: instance.videoDialog.offsetWidth,
videoId: params.v,
events: {
'onReady': onPlayerReady,
'onStateChange': function (event) {
if (event.data === YT.PlayerState.PLAYING) {
onPlaying(instance, options, resolve);
} else if (event.data === YT.PlayerState.ENDED) {
onEndedInternal(instance);
} else if (event.data === YT.PlayerState.PAUSED) {
events.trigger(instance, 'pause');
}
}
},
playerVars: {
controls: 0,
enablejsapi: 1,
modestbranding: 1,
rel: 0,
showinfo: 0,
fs: 0,
playsinline: 1
}
});
var resizeListener = instance.resizeListener;
if (resizeListener) {
window.removeEventListener('resize', resizeListener);
window.addEventListener('resize', resizeListener);
} else {
resizeListener = instance.resizeListener = onVideoResize.bind(instance);
window.addEventListener('resize', resizeListener);
}
window.removeEventListener('orientationChange', resizeListener);
window.addEventListener('orientationChange', resizeListener);
};
if (!window.YT) {
var tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
} else {
window.onYouTubeIframeAPIReady();
}
});
});
}
function YoutubePlayer() {
this.name = 'Youtube Player';
this.type = 'mediaplayer';
this.id = 'youtubeplayer';
// Let any players created by plugins take priority
this.priority = 1;
}
YoutubePlayer.prototype.play = function (options) {
this.started = false;
var instance = this;
return createMediaElement(this, options).then(function (elem) {
return setCurrentSrc(instance, elem, options);
});
};
YoutubePlayer.prototype.stop = function (destroyPlayer) {
var src = this._currentSrc;
if (src) {
if (this.currentYoutubePlayer) {
this.currentYoutubePlayer.stopVideo();
}
onEndedInternal(this);
if (destroyPlayer) {
this.destroy();
}
}
return Promise.resolve();
};
YoutubePlayer.prototype.destroy = function () {
appRouter.setTransparency('none');
var dlg = this.videoDialog;
if (dlg) {
this.videoDialog = null;
dlg.parentNode.removeChild(dlg);
}
};
YoutubePlayer.prototype.canPlayMediaType = function (mediaType) {
mediaType = (mediaType || '').toLowerCase();
return mediaType === 'audio' || mediaType === 'video';
};
YoutubePlayer.prototype.canPlayItem = function (item) {
// Does not play server items
return false;
};
YoutubePlayer.prototype.canPlayUrl = function (url) {
return url.toLowerCase().indexOf('youtube.com') !== -1;
};
YoutubePlayer.prototype.getDeviceProfile = function () {
return Promise.resolve({});
};
YoutubePlayer.prototype.currentSrc = function () {
return this._currentSrc;
};
YoutubePlayer.prototype.setSubtitleStreamIndex = function (index) {
};
YoutubePlayer.prototype.canSetAudioStreamIndex = function () {
return false;
};
YoutubePlayer.prototype.setAudioStreamIndex = function (index) {
};
// Save this for when playback stops, because querying the time at that point might return 0
YoutubePlayer.prototype.currentTime = function (val) {
var currentYoutubePlayer = this.currentYoutubePlayer;
if (currentYoutubePlayer) {
if (val != null) {
currentYoutubePlayer.seekTo(val / 1000, true);
return;
}
return currentYoutubePlayer.getCurrentTime() * 1000;
}
};
YoutubePlayer.prototype.duration = function (val) {
var currentYoutubePlayer = this.currentYoutubePlayer;
if (currentYoutubePlayer) {
return currentYoutubePlayer.getDuration() * 1000;
}
return null;
};
YoutubePlayer.prototype.pause = function () {
var currentYoutubePlayer = this.currentYoutubePlayer;
if (currentYoutubePlayer) {
currentYoutubePlayer.pauseVideo();
var instance = this;
// This needs a delay before the youtube player will report the correct player state
setTimeout(function () {
events.trigger(instance, 'pause');
}, 200);
}
};
YoutubePlayer.prototype.unpause = function () {
var currentYoutubePlayer = this.currentYoutubePlayer;
if (currentYoutubePlayer) {
currentYoutubePlayer.playVideo();
var instance = this;
// This needs a delay before the youtube player will report the correct player state
setTimeout(function () {
events.trigger(instance, 'unpause');
}, 200);
}
};
YoutubePlayer.prototype.paused = function () {
var currentYoutubePlayer = this.currentYoutubePlayer;
if (currentYoutubePlayer) {
return currentYoutubePlayer.getPlayerState() === 2;
}
return false;
};
YoutubePlayer.prototype.volume = function (val) {
if (val != null) {
return this.setVolume(val);
}
return this.getVolume();
};
YoutubePlayer.prototype.setVolume = function (val) {
var currentYoutubePlayer = this.currentYoutubePlayer;
if (currentYoutubePlayer) {
if (val != null) {
currentYoutubePlayer.setVolume(val);
}
}
};
YoutubePlayer.prototype.getVolume = function () {
var currentYoutubePlayer = this.currentYoutubePlayer;
if (currentYoutubePlayer) {
return currentYoutubePlayer.getVolume();
}
};
YoutubePlayer.prototype.setMute = function (mute) {
var currentYoutubePlayer = this.currentYoutubePlayer;
if (mute) {
if (currentYoutubePlayer) {
currentYoutubePlayer.mute();
}
} else {
if (currentYoutubePlayer) {
currentYoutubePlayer.unMute();
}
}
};
YoutubePlayer.prototype.isMuted = function () {
var currentYoutubePlayer = this.currentYoutubePlayer;
if (currentYoutubePlayer) {
return currentYoutubePlayer.isMuted();
}
};
return YoutubePlayer;
});

View file

@ -1,21 +0,0 @@
.youtubePlayerContainer {
background: #000 !important;
position: fixed !important;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
}
.youtubePlayerContainer.onTop {
z-index: 1000;
}
.youtubePlayerContainer video {
margin: 0 !important;
padding: 0 !important;
width: 100%;
height: 100%;
}