mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2026-10-26 16:45:18 +00:00
Cleanup global variables (#8544)
* Mark all globals readonly * Remove legacy Windows UWP app global * Remove unused globals * Remove Events global * Remove jQuery global * Remove Hls global * Fix player source removal * Fix es5 compat import * Remove jQuery ($) global
This commit is contained in:
11 files changed
+118
-174
No files matched your search
+12
-19
@@ -207,29 +207,22 @@ export default tseslint.config(
|
||||
},
|
||||
globals: {
|
||||
...globals.browser,
|
||||
// Chromecast globals
|
||||
'chrome': 'readonly',
|
||||
// Tizen globals
|
||||
'tizen': false,
|
||||
'webapis': false,
|
||||
'tizen': 'readonly',
|
||||
'webapis': 'readonly',
|
||||
// WebOS globals
|
||||
'webOS': false,
|
||||
// Dependency globals
|
||||
'$': false,
|
||||
'jQuery': false,
|
||||
'webOS': 'readonly',
|
||||
// Jellyfin globals
|
||||
'ApiClient': true,
|
||||
'Events': true,
|
||||
'chrome': true,
|
||||
'Emby': false,
|
||||
'Hls': true,
|
||||
'LibraryMenu': true,
|
||||
'Windows': false,
|
||||
'ApiClient': 'readonly',
|
||||
// Build time definitions
|
||||
__COMMIT_SHA__: false,
|
||||
__JF_BUILD_VERSION__: false,
|
||||
__PACKAGE_JSON_NAME__: false,
|
||||
__PACKAGE_JSON_VERSION__: false,
|
||||
__USE_SYSTEM_FONTS__: false,
|
||||
__WEBPACK_SERVE__: false
|
||||
__COMMIT_SHA__: 'readonly',
|
||||
__JF_BUILD_VERSION__: 'readonly',
|
||||
__PACKAGE_JSON_NAME__: 'readonly',
|
||||
__PACKAGE_JSON_VERSION__: 'readonly',
|
||||
__USE_SYSTEM_FONTS__: 'readonly',
|
||||
__WEBPACK_SERVE__: 'readonly'
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import appSettings from '../scripts/settings/appSettings' ;
|
||||
import { Events as HlsEvents, ErrorTypes as HlsErrorTypes } from 'hls.js/dist/hls.js';
|
||||
|
||||
import appSettings from '../scripts/settings/appSettings';
|
||||
import browser from '../scripts/browser';
|
||||
import Events from '../utils/events.ts';
|
||||
import { MediaError } from 'types/mediaError';
|
||||
@@ -167,24 +169,6 @@ export function seekOnPlaybackStart(instance, element, ticks, onMediaReady) {
|
||||
}
|
||||
}
|
||||
|
||||
export function applySrc(elem, src, options) {
|
||||
if (window.Windows && options.mediaSource?.IsLocal) {
|
||||
return Windows.Storage.StorageFile.getFileFromPathAsync(options.url).then(function (file) {
|
||||
const playlist = new Windows.Media.Playback.MediaPlaybackList();
|
||||
|
||||
const source1 = Windows.Media.Core.MediaSource.createFromStorageFile(file);
|
||||
const startTime = (options.playerStartPositionTicks || 0) / 10000;
|
||||
playlist.items.append(new Windows.Media.Playback.MediaPlaybackItem(source1, startTime));
|
||||
elem.src = URL.createObjectURL(playlist, { oneTimeOnly: true });
|
||||
return Promise.resolve();
|
||||
});
|
||||
} else {
|
||||
elem.src = src;
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function resetSrc(elem) {
|
||||
elem.src = '';
|
||||
elem.innerHTML = '';
|
||||
@@ -260,7 +244,7 @@ export function destroyFlvPlayer(instance) {
|
||||
}
|
||||
|
||||
export function bindEventsToHlsPlayer(instance, hls, elem, onErrorFn, resolve, reject) {
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, function () {
|
||||
hls.on(HlsEvents.MANIFEST_PARSED, function () {
|
||||
playWithPromise(elem, onErrorFn).then(resolve, function () {
|
||||
if (reject) {
|
||||
reject();
|
||||
@@ -269,11 +253,11 @@ export function bindEventsToHlsPlayer(instance, hls, elem, onErrorFn, resolve, r
|
||||
});
|
||||
});
|
||||
|
||||
hls.on(Hls.Events.ERROR, function (event, data) {
|
||||
hls.on(HlsEvents.ERROR, function (event, data) {
|
||||
console.error('HLS Error: Type: ' + data.type + ' Details: ' + (data.details || '') + ' Fatal: ' + (data.fatal || false));
|
||||
|
||||
// try to recover network error
|
||||
if (data.type === Hls.ErrorTypes.NETWORK_ERROR
|
||||
if (data.type === HlsErrorTypes.NETWORK_ERROR
|
||||
&& data.response?.code && data.response.code >= 400
|
||||
) {
|
||||
console.debug('hls.js response error code: ' + data.response.code);
|
||||
@@ -293,7 +277,7 @@ export function bindEventsToHlsPlayer(instance, hls, elem, onErrorFn, resolve, r
|
||||
|
||||
if (data.fatal) {
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
case HlsErrorTypes.NETWORK_ERROR:
|
||||
|
||||
if (data.response && data.response.code === 0) {
|
||||
// This could be a CORS error related to access control response headers
|
||||
@@ -315,7 +299,7 @@ export function bindEventsToHlsPlayer(instance, hls, elem, onErrorFn, resolve, r
|
||||
}
|
||||
|
||||
break;
|
||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||
case HlsErrorTypes.MEDIA_ERROR:
|
||||
console.debug('fatal media error encountered, try to recover');
|
||||
handleHlsJsMediaError(instance, reject);
|
||||
reject = null;
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { OutboundWebSocketMessageType } from '@jellyfin/sdk/lib/websocket';
|
||||
import loading from '../loading/loading';
|
||||
import dom from '../../utils/dom';
|
||||
|
||||
import loading from 'components/loading/loading';
|
||||
import toast from 'components/toast/toast';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import dom from 'utils/dom';
|
||||
import Events from 'utils/events';
|
||||
|
||||
import recordingHelper from './recordinghelper';
|
||||
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import './recordingfields.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import toast from '../toast/toast';
|
||||
import template from './recordingfields.template.html';
|
||||
|
||||
import 'elements/emby-button/emby-button';
|
||||
import 'elements/emby-button/paper-icon-button-light';
|
||||
import './recordingfields.scss';
|
||||
import 'styles/flexstyles.scss';
|
||||
|
||||
function loadData(parent, program) {
|
||||
if (program.IsSeries) {
|
||||
parent.querySelector('.recordSeriesContainer').classList.remove('hide');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'jquery';
|
||||
import $ from 'jquery';
|
||||
|
||||
import loading from '../loading/loading';
|
||||
import globalize from '../../lib/globalize';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'jquery';
|
||||
import $ from 'jquery';
|
||||
|
||||
import loading from '../loading/loading';
|
||||
import globalize from '../../lib/globalize';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import $ from 'jquery';
|
||||
import { importModule } from '@uupaa/dynamic-import-polyfill';
|
||||
|
||||
import Dashboard from 'utils/dashboard';
|
||||
|
||||
import './viewManager/viewContainer.scss';
|
||||
import Dashboard from '../utils/dashboard';
|
||||
|
||||
const getMainAnimatedPages = () => {
|
||||
return document.querySelector('.mainAnimatedPages');
|
||||
@@ -65,13 +68,13 @@ export function loadView(options) {
|
||||
}
|
||||
|
||||
if (currentPage) {
|
||||
if (newViewInfo.hasScript && window.$) {
|
||||
if (newViewInfo.hasScript) {
|
||||
mainAnimatedPages.removeChild(currentPage);
|
||||
view = $(view).appendTo(mainAnimatedPages)[0];
|
||||
} else {
|
||||
mainAnimatedPages.replaceChild(view, currentPage);
|
||||
}
|
||||
} else if (newViewInfo.hasScript && window.$) {
|
||||
} else if (newViewInfo.hasScript) {
|
||||
view = $(view).appendTo(mainAnimatedPages)[0];
|
||||
} else {
|
||||
mainAnimatedPages.appendChild(view);
|
||||
@@ -109,11 +112,6 @@ export function loadView(options) {
|
||||
afterAnimate(allPages, pageIndex);
|
||||
}
|
||||
|
||||
if (window.$) {
|
||||
$.mobile = $.mobile || {};
|
||||
$.mobile.activePage = view;
|
||||
}
|
||||
|
||||
return view;
|
||||
});
|
||||
}
|
||||
@@ -213,11 +211,6 @@ export function tryRestoreView(options) {
|
||||
afterAnimate(allPages, index);
|
||||
}
|
||||
|
||||
if (window.$) {
|
||||
$.mobile = $.mobile || {};
|
||||
$.mobile.activePage = view;
|
||||
}
|
||||
|
||||
return view;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { OutboundWebSocketMessageType } from '@jellyfin/sdk/lib/websocket';
|
||||
import 'webcomponents.js/webcomponents-lite';
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
import itemShortcuts from '../../components/shortcuts';
|
||||
import inputManager from '../../scripts/inputManager';
|
||||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import browser from '../../scripts/browser';
|
||||
import dom from '../../utils/dom';
|
||||
import loading from '../../components/loading/loading';
|
||||
import focusManager from '../../components/focusManager';
|
||||
import focusManager from 'components/focusManager';
|
||||
import imageLoader from 'components/images/imageLoader';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import loading from 'components/loading/loading';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import itemShortcuts from 'components/shortcuts';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { OutboundWebSocketMessageType } from '@jellyfin/sdk/lib/websocket';
|
||||
import browser from 'scripts/browser';
|
||||
import inputManager from 'scripts/inputManager';
|
||||
import dom from 'utils/dom';
|
||||
import Events from 'utils/events';
|
||||
|
||||
const ItemsContainerPrototype = Object.create(HTMLDivElement.prototype);
|
||||
|
||||
|
||||
@@ -50,13 +50,12 @@ function supportsFade() {
|
||||
return !browser.tv;
|
||||
}
|
||||
|
||||
function requireHlsPlayer(callback) {
|
||||
import('hls.js/dist/hls.js').then(({ default: hls }) => {
|
||||
function requireHlsPlayer() {
|
||||
return import('hls.js/dist/hls.js').then(({ default: hls }) => {
|
||||
hls.DefaultConfig.lowLatencyMode = false;
|
||||
hls.DefaultConfig.backBufferLength = Infinity;
|
||||
hls.DefaultConfig.liveBackBufferLength = 90;
|
||||
window.Hls = hls;
|
||||
callback();
|
||||
return hls;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -174,26 +173,25 @@ class HtmlAudioPlayer {
|
||||
navigator.audioSession.type = 'playback';
|
||||
}
|
||||
|
||||
return enableHlsPlayer(val, options.item, options.mediaSource, 'Audio').then(function () {
|
||||
return enableHlsPlayer(val, options.item, options.mediaSource, 'Audio').then(async function () {
|
||||
const Hls = await requireHlsPlayer();
|
||||
const includeCorsCredentials = await getIncludeCorsCredentials();
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
requireHlsPlayer(async () => {
|
||||
const includeCorsCredentials = await getIncludeCorsCredentials();
|
||||
|
||||
const hls = new Hls({
|
||||
manifestLoadingTimeOut: 20000,
|
||||
xhrSetup: function (xhr) {
|
||||
xhr.withCredentials = includeCorsCredentials;
|
||||
}
|
||||
});
|
||||
hls.loadSource(val);
|
||||
hls.attachMedia(elem);
|
||||
|
||||
htmlMediaHelper.bindEventsToHlsPlayer(self, hls, elem, onError, resolve, reject);
|
||||
|
||||
self._hlsPlayer = hls;
|
||||
|
||||
self._currentSrc = val;
|
||||
const hls = new Hls({
|
||||
manifestLoadingTimeOut: 20000,
|
||||
xhrSetup: function (xhr) {
|
||||
xhr.withCredentials = includeCorsCredentials;
|
||||
}
|
||||
});
|
||||
hls.loadSource(val);
|
||||
hls.attachMedia(elem);
|
||||
|
||||
htmlMediaHelper.bindEventsToHlsPlayer(self, hls, elem, onError, resolve, reject);
|
||||
|
||||
self._hlsPlayer = hls;
|
||||
|
||||
self._currentSrc = val;
|
||||
});
|
||||
}, async () => {
|
||||
elem.autoplay = true;
|
||||
@@ -204,11 +202,9 @@ class HtmlAudioPlayer {
|
||||
elem.crossOrigin = 'use-credentials';
|
||||
}
|
||||
|
||||
return htmlMediaHelper.applySrc(elem, val, options).then(function () {
|
||||
self._currentSrc = val;
|
||||
|
||||
return htmlMediaHelper.playWithPromise(elem, onError);
|
||||
});
|
||||
elem.src = val;
|
||||
self._currentSrc = val;
|
||||
return htmlMediaHelper.playWithPromise(elem, onError);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
destroyCastPlayer,
|
||||
getCrossOriginValue,
|
||||
enableHlsJsPlayerForCodecs,
|
||||
applySrc,
|
||||
resetSrc,
|
||||
playWithPromise,
|
||||
onEndedInternal,
|
||||
@@ -119,13 +118,12 @@ function enableNativeTrackSupport(mediaSource, track) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function requireHlsPlayer(callback) {
|
||||
import('hls.js/dist/hls.js').then(({ default: hls }) => {
|
||||
function requireHlsPlayer() {
|
||||
return import('hls.js/dist/hls.js').then(({ default: hls }) => {
|
||||
hls.DefaultConfig.lowLatencyMode = false;
|
||||
hls.DefaultConfig.backBufferLength = Infinity;
|
||||
hls.DefaultConfig.liveBackBufferLength = 90;
|
||||
window.Hls = hls;
|
||||
callback();
|
||||
return hls;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -549,41 +547,40 @@ export class HtmlVideoPlayer {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setSrcWithHlsJs(elem, options, url) {
|
||||
async setSrcWithHlsJs(elem, options, url) {
|
||||
const Hls = await requireHlsPlayer();
|
||||
const includeCorsCredentials = await getIncludeCorsCredentials();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
requireHlsPlayer(async () => {
|
||||
let maxBufferLength = 30;
|
||||
let maxBufferLength = 30;
|
||||
|
||||
// Some browsers cannot handle huge fragments in high bitrate.
|
||||
// This issue usually happens when using HWA encoders with a high bitrate setting.
|
||||
// Limit the BufferLength to 6s, it works fine when playing 4k 120Mbps over HLS on chrome.
|
||||
// https://github.com/video-dev/hls.js/issues/876
|
||||
if ((browser.chrome || browser.edgeChromium || browser.firefox) && playbackManager.getMaxStreamingBitrate(this) >= 25000000) {
|
||||
maxBufferLength = 6;
|
||||
// Some browsers cannot handle huge fragments in high bitrate.
|
||||
// This issue usually happens when using HWA encoders with a high bitrate setting.
|
||||
// Limit the BufferLength to 6s, it works fine when playing 4k 120Mbps over HLS on chrome.
|
||||
// https://github.com/video-dev/hls.js/issues/876
|
||||
if ((browser.chrome || browser.edgeChromium || browser.firefox) && playbackManager.getMaxStreamingBitrate(this) >= 25000000) {
|
||||
maxBufferLength = 6;
|
||||
}
|
||||
|
||||
const hls = new Hls({
|
||||
startPosition: options.playerStartPositionTicks / 10000000,
|
||||
manifestLoadingTimeOut: 20000,
|
||||
maxBufferLength,
|
||||
maxMaxBufferLength: maxBufferLength,
|
||||
videoPreference: { preferHDR: true },
|
||||
xhrSetup(xhr) {
|
||||
xhr.withCredentials = includeCorsCredentials;
|
||||
}
|
||||
|
||||
const includeCorsCredentials = await getIncludeCorsCredentials();
|
||||
|
||||
const hls = new Hls({
|
||||
startPosition: options.playerStartPositionTicks / 10000000,
|
||||
manifestLoadingTimeOut: 20000,
|
||||
maxBufferLength: maxBufferLength,
|
||||
maxMaxBufferLength: maxBufferLength,
|
||||
videoPreference: { preferHDR: true },
|
||||
xhrSetup(xhr) {
|
||||
xhr.withCredentials = includeCorsCredentials;
|
||||
}
|
||||
});
|
||||
hls.loadSource(url);
|
||||
hls.attachMedia(elem);
|
||||
|
||||
bindEventsToHlsPlayer(this, hls, elem, this.onError, resolve, reject);
|
||||
|
||||
this._hlsPlayer = hls;
|
||||
|
||||
// This is needed in setCurrentTrackElement
|
||||
this.#currentSrc = url;
|
||||
});
|
||||
hls.loadSource(url);
|
||||
hls.attachMedia(elem);
|
||||
|
||||
bindEventsToHlsPlayer(this, hls, elem, this.onError, resolve, reject);
|
||||
|
||||
this._hlsPlayer = hls;
|
||||
|
||||
// This is needed in setCurrentTrackElement
|
||||
this.#currentSrc = url;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -657,11 +654,9 @@ export class HtmlVideoPlayer {
|
||||
elem.crossOrigin = 'use-credentials';
|
||||
}
|
||||
|
||||
return applySrc(elem, val, options).then(() => {
|
||||
this.#currentSrc = val;
|
||||
|
||||
return playWithPromise(elem, this.onError);
|
||||
});
|
||||
elem.src = val;
|
||||
this.#currentSrc = val;
|
||||
return playWithPromise(elem, this.onError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1328,25 +1323,10 @@ export class HtmlVideoPlayer {
|
||||
this.#currentBitmapSubRenderer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
fetchSubtitlesUwp(track) {
|
||||
return Windows.Storage.StorageFile.getFileFromPathAsync(track.Path).then(function (storageFile) {
|
||||
return Windows.Storage.FileIO.readTextAsync(storageFile);
|
||||
}).then(function (text) {
|
||||
return JSON.parse(text);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
async fetchSubtitles(track, item) {
|
||||
if (window.Windows && itemHelper.isLocalItem(item)) {
|
||||
return this.fetchSubtitlesUwp(track, item);
|
||||
}
|
||||
|
||||
this.incrementFetchQueue();
|
||||
try {
|
||||
const response = await fetch(getTextTrackUrl(track, item, '.js'));
|
||||
@@ -1956,10 +1936,6 @@ export class HtmlVideoPlayer {
|
||||
if (
|
||||
// Check non-standard Safari PiP support
|
||||
typeof video.webkitSupportsPresentationMode === 'function' && video.webkitSupportsPresentationMode('picture-in-picture') && typeof video.webkitSetPresentationMode === 'function'
|
||||
// Check non-standard Windows PiP support
|
||||
|| (window.Windows
|
||||
&& Windows.UI.ViewManagement.ApplicationView.getForCurrentView()
|
||||
.isViewModeSupported(Windows.UI.ViewManagement.ApplicationViewMode.compactOverlay))
|
||||
// Check standard PiP support
|
||||
|| document.pictureInPictureEnabled
|
||||
) {
|
||||
@@ -2043,13 +2019,6 @@ export class HtmlVideoPlayer {
|
||||
document.exitPictureInPicture().catch(HtmlVideoPlayer.onPictureInPictureError);
|
||||
}
|
||||
}
|
||||
} else if (window.Windows) {
|
||||
this.isPip = isEnabled;
|
||||
if (isEnabled) {
|
||||
Windows.UI.ViewManagement.ApplicationView.getForCurrentView().tryEnterViewModeAsync(Windows.UI.ViewManagement.ApplicationViewMode.compactOverlay);
|
||||
} else {
|
||||
Windows.UI.ViewManagement.ApplicationView.getForCurrentView().tryEnterViewModeAsync(Windows.UI.ViewManagement.ApplicationViewMode.default);
|
||||
}
|
||||
} else if (video?.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === 'function') {
|
||||
video.webkitSetPresentationMode(isEnabled ? 'picture-in-picture' : 'inline');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
import 'jquery';
|
||||
import $ from 'jquery';
|
||||
import 'material-design-icons-iconfont';
|
||||
|
||||
import globalize from 'lib/globalize';
|
||||
@@ -281,7 +281,7 @@ function updateEditorNode(page, item) {
|
||||
$('.editorNode', elem).remove();
|
||||
$(elem).append(getNodeInnerHtml(item));
|
||||
if (item.IsFolder) {
|
||||
const tree = jQuery.jstree._reference('.libraryTree');
|
||||
const tree = $.jstree._reference('.libraryTree');
|
||||
const currentNode = tree._get_node(null, false);
|
||||
tree.refresh(currentNode);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
import globalize from 'lib/globalize';
|
||||
|
||||
export function showLayoutMenu (button, currentLayout, views) {
|
||||
@@ -30,7 +32,7 @@ export function showLayoutMenu (button, currentLayout, views) {
|
||||
cancelable: false
|
||||
}));
|
||||
|
||||
if (!dispatchEvent && window.$) {
|
||||
if (!dispatchEvent) {
|
||||
$(button).trigger('layoutchange', [id]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user