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 osd-tweak

This commit is contained in:
dkanada 2020-11-21 22:15:49 +09:00 committed by GitHub
commit 36344af64f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
291 changed files with 6833 additions and 6332 deletions

View file

@ -1,4 +1,5 @@
/* eslint-disable indent */
import ServerConnections from '../../components/ServerConnections';
class BackdropScreensaver {
constructor() {
@ -20,10 +21,10 @@ class BackdropScreensaver {
Limit: 200
};
const apiClient = window.connectionManager.currentApiClient();
const apiClient = ServerConnections.currentApiClient();
apiClient.getItems(apiClient.getCurrentUserId(), query).then((result) => {
if (result.Items.length) {
import('slideshow').then(({default: Slideshow}) => {
import('../../components/slideshow/slideshow').then(({default: Slideshow}) => {
const newSlideShow = new Slideshow({
showTitle: true,
cover: true,

View file

@ -1,14 +1,14 @@
import browser from 'browser';
import loading from 'loading';
import keyboardnavigation from 'keyboardnavigation';
import dialogHelper from 'dialogHelper';
import dom from 'dom';
import events from 'events';
import 'css!./style';
import 'material-icons';
import 'paper-icon-button-light';
import loading from '../../components/loading/loading';
import keyboardnavigation from '../../scripts/keyboardNavigation';
import dialogHelper from '../../components/dialogHelper/dialogHelper';
import '../../scripts/dom';
import { Events } from 'jellyfin-apiclient';
import './style.css';
import 'material-design-icons-iconfont';
import '../../elements/emby-button/paper-icon-button-light';
import ServerConnections from '../../components/ServerConnections';
import TableOfContents from './tableOfContents';
import browser from '../../scripts/browser';
export class BookPlayer {
constructor() {
@ -260,7 +260,7 @@ export class BookPlayer {
};
const serverId = item.ServerId;
const apiClient = window.connectionManager.getApiClient(serverId);
const apiClient = ServerConnections.getApiClient(serverId);
return new Promise((resolve, reject) => {
import('epubjs').then(({default: epubjs}) => {
@ -290,7 +290,7 @@ export class BookPlayer {
epubElem.style.display = 'block';
rendition.on('relocated', (locations) => {
this.progress = book.locations.percentageFromCfi(locations.start.cfi);
events.trigger(this, 'timeupdate');
Events.trigger(this, 'timeupdate');
});
loading.hide();

View file

@ -1,4 +1,4 @@
import dialogHelper from 'dialogHelper';
import dialogHelper from '../../components/dialogHelper/dialogHelper';
export default class TableOfContents {
constructor(bookPlayer) {

View file

@ -1,229 +0,0 @@
import events from 'events';
// 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.
const protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)';
const 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/
const 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}))|:))'
+ ')(%.+)?';
const 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...
const 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');
const linkRegExp = RegExp(linkRegExpString, 'gi');
const 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
const LinkParser = {
parse: function (text) {
const links = [];
let match;
// eslint-disable-next-line no-cond-assign
while (match = linkRegExp.exec(text)) {
console.debug(match);
const txt = match[0];
const pos = match.index;
const len = txt.length;
const url = ensureProtocol(text);
links.push({ 'pos': pos, 'text': txt, 'len': len, 'url': url });
}
return links;
}
};
window.LinkParser = LinkParser;
})();
let cache = {};
// TODO: Replace with isIP (https://www.npmjs.com/package/is-ip)
function isValidIpAddress(address) {
const links = LinkParser.parse(address);
return links.length == 1;
}
// TODO: Add IPv6 support. Potentially replace with isLocalhost (https://www.npmjs.com/package/is-localhost-ip)
function isLocalIpAddress(address) {
address = address.toLowerCase();
if (address.includes('127.0.0.1')) {
return true;
}
if (address.includes('localhost')) {
return true;
}
return false;
}
export function getServerAddress(apiClient) {
const serverAddress = apiClient.serverAddress();
if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) {
return Promise.resolve(serverAddress);
}
const cachedValue = getCachedValue(serverAddress);
if (cachedValue) {
return Promise.resolve(cachedValue);
}
return apiClient.getEndpointInfo().then(function (endpoint) {
if (endpoint.IsInNetwork) {
return apiClient.getPublicSystemInfo().then(function (info) {
let 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) {
const obj = cache[key];
if (obj && (new Date().getTime() - obj.time) < 180000) {
return obj.value;
}
return null;
}
events.on(window.connectionManager, 'localusersignedin', clearCache);
events.on(window.connectionManager, 'localusersignedout', clearCache);
export default {
getServerAddress: getServerAddress
};

View file

@ -1,9 +1,11 @@
import appSettings from 'appSettings';
import * as userSettings from 'userSettings';
import playbackManager from 'playbackManager';
import globalize from 'globalize';
import events from 'events';
import castSenderApiLoader from 'castSenderApiLoader';
import appSettings from '../../scripts/settings/appSettings';
import * as userSettings from '../../scripts/settings/userSettings';
import { playbackManager } from '../../components/playback/playbackmanager';
import globalize from '../../scripts/globalize';
import { Events } from 'jellyfin-apiclient';
import castSenderApiLoader from '../../components/castSenderApi';
import ServerConnections from '../../components/ServerConnections';
import alert from '../../components/alert';
// Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js
@ -167,7 +169,7 @@ class CastPlayer {
alertText(globalize.translate('MessageChromecastConnectionError'), globalize.translate('HeaderError'));
}, 300);
} else if (message.type) {
events.trigger(this, message.type, [message.data]);
Events.trigger(this, message.type, [message.data]);
}
}
@ -236,7 +238,7 @@ class CastPlayer {
document.addEventListener('volumeupbutton', onVolumeUpKeyDown, false);
document.addEventListener('volumedownbutton', onVolumeDownKeyDown, false);
events.trigger(this, 'connect');
Events.trigger(this, 'connect');
this.sendMessage({
options: {},
command: 'Identify'
@ -324,11 +326,11 @@ class CastPlayer {
let apiClient;
if (message.options && message.options.ServerId) {
apiClient = window.connectionManager.getApiClient(message.options.ServerId);
apiClient = ServerConnections.getApiClient(message.options.ServerId);
} else if (message.options && message.options.items && message.options.items.length) {
apiClient = window.connectionManager.getApiClient(message.options.items[0].ServerId);
apiClient = ServerConnections.getApiClient(message.options.items[0].ServerId);
} else {
apiClient = window.connectionManager.currentApiClient();
apiClient = ServerConnections.currentApiClient();
}
message = Object.assign(message, {
@ -351,14 +353,7 @@ class CastPlayer {
message.subtitleBurnIn = appSettings.get('subtitleburnin') || '';
}
return new Promise(function (resolve, reject) {
import('./chromecastHelper').then(({ default: chromecastHelper }) => {
chromecastHelper.getServerAddress(apiClient).then(function (serverAddress) {
message.serverAddress = serverAddress;
player.sendMessageInternal(message).then(resolve, reject);
}, reject);
});
});
return player.sendMessageInternal(message);
}
sendMessageInternal(message) {
@ -439,11 +434,9 @@ class CastPlayer {
}
function alertText(text, title) {
import('alert').then(({default: alert}) => {
alert({
text: text,
title: title
});
alert({
text,
title
});
}
@ -495,11 +488,11 @@ function getItemsForPlayback(apiClient, query) {
}
function bindEventForRelay(instance, eventName) {
events.on(instance._castPlayer, eventName, function (e, data) {
Events.on(instance._castPlayer, eventName, function (e, data) {
console.debug('cc: ' + eventName);
const state = instance.getPlayerStateInternal(data);
events.trigger(instance, eventName, [state]);
Events.trigger(instance, eventName, [state]);
});
}
@ -514,7 +507,7 @@ function initializeChromecast() {
}
}));
events.on(instance._castPlayer, 'connect', function (e) {
Events.on(instance._castPlayer, 'connect', function (e) {
if (currentResolve) {
sendConnectionResult(true);
} else {
@ -526,20 +519,20 @@ function initializeChromecast() {
instance.lastPlayerData = null;
});
events.on(instance._castPlayer, 'playbackstart', function (e, data) {
Events.on(instance._castPlayer, 'playbackstart', function (e, data) {
console.debug('cc: playbackstart');
instance._castPlayer.initializeCastPlayer();
const state = instance.getPlayerStateInternal(data);
events.trigger(instance, 'playbackstart', [state]);
Events.trigger(instance, 'playbackstart', [state]);
});
events.on(instance._castPlayer, 'playbackstop', function (e, data) {
Events.on(instance._castPlayer, 'playbackstop', function (e, data) {
console.debug('cc: playbackstop');
let state = instance.getPlayerStateInternal(data);
events.trigger(instance, 'playbackstop', [state]);
Events.trigger(instance, 'playbackstop', [state]);
state = instance.lastPlayerData.PlayState || {};
const volume = state.VolumeLevel || 0.5;
@ -552,11 +545,11 @@ function initializeChromecast() {
instance.lastPlayerData.PlayState.IsMuted = mute;
});
events.on(instance._castPlayer, 'playbackprogress', function (e, data) {
Events.on(instance._castPlayer, 'playbackprogress', function (e, data) {
console.debug('cc: positionchange');
const state = instance.getPlayerStateInternal(data);
events.trigger(instance, 'timeupdate', [state]);
Events.trigger(instance, 'timeupdate', [state]);
});
bindEventForRelay(instance, 'timeupdate');
@ -566,11 +559,11 @@ function initializeChromecast() {
bindEventForRelay(instance, 'repeatmodechange');
bindEventForRelay(instance, 'shufflequeuemodechange');
events.on(instance._castPlayer, 'playstatechange', function (e, data) {
Events.on(instance._castPlayer, 'playstatechange', function (e, data) {
console.debug('cc: playstatechange');
const state = instance.getPlayerStateInternal(data);
events.trigger(instance, 'pause', [state]);
Events.trigger(instance, 'pause', [state]);
});
}
@ -664,7 +657,7 @@ class ChromecastPlayer {
console.debug(JSON.stringify(data));
if (triggerStateChange) {
events.trigger(this, 'statechange', [data]);
Events.trigger(this, 'statechange', [data]);
}
return data;
@ -672,7 +665,7 @@ class ChromecastPlayer {
playWithCommand(options, command) {
if (!options.items) {
const apiClient = window.connectionManager.getApiClient(options.serverId);
const apiClient = ServerConnections.getApiClient(options.serverId);
const instance = this;
return apiClient.getItem(apiClient.getCurrentUserId(), options.ids[0]).then(function (item) {
@ -984,7 +977,7 @@ class ChromecastPlayer {
}
shuffle(item) {
const apiClient = window.connectionManager.getApiClient(item.ServerId);
const apiClient = ServerConnections.getApiClient(item.ServerId);
const userId = apiClient.getCurrentUserId();
const instance = this;
@ -997,7 +990,7 @@ class ChromecastPlayer {
}
instantMix(item) {
const apiClient = window.connectionManager.getApiClient(item.ServerId);
const apiClient = ServerConnections.getApiClient(item.ServerId);
const userId = apiClient.getCurrentUserId();
const instance = this;
@ -1035,7 +1028,7 @@ class ChromecastPlayer {
}
const instance = this;
const apiClient = window.connectionManager.getApiClient(options.serverId);
const apiClient = ServerConnections.getApiClient(options.serverId);
return getItemsForPlayback(apiClient, {
Ids: options.ids.join(',')

View file

@ -1,8 +1,10 @@
import loading from 'loading';
import dialogHelper from 'dialogHelper';
import keyboardnavigation from 'keyboardnavigation';
import appRouter from 'appRouter';
import * as libarchive from 'libarchive';
// eslint-disable-next-line import/named, import/namespace
import { Archive } from 'libarchive.js';
import loading from '../../components/loading/loading';
import dialogHelper from '../../components/dialogHelper/dialogHelper';
import keyboardnavigation from '../../scripts/keyboardNavigation';
import { appRouter } from '../../components/appRouter';
import ServerConnections from '../../components/ServerConnections';
export class ComicsPlayer {
constructor() {
@ -93,9 +95,9 @@ export class ComicsPlayer {
loading.show();
const serverId = item.ServerId;
const apiClient = window.connectionManager.getApiClient(serverId);
const apiClient = ServerConnections.getApiClient(serverId);
libarchive.Archive.init({
Archive.init({
workerUrl: appRouter.baseUrl() + '/libraries/worker-bundle.js'
});
@ -175,7 +177,7 @@ class ArchiveSource {
}
const blob = await res.blob();
this.archive = await libarchive.Archive.open(blob);
this.archive = await Archive.open(blob);
this.raw = await this.archive.getFilesArray();
this.numberOfFiles = this.raw.length;
await this.archive.extractFiles();

View file

@ -1,6 +1,7 @@
import globalize from 'globalize';
import * as userSettings from 'userSettings';
import appHost from 'apphost';
import globalize from '../../scripts/globalize';
import * as userSettings from '../../scripts/settings/userSettings';
import { appHost } from '../../components/apphost';
import alert from '../../components/alert';
// TODO: Replace with date-fns
// https://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php
@ -26,13 +27,8 @@ function showMessage(text, userSettingsKey, appHostFeature) {
return Promise.resolve();
}
return new Promise(function (resolve, reject) {
userSettings.set(userSettingsKey, '1', false);
import('alert').then(({default: alert}) => {
return alert(text).then(resolve, resolve);
});
});
userSettings.set(userSettingsKey, '1', false);
return alert(text).catch(() => { /* ignore exceptions */ });
}
function showBlurayMessage() {

View file

@ -1,12 +1,11 @@
import events from 'events';
import browser from 'browser';
import appHost from 'apphost';
import * as htmlMediaHelper from 'htmlMediaHelper';
import { Events } from 'jellyfin-apiclient';
import browser from '../../scripts/browser';
import { appHost } from '../../components/apphost';
import * as htmlMediaHelper from '../../components/htmlMediaHelper';
import profileBuilder from '../../scripts/browserDeviceProfile';
function getDefaultProfile() {
return import('browserdeviceprofile').then(({ default: profileBuilder }) => {
return profileBuilder({});
});
return profileBuilder({});
}
let fadeTimeout;
@ -51,7 +50,7 @@ function supportsFade() {
}
function requireHlsPlayer(callback) {
import('hlsjs').then(({ default: hls }) => {
import('hls.js').then(({ default: hls }) => {
window.Hls = hls;
callback();
});
@ -68,7 +67,7 @@ function enableHlsPlayer(url, item, mediaSource, mediaType) {
// issue head request to get content type
return new Promise(function (resolve, reject) {
import('fetchHelper').then((fetchHelper) => {
import('../../components/fetchhelper').then((fetchHelper) => {
fetchHelper.ajax({
url: url,
type: 'HEAD'
@ -251,14 +250,14 @@ class HtmlAudioPlayer {
// Don't trigger events after user stop
if (!self._isFadingOut) {
self._currentTime = time;
events.trigger(self, 'timeupdate');
Events.trigger(self, 'timeupdate');
}
}
function onVolumeChange() {
if (!self._isFadingOut) {
htmlMediaHelper.saveVolume(this.volume);
events.trigger(self, 'volumechange');
Events.trigger(self, 'volumechange');
}
}
@ -269,19 +268,19 @@ class HtmlAudioPlayer {
htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks);
}
events.trigger(self, 'playing');
Events.trigger(self, 'playing');
}
function onPlay(e) {
events.trigger(self, 'unpause');
Events.trigger(self, 'unpause');
}
function onPause() {
events.trigger(self, 'pause');
Events.trigger(self, 'pause');
}
function onWaiting() {
events.trigger(self, 'waiting');
Events.trigger(self, 'waiting');
}
function onError() {

View file

@ -1,10 +1,10 @@
import browser from 'browser';
import events from 'events';
import appHost from 'apphost';
import loading from 'loading';
import dom from 'dom';
import playbackManager from 'playbackManager';
import appRouter from 'appRouter';
import browser from '../../scripts/browser';
import { Events } from 'jellyfin-apiclient';
import { appHost } from '../../components/apphost';
import loading from '../../components/loading/loading';
import dom from '../../scripts/dom';
import { playbackManager } from '../../components/playback/playbackmanager';
import { appRouter } from '../../components/appRouter';
import {
bindEventsToHlsPlayer,
destroyHlsPlayer,
@ -22,10 +22,12 @@ import {
getSavedVolume,
isValidDuration,
getBufferedRanges
} from 'htmlMediaHelper';
import itemHelper from 'itemHelper';
import screenfull from 'screenfull';
import globalize from 'globalize';
} from '../../components/htmlMediaHelper';
import itemHelper from '../../components/itemHelper';
import Screenfull from 'screenfull';
import globalize from '../../scripts/globalize';
import ServerConnections from '../../components/ServerConnections';
import profileBuilder from '../../scripts/browserDeviceProfile';
/* eslint-disable indent */
@ -85,7 +87,7 @@ function tryRemoveElement(elem) {
}
function requireHlsPlayer(callback) {
import('hlsjs').then(({default: hls}) => {
import('hls.js').then(({default: hls}) => {
window.Hls = hls;
callback();
});
@ -139,9 +141,7 @@ function tryRemoveElement(elem) {
}
function getDefaultProfile() {
return import('browserdeviceprofile').then(({default: profileBuilder}) => {
return profileBuilder({});
});
return profileBuilder({});
}
export class HtmlVideoPlayer {
@ -286,7 +286,7 @@ function tryRemoveElement(elem) {
incrementFetchQueue() {
if (this.#fetchQueue <= 0) {
this.isFetching = true;
events.trigger(this, 'beginFetch');
Events.trigger(this, 'beginFetch');
}
this.#fetchQueue++;
@ -300,7 +300,7 @@ function tryRemoveElement(elem) {
if (this.#fetchQueue <= 0) {
this.isFetching = false;
events.trigger(this, 'endFetch');
Events.trigger(this, 'endFetch');
}
}
@ -323,7 +323,7 @@ function tryRemoveElement(elem) {
console.debug(`prefetching hls playlist: ${hlsPlaylistUrl}`);
return window.connectionManager.getApiClient(item.ServerId).ajax({
return ServerConnections.getApiClient(item.ServerId).ajax({
type: 'GET',
url: hlsPlaylistUrl
@ -362,7 +362,7 @@ function tryRemoveElement(elem) {
* @private
*/
setSrcWithFlvJs(elem, options, url) {
return import('flvjs').then(({default: flvjs}) => {
return import('flv.js').then(({default: flvjs}) => {
const flvPlayer = flvjs.createPlayer({
type: 'flv',
url: url
@ -704,8 +704,8 @@ function tryRemoveElement(elem) {
dlg.parentNode.removeChild(dlg);
}
if (screenfull.isEnabled) {
screenfull.exit();
if (Screenfull.isEnabled) {
Screenfull.exit();
} else {
// iOS Safari
if (document.webkitIsFullScreen && document.webkitCancelFullscreen) {
@ -754,7 +754,7 @@ function tryRemoveElement(elem) {
this.updateSubtitleText(timeMs);
}
events.trigger(this, 'timeupdate');
Events.trigger(this, 'timeupdate');
};
/**
@ -767,7 +767,7 @@ function tryRemoveElement(elem) {
*/
const elem = e.target;
saveVolume(elem.volume);
events.trigger(this, 'volumechange');
Events.trigger(this, 'volumechange');
};
/**
@ -826,14 +826,14 @@ function tryRemoveElement(elem) {
this.onStartedAndNavigatedToOsd();
}
}
events.trigger(this, 'playing');
Events.trigger(this, 'playing');
};
/**
* @private
*/
onPlay = () => {
events.trigger(this, 'unpause');
Events.trigger(this, 'unpause');
};
/**
@ -859,25 +859,25 @@ function tryRemoveElement(elem) {
* @private
*/
onClick = () => {
events.trigger(this, 'click');
Events.trigger(this, 'click');
};
/**
* @private
*/
onDblClick = () => {
events.trigger(this, 'dblclick');
Events.trigger(this, 'dblclick');
};
/**
* @private
*/
onPause = () => {
events.trigger(this, 'pause');
Events.trigger(this, 'pause');
};
onWaiting() {
events.trigger(this, 'waiting');
Events.trigger(this, 'waiting');
}
/**
@ -1036,7 +1036,7 @@ function tryRemoveElement(elem) {
// embedded font url
return avaliableFonts.push(i.DeliveryUrl);
});
const apiClient = window.connectionManager.getApiClient(item);
const apiClient = ServerConnections.getApiClient(item);
const fallbackFontList = apiClient.getUrl('/FallbackFont/Fonts', {
api_key: apiClient.accessToken()
});
@ -1064,15 +1064,15 @@ function tryRemoveElement(elem) {
resizeVariation: 0.2,
renderAhead: 90
};
import('JavascriptSubtitlesOctopus').then(({default: SubtitlesOctopus}) => {
apiClient.getNamedConfiguration('encoding').then((config) => {
import('libass-wasm').then(({default: SubtitlesOctopus}) => {
apiClient.getNamedConfiguration('encoding').then(config => {
if (config.EnableFallbackFont) {
apiClient.getJSON(fallbackFontList).then((fontFiles) => {
(fontFiles || []).map(function (font) {
apiClient.getJSON(fallbackFontList).then((fontFiles = []) => {
fontFiles.forEach(font => {
const fontUrl = apiClient.getUrl(`/FallbackFont/Fonts/${font.Name}`, {
api_key: apiClient.accessToken()
});
return avaliableFonts.push(fontUrl);
avaliableFonts.push(fontUrl);
});
this.#currentSubtitlesOctopus = new SubtitlesOctopus(options);
});
@ -1134,7 +1134,7 @@ function tryRemoveElement(elem) {
* @private
*/
setSubtitleAppearance(elem, innerElem) {
Promise.all([import('userSettings'), import('subtitleAppearanceHelper')]).then(([userSettings, subtitleAppearanceHelper]) => {
Promise.all([import('../../scripts/settings/userSettings'), import('../../components/subtitlesettings/subtitleappearancehelper')]).then(([userSettings, subtitleAppearanceHelper]) => {
subtitleAppearanceHelper.applyStyles({
text: innerElem,
window: elem
@ -1155,7 +1155,7 @@ function tryRemoveElement(elem) {
* @private
*/
setCueAppearance() {
Promise.all([import('userSettings'), import('subtitleAppearanceHelper')]).then(([userSettings, subtitleAppearanceHelper]) => {
Promise.all([import('../../scripts/settings/userSettings'), import('../../components/subtitlesettings/subtitleappearancehelper')]).then(([userSettings, subtitleAppearanceHelper]) => {
const elementId = `${this.id}-cuestyle`;
let styleElem = document.querySelector(`#${elementId}`);
@ -1210,7 +1210,7 @@ function tryRemoveElement(elem) {
// download the track json
this.fetchSubtitles(track, item).then(function (data) {
import('userSettings').then((userSettings) => {
import('../../scripts/settings/userSettings').then((userSettings) => {
// show in ui
console.debug(`downloaded ${data.TrackEvents.length} track events`);
@ -1302,7 +1302,7 @@ function tryRemoveElement(elem) {
const dlg = document.querySelector('.videoPlayerContainer');
if (!dlg) {
return import('css!./style').then(() => {
return import('./style.css').then(() => {
loading.show();
const dlg = document.createElement('div');
@ -1539,7 +1539,7 @@ function tryRemoveElement(elem) {
return false;
}
static isAirPlayEnabled() {
isAirPlayEnabled() {
if (document.AirPlayEnabled) {
return !!document.AirplayElement;
}
@ -1581,7 +1581,7 @@ function tryRemoveElement(elem) {
elem.style['-webkit-filter'] = `brightness(${cssValue})`;
elem.style.filter = `brightness(${cssValue})`;
elem.brightnessValue = val;
events.trigger(this, 'brightnesschange');
Events.trigger(this, 'brightnesschange');
}
}

View file

@ -1,5 +1,3 @@
import pluginManager from 'pluginManager';
export default function () {
const self = this;
@ -128,7 +126,7 @@ export default function () {
}
self.show = function () {
import('css!' + pluginManager.mapPath(self, 'style.css')).then(() => {
import('./style.css').then(() => {
let elem = document.querySelector('.logoScreenSaver');
if (!elem) {

View file

@ -1,12 +1,13 @@
import loading from 'loading';
import keyboardnavigation from 'keyboardnavigation';
import dialogHelper from 'dialogHelper';
import dom from 'dom';
import appRouter from 'appRouter';
import events from 'events';
import 'css!./style';
import 'material-icons';
import 'paper-icon-button-light';
import ServerConnections from '../../components/ServerConnections';
import loading from '../../components/loading/loading';
import keyboardnavigation from '../../scripts/keyboardNavigation';
import dialogHelper from '../../components/dialogHelper/dialogHelper';
import dom from '../../scripts/dom';
import { appRouter } from '../../components/appRouter';
import './style.css';
import '../../elements/emby-button/paper-icon-button-light';
import { Events } from 'jellyfin-apiclient';
import { GlobalWorkerOptions, getDocument } from 'pdfjs-dist';
export class PdfPlayer {
constructor() {
@ -186,31 +187,29 @@ export class PdfPlayer {
};
const serverId = item.ServerId;
const apiClient = window.connectionManager.getApiClient(serverId);
const apiClient = ServerConnections.getApiClient(serverId);
return new Promise((resolve, reject) => {
import('pdfjs').then(({default: pdfjs}) => {
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
this.bindEvents();
pdfjs.GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js';
this.bindEvents();
GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js';
const downloadTask = pdfjs.getDocument(downloadHref);
downloadTask.promise.then(book => {
if (this.cancellationToken) return;
this.book = book;
this.loaded = true;
const downloadTask = getDocument(downloadHref);
downloadTask.promise.then(book => {
if (this.cancellationToken) return;
this.book = book;
this.loaded = true;
const percentageTicks = options.startPositionTicks / 10000;
if (percentageTicks !== 0) {
this.loadPage(percentageTicks);
this.progress = percentageTicks;
} else {
this.loadPage(1);
}
const percentageTicks = options.startPositionTicks / 10000;
if (percentageTicks !== 0) {
this.loadPage(percentageTicks);
this.progress = percentageTicks;
} else {
this.loadPage(1);
}
return resolve();
});
return resolve();
});
});
}
@ -266,7 +265,7 @@ export class PdfPlayer {
renderPage(canvas, number) {
this.book.getPage(number).then(page => {
events.trigger(this, 'timeupdate');
Events.trigger(this, 'timeupdate');
const original = page.getViewport({ scale: 1 });
const context = canvas.getContext('2d');

View file

@ -1,3 +1,4 @@
import ServerConnections from '../../components/ServerConnections';
export default class PhotoPlayer {
constructor() {
@ -9,12 +10,12 @@ export default class PhotoPlayer {
play(options) {
return new Promise(function (resolve, reject) {
import('slideshow').then(({default: slideshow}) => {
import('../../components/slideshow/slideshow').then(({default: Slideshow}) => {
const index = options.startIndex || 0;
const apiClient = window.connectionManager.currentApiClient();
const apiClient = ServerConnections.currentApiClient();
apiClient.getCurrentUser().then(function(result) {
const newSlideShow = new slideshow({
const newSlideShow = new Slideshow({
showTitle: false,
cover: false,
items: options.items,

View file

@ -1,9 +1,9 @@
import globalize from 'globalize';
import globalize from '../../scripts/globalize';
import ServerConnections from '../../components/ServerConnections';
import alert from '../../components/alert';
function showErrorMessage() {
return import('alert').then(({default: alert}) => {
return alert(globalize.translate('MessagePlayAccessRestricted'));
});
return alert(globalize.translate('MessagePlayAccessRestricted'));
}
class PlayAccessValidation {
@ -24,7 +24,7 @@ class PlayAccessValidation {
return Promise.resolve();
}
return window.connectionManager.getApiClient(serverId).getCurrentUser().then(function (user) {
return ServerConnections.getApiClient(serverId).getCurrentUser().then(function (user) {
if (user.Policy.EnableMediaPlayback) {
return Promise.resolve();
}

View file

@ -1,6 +1,7 @@
import playbackManager from 'playbackManager';
import events from 'events';
import serverNotifications from 'serverNotifications';
import { playbackManager } from '../../components/playback/playbackmanager';
import { Events } from 'jellyfin-apiclient';
import serverNotifications from '../../scripts/serverNotifications';
import ServerConnections from '../../components/ServerConnections';
function getActivePlayerId() {
const info = playbackManager.getPlayerInfo();
@ -53,10 +54,10 @@ function getCurrentApiClient(instance) {
const currentServerId = instance.currentServerId;
if (currentServerId) {
return window.connectionManager.getApiClient(currentServerId);
return ServerConnections.getApiClient(currentServerId);
}
return window.connectionManager.currentApiClient();
return ServerConnections.currentApiClient();
}
function sendCommandByName(instance, name, options) {
@ -104,7 +105,7 @@ function processUpdatedSessions(instance, sessions, apiClient) {
instance.lastPlayerData = session;
for (let i = 0, length = eventNames.length; i < length; i++) {
events.trigger(instance, eventNames[i], [session]);
Events.trigger(instance, eventNames[i], [session]);
}
} else {
instance.lastPlayerData = session;
@ -186,7 +187,7 @@ class SessionPlayer {
this.isLocalPlayer = false;
this.id = 'remoteplayer';
events.on(serverNotifications, 'Sessions', function (e, apiClient, data) {
Events.on(serverNotifications, 'Sessions', function (e, apiClient, data) {
processUpdatedSessions(self, data, apiClient);
});
}

View file

@ -1,7 +1,7 @@
import events from 'events';
import browser from 'browser';
import appRouter from 'appRouter';
import loading from 'loading';
import { Events } from 'jellyfin-apiclient';
import browser from '../../scripts/browser';
import { appRouter } from '../../components/appRouter';
import loading from '../../components/loading/loading';
/* globals YT */
@ -20,7 +20,7 @@ function createMediaElement(instance, options) {
const dlg = document.querySelector('.youtubePlayerContainer');
if (!dlg) {
import('css!./style').then(() => {
import('./style.css').then(() => {
loading.show();
const dlg = document.createElement('div');
@ -80,7 +80,7 @@ function onEndedInternal(instance) {
src: instance._currentSrc
};
events.trigger(instance, 'stopped', [stopInfo]);
Events.trigger(instance, 'stopped', [stopInfo]);
instance._currentSrc = null;
if (instance.currentYoutubePlayer) {
@ -95,7 +95,7 @@ function onPlayerReady(event) {
}
function onTimeUpdate(e) {
events.trigger(this, 'timeupdate');
Events.trigger(this, 'timeupdate');
}
function onPlaying(instance, playOptions, resolve) {
@ -114,68 +114,64 @@ function onPlaying(instance, playOptions, resolve) {
instance.videoDialog.classList.remove('onTop');
}
import('loading').then(({default: loading}) => {
loading.hide();
});
loading.hide();
}
}
function setCurrentSrc(instance, elem, options) {
return new Promise(function (resolve, reject) {
import('queryString').then(({default: queryString}) => {
instance._currentSrc = options.url;
const 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');
}
instance._currentSrc = options.url;
const params = new URLSearchParams(options.url.split('?')[1]); /* eslint-disable-line compat/compat */
// 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.get('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
}
});
let resizeListener = instance.resizeListener;
if (resizeListener) {
window.removeEventListener('resize', resizeListener);
window.addEventListener('resize', resizeListener);
} else {
resizeListener = instance.resizeListener = onVideoResize.bind(instance);
window.addEventListener('resize', resizeListener);
},
playerVars: {
controls: 0,
enablejsapi: 1,
modestbranding: 1,
rel: 0,
showinfo: 0,
fs: 0,
playsinline: 1
}
window.removeEventListener('orientationChange', resizeListener);
window.addEventListener('orientationChange', resizeListener);
};
});
if (!window.YT) {
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
let resizeListener = instance.resizeListener;
if (resizeListener) {
window.removeEventListener('resize', resizeListener);
window.addEventListener('resize', resizeListener);
} else {
window.onYouTubeIframeAPIReady();
resizeListener = instance.resizeListener = onVideoResize.bind(instance);
window.addEventListener('resize', resizeListener);
}
});
window.removeEventListener('orientationChange', resizeListener);
window.addEventListener('orientationChange', resizeListener);
};
if (!window.YT) {
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
} else {
window.onYouTubeIframeAPIReady();
}
});
}
@ -278,7 +274,7 @@ class YoutubePlayer {
// This needs a delay before the youtube player will report the correct player state
setTimeout(function () {
events.trigger(instance, 'pause');
Events.trigger(instance, 'pause');
}, 200);
}
}
@ -292,7 +288,7 @@ class YoutubePlayer {
// This needs a delay before the youtube player will report the correct player state
setTimeout(function () {
events.trigger(instance, 'unpause');
Events.trigger(instance, 'unpause');
}, 200);
}
}