mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'master' into es6-modules-components-playback
This commit is contained in:
commit
7d6dca7907
89 changed files with 3687 additions and 1930 deletions
|
@ -72,12 +72,12 @@ define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-butt
|
|||
reject();
|
||||
}
|
||||
});
|
||||
dlg.querySelector('.btnCancel').addEventListener('click', function (e) {
|
||||
dlg.querySelector('.btnCancel').addEventListener('click', function () {
|
||||
dialogHelper.close(dlg);
|
||||
});
|
||||
dlg.querySelector('form').addEventListener('submit', function (e) {
|
||||
dlg.querySelector('form').addEventListener('submit', function (event) {
|
||||
submitSchedule(dlg, options);
|
||||
e.preventDefault();
|
||||
event.preventDefault();
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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>';
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
|
@ -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
|
@ -182,6 +182,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
|
|||
context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos();
|
||||
context.querySelector('#chkFadein').checked = userSettings.enableFastFadein();
|
||||
context.querySelector('#chkBackdrops').checked = userSettings.enableBackdrops();
|
||||
context.querySelector('#chkDetailsBanner').checked = userSettings.detailsBanner();
|
||||
|
||||
context.querySelector('#selectLanguage').value = userSettings.language() || '';
|
||||
context.querySelector('.selectDateTimeLocale').value = userSettings.dateTimeLocale() || '';
|
||||
|
@ -223,6 +224,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
|
|||
|
||||
userSettingsInstance.enableFastFadein(context.querySelector('#chkFadein').checked);
|
||||
userSettingsInstance.enableBackdrops(context.querySelector('#chkBackdrops').checked);
|
||||
userSettingsInstance.detailsBanner(context.querySelector('#chkDetailsBanner').checked);
|
||||
|
||||
if (user.Id === apiClient.getCurrentUserId()) {
|
||||
skinManager.setTheme(userSettingsInstance.theme());
|
||||
|
|
|
@ -156,6 +156,14 @@
|
|||
<div class="fieldDescription checkboxFieldDescription">${EnableFastImageFadeInHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription fldDetailsBanner">
|
||||
<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">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkBackdrops" />
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>';
|
||||
|
|
|
@ -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>';
|
||||
|
|
|
@ -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>';
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
});
|
|
@ -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;
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
|
@ -2187,7 +2185,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 +2214,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);
|
||||
|
|
|
@ -2,31 +2,31 @@ import events from 'events';
|
|||
import playbackManager from 'playbackManager';
|
||||
|
||||
function transferPlayback(oldPlayer, newPlayer) {
|
||||
|
||||
var state = playbackManager.getPlayerState(oldPlayer);
|
||||
|
||||
var item = state.NowPlayingItem;
|
||||
const state = playbackManager.getPlayerState(oldPlayer);
|
||||
const item = state.NowPlayingItem;
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
var playState = state.PlayState || {};
|
||||
var resumePositionTicks = playState.PositionTicks || 0;
|
||||
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;
|
||||
|
||||
playbackManager.stop(oldPlayer).then(function () {
|
||||
|
||||
playbackManager.play({
|
||||
ids: [item.Id],
|
||||
serverId: item.ServerId,
|
||||
startPositionTicks: resumePositionTicks
|
||||
|
||||
}, newPlayer);
|
||||
playbackManager.stop(oldPlayer).then(() => {
|
||||
playbackManager.play({
|
||||
ids: playlistIds,
|
||||
serverId: item.ServerId,
|
||||
startPositionTicks: resumePositionTicks,
|
||||
startIndex: playlistIndex
|
||||
}, newPlayer);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
events.on(playbackManager, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) {
|
||||
|
||||
events.on(playbackManager, 'playerchange', (e, newPlayer, newTarget, oldPlayer) => {
|
||||
if (!oldPlayer || !newPlayer) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
});
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
|
@ -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%;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue