EmbyCrackedClient/web/app.js

2722 lines
82 KiB
JavaScript
Raw Permalink Normal View History

2025-06-25 11:46:04 +08:00
/* global define */
/* jshint module: true */
// closure start
const appMode = globalThis.appMode;
const isNativeTizen = globalThis.appMode === 'tizen';
const isNativeLG = globalThis.appMode === 'webos';
let customPaths;
function returnFirstDependency(obj) {
return obj;
}
function returnFirstDependencyDefault(obj) {
if (Array.isArray(obj)) {
obj = obj[0];
}
// this ? check is needed for importing modules without exports and babel-transpiled code to requirejs
return obj?.default || obj;
}
function loadSharedComponentsDictionary(globalize) {
const baseUrl = 'modules/common/strings/';
const languages = [
'ar',
'bg-BG',
'ca',
'cs',
'da',
'de',
'el',
'en-GB',
'en-US',
'es',
'es-AR',
'es-MX',
'et-EE',
'fi',
'fr',
'fr-CA',
'gsw',
'he',
'hr',
'hu',
'id',
'it',
'ja',
'kk',
'ko',
'lt-LT',
'ms',
'nb',
'nl',
'pl',
'pt-BR',
'pt-PT',
'ro',
'ru',
'sk',
'sl-SI',
'sv',
'tr',
'uk',
'vi',
'zh-CN',
'zh-HK',
'zh-TW'
];
const translations = languages.map(function (i) {
return {
lang: i,
path: baseUrl + i + '.json'
};
});
return globalize.loadStrings({
name: 'sharedcomponents',
translations: translations
});
}
function isGamepadSupported() {
return 'ongamepadconnected' in window || navigator.getGamepads || navigator.webkitGetGamepads;
}
function enableNativeGamepadKeyMapping() {
// On Windows UWP, this will tell the platform to make the gamepad emit normal keyboard events
if (window.navigator && typeof window.navigator.gamepadInputEmulation === "string") {
// We want the gamepad to provide gamepad VK keyboard events rather than moving a
// mouse like cursor. Set to "keyboard", the gamepad will provide such keyboard events
// and provide input to the DOM navigator.getGamepads API.
window.navigator.gamepadInputEmulation = "keyboard";
return true;
}
return false;
}
function loadPlugin(url) {
return Promise.all([
importFromPath('./modules/common/pluginmanager.js')
]).then(function (responses) {
const pluginManager = responses[0];
if (url.startsWith('./') && url.endsWith('.js')) {
console.log('Loading plugin module: ' + url);
return getDynamicImport(url)().then(function (f) {
return pluginManager.loadPlugin(f, url);
});
}
return pluginManager.loadPluginFromUrl(url);
});
}
function getAvailablePluginsAppStore() {
const promises = [
this._getAvailablePlugins(),
importFromPath('./modules/common/pluginmanager.js')
];
return Promise.all(promises).then(function (responses) {
const plugins = responses[0];
const pluginManager = responses[1];
return plugins.filter(function (p) {
return pluginManager.allowPluginPages(p.guid);
});
});
}
function returnFalse() {
return false;
}
function onApiClientCreated(e, apiClient) {
if (appMode === 'ios' || appMode === 'android') {
apiClient._getAvailablePlugins = apiClient.getAvailablePlugins;
apiClient.getAvailablePlugins = getAvailablePluginsAppStore.bind(apiClient);
}
Promise.all([
importFromPath('./modules/browser.js')
]).then(function (responses) {
const browser = responses[0];
// replace this, not reliable
if (browser.operaTv) {
apiClient.isWebSocketSupported = returnFalse;
}
});
}
function getServerAddressFromWindowUrl() {
// Try to get the server address from the browser url
// This will preserve protocol, hostname, port and subdirectory
const urlLower = window.location.href.toLowerCase();
const index = urlLower.lastIndexOf('/web');
if (index !== -1) {
return urlLower.substring(0, index);
}
// If the above failed, just piece it together manually
const loc = window.location;
let address = loc.protocol + '//' + loc.hostname;
if (loc.port) {
address += ':' + loc.port;
}
return address;
}
function createConnectionManager() {
return Promise.all([
importFromPath('./modules/emby-apiclient/connectionmanager.js'),
importFromPath('./modules/emby-apiclient/events.js'),
importFromPath('./modules/common/servicelocator.js')
]).then(function (outerResponses) {
const connectionManager = outerResponses[0];
const events = outerResponses[1];
const serviceLocator = outerResponses[2];
const appHost = serviceLocator.appHost;
// TODO: This needs to go. Shouldn't be needed globally anymore
globalThis.Events = events;
connectionManager.globalScopeApiClient = true;
connectionManager.devicePixelRatio = globalThis.devicePixelRatio;
// This is solely for the native apps to call connectionManager methods from native code
// client-side code in js files should not use the global scope object
globalThis.ConnectionManager = connectionManager;
events.on(connectionManager, 'apiclientcreated', onApiClientCreated);
if (!appHost.supports('multiserver')) {
connectionManager.enableServerAddressValidation = false;
let accessToken;
let userId;
const windowSearch = window.location.search;
if (windowSearch) {
const params = new URLSearchParams(window.location.search);
accessToken = params.get('accessToken');
userId = params.get('userId');
if (!accessToken || !userId || params.get('e') !== '1') {
accessToken = null;
userId = null;
}
}
console.log('creating ApiClient singleton');
connectionManager.validateServerIds = false;
const serverAddress = getServerAddressFromWindowUrl();
const apiClient = connectionManager.getApiClientFromServerInfo({
ManualAddress: serverAddress,
ManualAddressOnly: true,
IsLocalServer: true,
AccessToken: accessToken,
UserId: userId
}, serverAddress);
if (accessToken && userId) {
window.location = 'index.html';
}
apiClient.enableAutomaticNetworking = false;
console.log('loaded ApiClient singleton');
}
});
}
function getPluginPageContentPath() {
return globalThis.ApiClient ? globalThis.ApiClient.getUrl('web/ConfigurationPage') : null;
}
function defineCoreRoutes(appRouter, appHost, browser) {
console.log('Defining core routes');
appRouter.addRoute({
path: '/startup/connectlogin.html',
controller: 'startup/connectlogin.js',
controllerType: 'module',
anonymous: true,
startup: true,
defaultTitle: true,
clearBackdrop: true,
autoFocus: false,
adjustHeaderForEmbeddedScroll: true,
drawer: false
});
appRouter.addRoute({
path: '/startup/connectsignup.html',
controller: 'startup/connectsignup.js',
controllerType: 'module',
anonymous: true,
startup: true,
defaultTitle: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
drawer: false
});
appRouter.addRoute({
path: '/startup/welcome.html',
controller: 'startup/welcome.js',
controllerType: 'module',
anonymous: true,
startup: true,
defaultTitle: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
drawer: false
});
appRouter.addRoute({
path: '/startup/forgotpassword.html',
controller: 'startup/forgotpassword.js',
controllerType: 'module',
anonymous: true,
startup: true,
defaultTitle: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
drawer: false
});
appRouter.addRoute({
path: '/startup/forgotpasswordpin.html',
controller: 'startup/forgotpasswordpin.js',
controllerType: 'module',
anonymous: true,
startup: true,
defaultTitle: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
drawer: false
});
appRouter.addRoute({
path: '/home',
contentPath: '/home/home.html',
type: 'home',
defaultTitle: true,
controller: 'home/home.js',
controllerType: 'module',
autoFocus: false,
homeButton: false,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true,
enableMediaControlTV: true
});
// legacy
appRouter.addRoute({
path: '/home/home.html',
contentPath: '/home/home.html',
type: 'home',
defaultTitle: true,
controller: 'home/home.js',
controllerType: 'module',
autoFocus: false,
homeButton: false,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true,
enableMediaControlTV: true
});
// legacy
appRouter.addRoute({
path: '/home.html',
contentPath: '/home/home.html',
type: 'home',
defaultTitle: true,
controller: 'home/home.js',
controllerType: 'module',
autoFocus: false,
homeButton: false,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true,
enableMediaControlTV: true
});
appRouter.addRoute({
path: '/home_horiz/home.html',
type: 'home',
defaultTitle: true,
controller: 'home_horiz/home.js',
controllerType: 'module',
headerTabs: true,
autoFocus: false,
homeButton: false,
headerBackground: false,
clearBackdrop: true,
enableMediaControlTV: true
});
appRouter.addRoute({
path: '/list/list.html',
controller: 'list/list.js',
controllerType: 'module',
autoFocus: false,
canRefresh: true,
adjustHeaderForEmbeddedScroll: true,
supportsThemeMedia: true
});
appRouter.addRoute({
contentPath: '/item/item.html',
path: '/item',
autoFocus: false,
controller: 'item/item.js',
controllerType: 'module',
transparentHeader: true,
adjustHeaderForEmbeddedScroll: true,
supportsThemeMedia: true,
transition: true
});
// legacy
appRouter.addRoute({
contentPath: '/item/item.html',
path: '/item/item.html',
controller: 'item/item.js',
controllerType: 'module',
autoFocus: false,
transparentHeader: true,
adjustHeaderForEmbeddedScroll: true,
supportsThemeMedia: true,
transition: true
});
appRouter.addRoute({
contentPath: '/livetv/livetv.html',
path: '/livetv',
controller: 'livetv/livetv.js',
title: 'LiveTV',
controllerType: 'module',
autoFocus: false,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true,
enableMediaControlTV: true
});
appRouter.addRoute({
path: '/startup/login.html',
contentPath: '/list/list.html',
controller: 'startup/login.js',
controllerType: 'module',
anonymous: true,
autoFocus: false,
startup: true,
defaultTitle: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
drawer: false
});
appRouter.addRoute({
path: '/startup/manuallogin.html',
controller: 'startup/manuallogin.js',
controllerType: 'module',
anonymous: true,
startup: true,
defaultTitle: true,
autoFocus: false,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
drawer: false
});
appRouter.addRoute({
contentPath: '/games/games.html',
path: '/games',
controller: 'games/games.js',
controllerType: 'module',
autoFocus: false,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
contentPath: '/videos/videos.html',
path: '/videos',
controller: 'videos/videos.js',
controllerType: 'module',
autoFocus: false,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
contentPath: '/music/music.html',
path: '/music',
controller: 'music/music.js',
controllerType: 'module',
autoFocus: false,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true,
enableMediaControlTV: true
});
appRouter.addRoute({
path: '/videoosd/videoosd.html',
controller: 'videoosd/videoosd.js',
controllerType: 'module',
type: 'video-osd',
supportsThemeMedia: true,
// the page has it's own
enableMediaControl: false,
autoFocus: false,
headerBackground: false,
homeButton: false,
drawer: false,
dockedTabs: false,
backButton: true,
transparentHeader: true,
// for offline playback
anonymous: true
});
appRouter.addRoute({
contentPath: '/settings/settings.html',
path: '/settings',
controller: 'settings/settings.js',
controllerType: 'module',
title: 'Settings',
autoFocus: false,
clearBackdrop: true,
settingsTheme: true,
drawer: false,
adjustHeaderForEmbeddedScroll: true
});
if (appHost.supports('keyboardsettings')) {
appRouter.addRoute({
path: '/settings/keyboard.html',
controller: 'settings/keyboard.js',
controllerType: 'module',
type: 'settings',
title: 'HeaderKeyboardAndRemote',
thumbImage: '',
order: 2,
icon: '',
clearBackdrop: true,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
autoFocus: false
});
}
if (appHost.supports('applogger')) {
appRouter.addRoute({
path: '/applogs',
contentPath: '/logs/logs.html',
controller: 'logs/logs.js',
autoFocus: false,
controllerType: 'module',
type: 'settings',
icon: 'folder_open',
clearBackdrop: true,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
navMenuId: 'applogs',
title: 'Logs'
});
appRouter.addRoute({
path: '/applog',
contentPath: '/list/list.html',
autoFocus: false,
controller: 'logs/log.js',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
canRefresh: true,
navMenuId: 'applogs',
title: 'Logs'
});
}
appRouter.addRoute({
path: '/settings/notifications.html',
contentPath: '/list/list.html',
controller: 'settings/notifications.js',
controllerType: 'module',
type: 'settings',
title: 'Notifications',
category: 'Playback',
thumbImage: '',
order: 1001,
icon: '',
clearBackdrop: true,
settingsTheme: true,
settingsType: 'user',
adjustHeaderForEmbeddedScroll: true,
hideDrawerWithOtherUserIdParam: true,
featureId: 'notifications',
minServerVersion: '4.8.0.20'
});
appRouter.addRoute({
path: '/settings/playback.html',
controller: 'settings/playback.js',
controllerType: 'module',
type: 'settings',
title: 'Playback',
category: 'Playback',
thumbImage: '',
order: 2,
icon: '',
clearBackdrop: true,
settingsTheme: true,
settingsType: 'user',
adjustHeaderForEmbeddedScroll: true,
hideDrawerWithOtherUserIdParam: true
});
appRouter.addRoute({
path: '/settings/subtitles.html',
controller: 'settings/subtitles.js',
controllerType: 'module',
type: 'settings',
title: 'Subtitles',
category: 'Playback',
thumbImage: '',
order: 3,
icon: '',
clearBackdrop: true,
settingsTheme: true,
settingsType: 'user',
adjustHeaderForEmbeddedScroll: true,
hideDrawerWithOtherUserIdParam: true
});
appRouter.addRoute({
path: '/settings/display.html',
controller: 'settings/display.js',
controllerType: 'module',
type: 'settings',
title: 'Display',
category: 'General',
thumbImage: '',
order: 0,
icon: '',
clearBackdrop: true,
settingsTheme: true,
settingsType: 'user',
adjustHeaderForEmbeddedScroll: true,
hideDrawerWithOtherUserIdParam: true
});
appRouter.addRoute({
path: '/settings/homescreen.html',
controller: 'settings/homescreen.js',
controllerType: 'module',
type: 'settings',
title: 'HeaderHomeScreen',
category: 'General',
thumbImage: '',
order: 1,
icon: '',
clearBackdrop: true,
settingsTheme: true,
settingsType: 'user',
adjustHeaderForEmbeddedScroll: true,
hideDrawerWithOtherUserIdParam: true
});
appRouter.addRoute({
path: '/settings/profile.html',
controller: 'settings/profile.js',
controllerType: 'module',
type: 'settings',
title: 'Profile',
icon: 'person',
clearBackdrop: true,
roles: 'EnableUserPreferenceAccess',
settingsTheme: true,
settingsType: 'user',
adjustHeaderForEmbeddedScroll: true,
hideDrawerWithOtherUserIdParam: true
});
if (appHost.supports('cameraupload')) {
appRouter.addRoute({
path: '/settings/cameraupload.html',
autoFocus: false,
controller: 'settings/cameraupload.js',
controllerType: 'module',
type: 'settings',
icon: 'photo',
title: 'HeaderCameraUpload',
clearBackdrop: true,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true
});
}
if (appHost.supports('sync')) {
appRouter.addRoute({
path: '/settings/download',
contentPath: '/settings/download/download.html',
controller: 'settings/download/download.js',
controllerType: 'module',
type: 'settings',
icon: '',
title: 'Downloads',
clearBackdrop: true,
settingsTheme: true,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true
});
}
appRouter.addRoute({
path: '/search',
contentPath: '/search/search.html',
controller: 'search/search.js',
controllerType: 'module',
title: '',
autoFocus: false,
clearBackdrop: true,
searchButton: false,
adjustHeaderForEmbeddedScroll: true,
navMenuId: 'search'
});
appRouter.addRoute({
path: '/startup/manualserver.html',
controller: 'startup/manualserver.js',
controllerType: 'module',
anonymous: true,
startup: true,
defaultTitle: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
drawer: false
});
appRouter.addRoute({
path: '/startup/selectserver.html',
contentPath: '/list/list.html',
autoFocus: false,
anonymous: true,
startup: true,
controller: 'startup/selectserver.js',
controllerType: 'module',
title: 'HeaderSelectServer',
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
drawer: false,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001160340-emby-connect'
});
appRouter.addRoute({
contentPath: '/tv/tv.html',
path: '/tv',
controller: 'tv/tv.js',
controllerType: 'module',
autoFocus: false,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true
});
if (appHost.supports('serversetup')) {
appRouter.addRoute({
contentPath: '/plugins/addplugin.html',
path: '/plugins/install',
autoFocus: false,
roles: 'admin',
controller: 'plugins/addpluginpage.js',
controllerType: 'module',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159720-plugins',
title: 'Plugins'
});
appRouter.addRoute({
path: '/database',
contentPath: '/server/database/database.html',
roles: 'admin',
controller: 'server/database/database.js',
controllerType: 'module',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
title: 'Database',
autoFocus: false
});
appRouter.addRoute({
path: '/dashboard',
contentPath: '/dashboard/dashboard.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/dashboard.js',
controllerType: 'module',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
title: 'Dashboard'
});
// legacy
appRouter.addRoute({
path: '/dashboard.html',
contentPath: '/dashboard/dashboard.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/dashboard.js',
controllerType: 'module',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
title: 'Dashboard'
});
appRouter.addRoute({
path: '/dashboard/settings',
contentPath: '/dashboard/settings.html',
controller: 'dashboard/settings.js',
controllerType: 'module',
autoFocus: false,
roles: 'admin',
settingsTheme: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159322-server-settings',
title: 'Settings',
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
contentPath: '/list/list.html',
path: '/devices',
autoFocus: false,
roles: 'admin',
controller: 'devices/devices.js',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159497-devices',
title: 'Devices',
adjustHeaderForEmbeddedScroll: true,
canRefresh: true
});
appRouter.addRoute({
contentPath: '/network/network.html',
path: '/network',
autoFocus: false,
roles: 'admin',
controller: 'network/network.js',
controllerType: 'module',
title: 'Network',
settingsTheme: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/articles/Hosting-Settings.html',
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
path: '/devices/device.html',
autoFocus: false,
roles: 'admin',
controller: 'devices/device.js',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159497-devices',
title: 'Devices',
navMenuId: 'devices',
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
path: '/devices/cameraupload.html',
autoFocus: false,
roles: 'admin',
controller: 'devices/cameraupload.js',
controllerType: 'module',
settingsTheme: true,
title: 'HeaderCameraUpload',
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159382-camera-upload',
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
contentPath: '/metadatamanager/metadatamanager.html',
path: '/metadatamanager',
controller: 'metadatamanager/metadatamanager.js',
controllerType: 'module',
autoFocus: false,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
title: 'MetadataManager'
});
appRouter.addRoute({
contentPath: '/transcoding/transcoding.html',
path: '/transcoding',
autoFocus: false,
roles: 'admin',
controller: 'transcoding/transcoding.js',
controllerType: 'module',
title: 'Transcoding',
settingsTheme: true,
headerTabs: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159897-transcoding',
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
path: '/librarysetup',
contentPath: '/librarysetup/librarysetup.html',
controller: 'librarysetup/librarysetup.js',
controllerType: 'module',
autoFocus: false,
roles: 'admin',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
headerTabs: true,
title: 'Library',
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159319-library-setup',
navMenuId: 'librarysetup'
});
appRouter.addRoute({
path: '/livetvsetup',
contentPath: '/livetvsetup/livetvsetup.html',
controller: 'livetvsetup/livetvsetup.js',
controllerType: 'module',
autoFocus: false,
roles: 'admin',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
headerTabs: true,
title: 'LiveTV',
helpUrl: 'https://support.emby.media/support/solutions/articles/44001160415-live-tv-setup',
navMenuId: 'livetvsetup'
});
appRouter.addRoute({
path: '/livetvsetup/guideprovider.html',
autoFocus: false,
roles: 'admin',
controller: 'livetvsetup/guideprovider.js',
controllerType: 'module',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001160415-live-tv-setup',
title: 'LiveTV',
navMenuId: 'livetvsetup'
});
appRouter.addRoute({
path: '/livetvsetup/livetvtuner.html',
autoFocus: false,
roles: 'admin',
controller: 'livetvsetup/livetvtuner.js',
controllerType: 'module',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001160415-live-tv-setup',
title: 'HeaderTVSourceSetup',
navMenuId: 'livetvsetup'
});
appRouter.addRoute({
path: '/logs',
contentPath: '/logs/logs.html',
controller: 'logs/logs.js',
autoFocus: false,
roles: 'admin',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
canRefresh: true,
title: 'Logs'
});
appRouter.addRoute({
path: '/log',
contentPath: '/list/list.html',
autoFocus: false,
roles: 'admin',
controller: 'logs/log.js',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
canRefresh: true,
navMenuId: 'logs',
title: 'Logs'
});
appRouter.addRoute({
path: '/plugins',
contentPath: '/plugins/plugins.html',
controller: 'plugins/plugins.js',
controllerType: 'module',
autoFocus: false,
roles: 'admin',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
headerTabs: true,
title: 'Plugins',
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159720-plugins',
navMenuId: 'plugins'
});
appRouter.addRoute({
path: '/dashboard/releasenotes.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/releasenotes.js',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
path: '/scheduledtasks',
contentPath: '/scheduledtasks/scheduledtasks.html',
roles: 'admin',
autoFocus: false,
controller: 'scheduledtasks/scheduledtasks.js',
controllerType: 'module',
title: 'HeaderScheduledTasks',
clearBackdrop: true,
settingsTheme: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159751-scheduled-tasks',
navMenuId: 'scheduledtasks',
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
path: '/scheduledtask',
contentPath: '/list/list.html',
autoFocus: false,
roles: 'admin',
controller: 'scheduledtasks/scheduledtask.js',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159751-scheduled-tasks',
title: 'HeaderScheduledTasks',
navMenuId: 'scheduledtasks',
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
path: '/serveractivity',
contentPath: '/list/list.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/serveractivity.js',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
canRefresh: true,
navMenuId: 'dashboard'
});
appRouter.addRoute({
path: '/apikeys',
contentPath: '/list/list.html',
autoFocus: false,
roles: 'admin',
controller: 'apikeys/apikeys.js',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
adjustHeaderForEmbeddedScroll: true,
canRefresh: true,
title: 'HeaderApiKeys'
});
appRouter.addRoute({
contentPath: '/embypremiere/embypremiere.html',
path: '/embypremiere',
controller: 'embypremiere/embypremiere.js',
controllerType: 'module',
autoFocus: false,
roles: 'admin',
settingsTheme: true,
clearBackdrop: true,
helpUrl: 'https://emby.media/premiere',
title: 'Emby Premiere',
adjustHeaderForEmbeddedScroll: true
});
appRouter.addRoute({
path: '/serverdownloads',
contentPath: 'server/sync/sync.html',
autoFocus: false,
controller: 'server/sync/sync.js',
controllerType: 'module',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
headerTabs: true,
clearBackdrop: true,
title: 'Downloads'
});
appRouter.addRoute({
path: '/conversions',
contentPath: 'server/sync/sync.html',
autoFocus: false,
controller: 'server/sync/sync.js',
controllerType: 'module',
settingsTheme: true,
headerTabs: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
title: 'Conversions'
});
appRouter.addRoute({
path: '/users/user',
contentPath: '/users/user.html',
controller: 'users/user.js',
controllerType: 'module',
autoFocus: false,
roles: 'admin',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
headerTabs: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001160237-users',
navMenuId: 'users'
});
appRouter.addRoute({
path: '/users/new',
contentPath: '/users/usernew.html',
controller: 'users/usernew.js',
controllerType: 'module',
autoFocus: false,
roles: 'admin',
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001160237-users',
navMenuId: 'users',
title: 'HeaderNewUser'
});
appRouter.addRoute({
path: '/users',
contentPath: '/list/list.html',
autoFocus: false,
roles: 'admin',
controller: 'users/users.js',
controllerType: 'module',
settingsTheme: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001160237-users',
adjustHeaderForEmbeddedScroll: true,
canRefresh: true,
title: 'Users'
});
appRouter.addRoute({
path: '/wizard/wizardagreement.html',
anonymous: true,
controller: 'wizard/wizardagreement.js',
controllerType: 'module',
homeButton: false,
secondaryHeaderFeatures: false,
defaultTitle: true,
drawer: false,
dockedTabs: false,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true
});
appRouter.addRoute({
path: '/wizard/wizardremoteaccess.html',
anonymous: true,
controller: 'wizard/wizardremoteaccess.js',
controllerType: 'module',
homeButton: false,
secondaryHeaderFeatures: false,
defaultTitle: true,
drawer: false,
dockedTabs: false,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true
});
appRouter.addRoute({
path: '/wizard/wizardfinish.html',
anonymous: true,
controller: 'wizard/wizardfinishpage.js',
controllerType: 'module',
homeButton: false,
secondaryHeaderFeatures: false,
defaultTitle: true,
drawer: false,
dockedTabs: false,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true
});
appRouter.addRoute({
path: '/wizard/wizardlibrary.html',
controller: 'wizard/wizardlibrary.js',
controllerType: 'module',
anonymous: true,
homeButton: false,
secondaryHeaderFeatures: false,
defaultTitle: true,
drawer: false,
dockedTabs: false,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
helpUrl: 'https://support.emby.media/support/solutions/articles/44001159319-library-setup'
});
appRouter.addRoute({
path: '/wizard/wizardstart.html',
anonymous: true,
controller: 'wizard/wizardstart.js',
controllerType: 'module',
homeButton: false,
secondaryHeaderFeatures: false,
defaultTitle: true,
drawer: false,
dockedTabs: false,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true
});
appRouter.addRoute({
path: '/wizard/wizarduser.html',
controller: 'wizard/wizarduserpage.js',
controllerType: 'module',
autoFocus: false,
anonymous: true,
homeButton: false,
secondaryHeaderFeatures: false,
defaultTitle: true,
drawer: false,
dockedTabs: false,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true
});
appRouter.addRoute({
path: '/configurationpage',
autoFocus: false,
enableCache: false,
enableContentQueryString: true,
roles: 'admin',
contentPath: appHost.supports('multiserver') ? getPluginPageContentPath : null,
settingsTheme: true,
windowScroll: 3,
// used by navdrawercontent, but this should be cleaned up and removed
requiresDynamicTitle: true
});
appRouter.addRoute({
path: '/genericui',
contentPath: 'modules/genericui/genericui.html',
autoFocus: false,
controller: 'modules/genericui/genericui.js',
controllerType: 'module',
enableContentQueryString: true,
settingsTheme: true,
adjustHeaderForEmbeddedScroll: true,
clearBackdrop: true,
});
}
appRouter.addRoute({
path: '/index.html',
isDefaultRoute: true,
clearBackdrop: true,
autoFocus: false
});
appRouter.addRoute({
path: '/',
isDefaultRoute: true,
clearBackdrop: true
});
}
function triggerWorkerTask() {
require(['bgtaskregister'], function (bgtaskregister) {
try {
// Trigger background task
bgtaskregister.triggerTask();
} catch (err) {
console.error("Error firing ApplicationTrigger", err);
}
});
}
function getWindowsLocalSync() {
return {
sync: triggerWorkerTask,
setProgressUpdatesEnabled: function (enabled) {
}
};
}
function getDynamicImportWithoutExport(path) {
return function () { return import(path); };
}
function getDynamicImport(path) {
return function () { return import(path).then(returnFirstDependencyDefault); };
}
function importFromPath(path) {
return getDynamicImport(path)();
}
function importFromPathWithoutExport(path) {
return getDynamicImportWithoutExport(path)();
}
function getNativeImport(objectName) {
const nativeObject = globalThis[objectName];
return Promise.resolve(nativeObject);
}
function loadAppStorage() {
let promise;
if (appMode === 'winjs') {
promise = getRequirePromise("native/windows/appstorage");
}
else {
try {
localStorage.setItem('_test', '0');
localStorage.removeItem('_test');
promise = importFromPath('./modules/emby-apiclient/appstorage-localstorage.js');
} catch (e) {
promise = importFromPath('./modules/emby-apiclient/appstorage-memory.js');
}
}
return promise.then(function (appStorage) {
return (appStorage.init ? appStorage.init() : Promise.resolve()).then(function () {
return appStorage;
});
});
}
function loadApiClientInternal() {
return loadAppHost().then(function (appHost) {
if (appHost.supports('sync')) {
return getDynamicImport('./modules/emby-apiclient/apiclientex.js')();
}
return getDynamicImport('./modules/emby-apiclient/apiclient.js')();
});
}
function loadApiClient() {
console.log('loadApiClient');
return getDynamicImport('./modules/common/servicelocator.js')().then(function (serviceLocator) {
return loadApiClientInternal().then(function (apiClientFactory) {
serviceLocator.initialize({
apiClientFactory: apiClientFactory
});
return apiClientFactory;
});
});
}
function supportsTizenNaclSockets() {
if (globalThis.tizen && globalThis.tizen.systeminfo) {
const v = globalThis.tizen.systeminfo.getCapability('http://tizen.org/feature/platform.version');
// Don't load sockets for 2015 models, wasm supported from 2020 models
// wasm is supported in 2020, but udp socket() creation fails in the 5.5 emulator so stick with nacl
return (!supportsTizenWasmSockets() && v && parseFloat(v) >= parseFloat('2.4'));
}
return false;
}
function supportsTizenWasmSockets() {
if (globalThis.tizen && globalThis.tizen.systeminfo) {
const v = globalThis.tizen.systeminfo.getCapability('http://tizen.org/feature/platform.version');
// Don't load sockets for 2015 models, wasm supported from 2020 models
// wasm is supported in 2020, but udp socket() creation fails in the 5.5 emulator so stick with nacl
return (v && parseFloat(v) >= parseFloat('6.0'));
}
return false;
}
function loadServerDiscovery() {
if (customPaths.serverdiscovery) {
return getRequirePromise(addJsExtIfNeeded(customPaths.serverdiscovery));
}
if (appMode === 'winjs') {
return getRequirePromise("native/windows/serverdiscovery");
}
if (isNativeTizen && (supportsTizenNaclSockets() || supportsTizenWasmSockets())) {
return getRequirePromise("native/tizen/serverdiscovery");
}
if (appMode === 'android') {
return getRequirePromise("native/android/serverdiscovery");
}
if (appMode === 'ios') {
return getRequirePromise("native/ios/serverdiscovery");
}
return getDynamicImport('./modules/emby-apiclient/serverdiscovery.js')();
}
function loadShell() {
if (customPaths.shell) {
return getRequirePromise(addJsExtIfNeeded(customPaths.shell));
}
if (appMode === 'android') {
return getRequirePromise("native/android/shell");
}
return getDynamicImport('./modules/shell.js')();
}
function loadWakeOnLan() {
if (customPaths.wakeonlan) {
return getRequirePromise(addJsExtIfNeeded(customPaths.wakeonlan));
}
if (appMode === 'winjs') {
return getRequirePromise("native/windows/wakeonlan");
}
if (isNativeTizen && (supportsTizenNaclSockets() || supportsTizenWasmSockets())) {
return getRequirePromise("native/tizen/wakeonlan");
}
if (appMode === 'ios') {
return getRequirePromise("native/ios/wakeonlan");
}
if (appMode === 'android') {
return getRequirePromise("native/android/wakeonlan");
}
return getDynamicImport('./modules/emby-apiclient/wakeonlan.js')();
}
function loadFullscreenManager() {
return getDynamicImport('./modules/common/servicelocator.js')().then(function (serviceLocator) {
let promise;
if (customPaths.fullscreenmanager) {
promise = getRequirePromise(customPaths.fullscreenmanager);
} else {
promise = getDynamicImport('./modules/fullscreen/fullscreenmanager.js')();
}
return promise.then(function (fullscreenManager) {
serviceLocator.initialize({
fullscreenManager: fullscreenManager
});
return fullscreenManager;
});
});
}
function bindOnBeforeWindowUnload() {
getDynamicImport('./modules/common/playback/playbackmanager.js')().then(function (playbackManager) {
window.addEventListener("beforeunload", function (e) {
try {
playbackManager.onAppClose();
} catch (err) {
console.log('error in onAppClose: ' + err);
}
});
});
}
function loadIap() {
console.log('loadIap');
let promise;
if (appMode === 'android') {
promise = getRequirePromise("native/android/iap");
} else if (appMode === 'ios') {
promise = getRequirePromise("native/ios/iap");
} else {
promise = getDynamicImport('./modules/iap.js')();
}
return promise.then(function (iapManager) {
return getDynamicImport('./modules/common/servicelocator.js')().then(function (serviceLocator) {
serviceLocator.initialize({
iapManager: iapManager
});
});
});
}
function loadServiceLocator() {
console.log('loadServiceLocator');
return Promise.all([
loadAppStorage(),
loadAppHost(),
loadShell(),
// this needs to be in place before anything tries to load playbackManager
loadFullscreenManager(),
loadWakeOnLan(),
loadServerDiscovery()
]).then(function (responses) {
console.log('loadServiceLocator - inner load 1');
const appStorage = responses[0];
const appHost = responses[1];
const shell = responses[2];
const wakeOnLan = responses[4];
const serverDiscovery = responses[5];
const promises = [
getDynamicImport('./modules/common/servicelocator.js')()
];
if (appHost.supports('sync')) {
promises.push(require(['filerepository']));
promises.push(require(['itemrepository']));
promises.push(require(['transfermanager']));
promises.push(require(['useractionrepository']));
promises.push(require(['localsync']));
}
if (appHost.supports('cameraupload')) {
promises.push(loadCameraUpload());
}
if (appHost.supports('applogger')) {
promises.push(loadAppLogger());
}
return Promise.all(promises).then(function (responsesInner) {
console.log('loadServiceLocator - inner load 2');
let index = 0;
const serviceLocator = responsesInner[index];
index++;
let fileRepository;
let itemRepository;
let transferManager;
let userActionRepository;
let localSync;
let cameraUpload;
let appLogger;
if (appHost.supports('sync')) {
fileRepository = responsesInner[index][0];
index++;
itemRepository = responsesInner[index][0];
index++;
transferManager = responsesInner[index][0];
index++;
userActionRepository = responsesInner[index][0];
index++;
localSync = responsesInner[index][0];
index++;
}
if (appHost.supports('cameraupload')) {
cameraUpload = responsesInner[index];
index++;
}
if (appHost.supports('applogger')) {
appLogger = responsesInner[index];
index++;
}
console.log('loadServiceLocator - calling serviceLocator.initialize');
serviceLocator.initialize({
appStorage: appStorage,
appHost: appHost,
shell: shell,
wakeOnLan: wakeOnLan,
serverDiscovery: serverDiscovery,
fileRepository: fileRepository,
itemRepository: itemRepository,
transferManager: transferManager,
userActionRepository: userActionRepository,
cameraUpload: cameraUpload,
appLogger: appLogger,
localSync: localSync
});
console.log('loadServiceLocator - calling appHost.init');
return appHost.init().then(loadApiClient).then(loadIap);
});
});
}
function addJsExtIfNeeded(path) {
if (!path.endsWith('.js')) {
path += '.js';
}
return path;
}
function getRequirePromise(dep) {
return new Promise(function (resolve, reject) {
require([dep], resolve);
});
}
function loadAppLogger() {
if (appMode === 'android') {
return getRequirePromise("native/android/applogger");
}
return Promise.resolve(getDummyAppLogger());
}
function getDummyAppLogger() {
return {
getLogFiles: function (query) {
let items = [];
items.push({
Name: 'currentlog.txt',
Id: 'currentlog.txt',
DateCreated: new Date().toISOString(),
DateModified: new Date().toISOString(),
Type: 'Log',
CanDownload: true,
CanShare: true
});
const total = items.length;
if (query.StartIndex) {
items = items.slice(query.StartIndex);
}
if (query.Limit) {
items.length = Math.min(query.Limit, items.length);
}
return Promise.resolve({
Items: items,
TotalRecordCount: total
});
},
getLogLines: function (query) {
let items = [];
for (let i = 0, length = 10000; i < length; i++) {
items.push('line ' + i);
}
const total = items.length;
if (query.StartIndex) {
items = items.slice(query.StartIndex);
}
if (query.Limit) {
items.length = Math.min(query.Limit, items.length);
}
return Promise.resolve({
Items: items,
TotalRecordCount: total
});
},
downloadLog: function (name) {
console.log('downloading dummy log file: ' + name);
return Promise.resolve();
},
shareLog: function (name) {
console.log('sharing dummy log file: ' + name);
return Promise.resolve();
}
};
}
function loadCameraUpload() {
if (appMode === 'ios') {
return getRequirePromise("native/ios/cameraupload");
}
if (appMode === 'android') {
return getRequirePromise("native/android/cameraupload");
}
return Promise.resolve(getDummyCameraUpload());
}
function getDummyCameraUpload() {
return {
start: function () { },
setProgressUpdatesEnabled: function () { },
getAvailableFolders: function () { return Promise.resolve([{ Id: '541C6607-9C45-4875-A292-5F89F742B2B3/L0/040', Name: 'TestFolder1' }, { Id: '773DFE72-F38F-4220-8F2F-C4A472DBBA75/L0/040', Name: 'TestFolder2' }]); }
};
}
function loadAppHost() {
if (customPaths.apphost) {
return getRequirePromise(addJsExtIfNeeded(customPaths.apphost));
}
if (appMode === 'ios') {
return getRequirePromise("native/ios/apphost");
}
if (appMode === 'android') {
return getRequirePromise("native/android/apphost");
}
return importFromPath('./modules/apphost.js');
}
function getImportMap() {
const elem = document.querySelector('script[type="importmap"]');
if (elem) {
const html = elem.innerHTML;
if (html) {
try {
const obj = JSON.parse(html);
if (obj) {
const imports = obj.imports;
if (imports) {
return imports;
}
}
}
catch (err) {
console.log('error parsing import map: ' + err);
}
}
}
return {};
}
/**
* Object.entries() polyfill
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
*/
// this has to be here due to initRequire using it before polyfills start getting loaded
if (!Object.entries) {
Object.entries = function (obj) {
const ownProps = Object.keys(obj);
let i = ownProps.length;
const resArray = new Array(i); // preallocate the Array
while (i--) {
resArray[i] = [ownProps[i], obj[ownProps[i]]];
}
return resArray;
};
}
function initRequire() {
const importMap = getImportMap();
const entries = Object.entries(importMap);
for (let i = 0, length = entries.length; i < length; i++) {
const entry = entries[i];
const key = entry[0];
const url = entry[1];
//console.log('defining from importMap: ' + key + ': ' + url);
define(key, [], getDynamicImport(url));
}
// HTML DONE
define("embyProgressBarStyle", [], returnFirstDependency);
// alias
define("inputmanager", ['inputManager'], returnFirstDependency);
define("fullscreenManager", loadFullscreenManager);
define("shell", [], loadShell);
if (customPaths.filesystem) {
define("filesystem", [addJsExtIfNeeded(customPaths.filesystem)], returnFirstDependency);
} else {
define("filesystem", [], getDynamicImport('./modules/common/filesystem.js'));
}
define("hlsjs", ["modules/hlsjs/hls.min"], returnFirstDependency);
// alias
define('connectionManagerResolver', ['connectionManager'], returnFirstDependency);
define("cardStyle", ['css!modules/cardbuilder/card.css'], returnFirstDependency);
define("flexStyles", ["css!modules/flexstyles.css"], returnFirstDependency);
define("programStyles", ['css!modules/emby-elements/guide/programs.css'], returnFirstDependency);
define("apphost", [], loadAppHost);
define("cameraUpload", [], loadCameraUpload);
define("appLogger", [], loadAppLogger);
define("serverdiscovery", [], loadServerDiscovery);
define("wakeOnLan", [], loadWakeOnLan);
define("appStorage", [], loadAppStorage);
define("clearButtonStyle", [], returnFirstDependency);
define("listViewStyle", ['css!' + "modules/listview/listview.css"], returnFirstDependency);
define("formDialogStyle", ["css!modules/formdialog.css"], returnFirstDependency);
define("sectionsStyle", ["css!modules/sections.css"], returnFirstDependency);
define("mediasync", ["modules/sync/mediasync"], returnFirstDependency);
define("scrollStyles", ['css!modules/scrollstyles.css'], returnFirstDependency);
// alias
define("appsettings", ['appSettings'], returnFirstDependency);
define("material-icons", ['css!modules/fonts/material-icons/style.css'], returnFirstDependency);
define("systemFontsCss", ['css!modules/fonts/fonts.css'], returnFirstDependency);
define("dialogTemplateHtml", ["text!modules/dialog/dialog.template.html"], returnFirstDependency);
define("jQuery", ['https://code.jquery.com/jquery-3.7.0.slim.min.js'], function () {
if (globalThis.ApiClient) {
globalThis.jQuery.ajax = globalThis.ApiClient.ajax;
}
return globalThis.jQuery;
});
define("apiInput", ['serverNotifications'], returnFirstDependency);
define('apiClientResolver', ['connectionManager'], function (connectionManager) {
return function () {
return connectionManager.currentApiClient();
};
});
define("embyRouter", ['appRouter'], returnFirstDependency);
define("webActionSheet", ["actionsheet"], returnFirstDependency);
if (isNativeTizen && supportsTizenNaclSockets()) {
define('sockets', ['native/tizen/naclSockets/sockets'], returnFirstDependency);
}
if (isNativeTizen && supportsTizenWasmSockets()) {
define('sockets', ['native/tizen/wasmSockets/sockets'], returnFirstDependency);
}
define("iapManager", [], loadIap);
define("detailtablecss", [], returnFirstDependency);
define("apiclient", [], loadApiClient);
}
function loadCoreDictionary(globalize) {
const baseUrl = 'strings/';
const languages = [
'ar',
'be-BY',
'bg-BG',
'ca',
'cs',
'da',
'de',
'el',
'en-GB',
'en-US',
'es',
'es-AR',
'es-MX',
'fa',
'fi',
'fr',
'fr-CA',
'gsw',
'he',
'hi-IN',
'hr',
'hu',
'id',
'it',
'ja',
'kk',
'ko',
'lt-LT',
'ms',
'nb',
'nl',
'no',
'pl',
'pt-BR',
'pt-PT',
'ro',
'ru',
'sk',
'sl-SI',
'sv',
'tr',
'uk',
'vi',
'zh-CN',
'zh-HK',
'zh-TW'
];
const translations = languages.map(function (i) {
return {
lang: i,
path: baseUrl + i + '.json'
};
});
return globalize.loadStrings({
name: 'core',
translations: translations
});
}
function replaceConsoleLogger() {
// We should probably have a logging abstraction to avoid replacing console.log
console.log = function () {
};
}
function loadWindowsComponents(appHost) {
if (!navigator.mediaSession) {
require(['native/windows/mediasession']);
}
if (!navigator.connection || !navigator.connection.type) {
require(['native/windows/networkpolyfill']);
}
if (!appHost.supports('sync')) {
return;
}
require(['bgtaskregister'], function (bgtaskregister) {
bgtaskregister.registerTask();
//bgtaskregister.unregisterTask();
});
}
function loadHeader() {
console.log('loadHeader');
return Promise.all([
importFromPath('./modules/appheader/appheader.js')
]).then(function (responses) {
return responses[0].init();
});
}
function onAppReady() {
if ("virtualKeyboard" in navigator) {
navigator.virtualKeyboard.overlaysContent = true;
}
console.log('onAppReady');
let promises = [
importFromPath('./modules/common/servicelocator.js'),
importFromPath('./modules/approuter.js'),
importFromPath('./modules/browser.js'),
importFromPath('./modules/common/pluginmanager.js')
];
if (appMode === 'ios' || appMode === 'android') {
promises.push(importFromPath('./modules/registrationservices/registrationservices.js'));
}
if (appMode === 'android') {
promises.push(getRequirePromise('native/android/appshortcuts'));
promises.push(getRequirePromise('native/android/nativecredentials'));
promises.push(getRequirePromise('native/android/nativesettings'));
}
else if (appMode === 'ios') {
promises.push(getRequirePromise('native/ios/appshortcuts'));
promises.push(getRequirePromise('native/ios/nativecredentials'));
promises.push(getRequirePromise('native/ios/nativesettings'));
}
return Promise.all(promises).then(function (responses) {
const serviceLocator = responses[0];
const appHost = serviceLocator.appHost;
const appRouter = responses[1];
const browser = responses[2];
const pluginManager = responses[3];
promises = [];
console.log('Loaded dependencies in onAppReady');
defineCoreRoutes(appRouter, appHost, browser);
appRouter.start({
click: false,
// this will need to be true to support pages in subfolders
hashbang: true
});
document.dispatchEvent(new CustomEvent("appready", {}));
if (appHost.supports('soundeffects')) {
importFromPath('./modules/soundeffects/soundeffectsmanager.js');
}
importFromPathWithoutExport('./modules/thememediaplayer.js');
importFromPathWithoutExport('./modules/transparencymanagement.js');
if (!appHost.supports('nativegamepadkeymapping') && !enableNativeGamepadKeyMapping() && isGamepadSupported()) {
importFromPathWithoutExport('./modules/input/gamepadtokey.js');
}
if (appHost.supports('webhidautoauth')) {
importFromPath('./modules/input/hidinput.js').then(function(hidInput) {
if (hidInput.shouldTryConnect()) {
window.addEventListener('keydown',
function() {
hidInput.tryConnect();
},
{ capture: true, passive: false, once: true });
}
});
}
if (appHost.supports('windowstate')) {
importFromPathWithoutExport('./modules/controlbox.js');
}
if (appMode === 'android') {
require([
'native/android/mediasession',
'native/android/chromecast'
]);
}
else if (appMode === 'ios') {
require([
'native/ios/mediasession',
'native/ios/backgroundfetch',
'native/ios/nativeplayerbridge'
]);
}
// We need these but it's ok to let the UI load first
else if (isNativeTizen) {
require(['native/tizen/input']);
require(['native/tizen/networkerror']);
if (browser.sdkVersion && browser.sdkVersion >= 2.4) {
require(['native/tizen/preview']);
}
require(['native/tizen/screensavermanager']);
// itemContextMenu is also checking this to allow all options when sideloading
browser.tizenSideload = false;
if (browser.tizenSideload) {
require(['native/tizen/expiration']);
}
}
if (!browser.tv) {
importFromPathWithoutExport('./modules/notifications.js');
importFromPath('./modules/dockedtabs/dockedtabs.js');
}
importFromPathWithoutExport('./modules/nowplayingbar/nowplayingbar.js');
if (appHost.supports('remotecontrol')) {
// For now this is needed because it also performs the mirroring function
importFromPathWithoutExport('./modules/playback/remotecontrolautoplay.js');
}
if (navigator.mediaSession && !appHost.supports('nativemediasession')) {
importFromPathWithoutExport('./modules/playback/mediasession.js');
}
importFromPath('./modules/input/mouse.js');
importFromPath('./modules/input/keyboard.js');
importFromPath('./modules/common/input/api.js');
if (appHost.supports('screensaver')) {
importFromPath('./modules/screensavermanager.js');
}
if (appHost.supports('fullscreenchange')) {
importFromPathWithoutExport('./modules/fullscreen/fullscreen-dc.js');
}
if (!appHost.supports('multiserver')) {
if (globalThis.ApiClient) {
require(['css!' + globalThis.ApiClient.getUrl('Branding/Css')]);
}
}
if (appMode === 'winjs') {
loadWindowsComponents(appHost);
}
// load this early so that css is already available and the first dialog isn't loaded awkwardly
// this will no longer be needed once we start using import assertions
importFromPath('./modules/actionsheet/actionsheet.js');
require(['formDialogStyle']);
bindOnBeforeWindowUnload();
return Promise.all(promises);
});
}
function loadExternalScripts() {
console.log('loadExternalScripts');
const startInfo = this;
const scripts = startInfo.scripts;
if (scripts) {
return require(scripts);
}
return Promise.resolve();
}
function loadPlugins() {
console.log('loadPlugins');
const startInfo = this;
return Promise.all([
importFromPath('./modules/common/servicelocator.js'),
importFromPath('./modules/browser.js'),
importFromPath('./modules/approuter.js')
]).then(function (responses) {
const appHost = responses[0].appHost;
const browser = responses[1];
// Doesn't really belong here but this is the earliest apphost is loaded
if (appHost.supports('windowstate')) {
document.querySelector('.skinHeader').insertAdjacentHTML('beforeend', '<div class="windowDragRegion hide-mouse-idle-tv"></div>');
require(['css!modules/windowdrag.css']);
}
const externalPlugins = startInfo.plugins || [];
console.log('Loading installed plugins');
// Load installed plugins
if (customPaths.pluginloader) {
const forcedPlugins = [
'./modules/common/playback/playbackvalidation.js',
'./modules/common/playback/playaccessvalidation.js',
'./modules/common/playback/experimentalwarnings.js',
'./modules/htmlaudioplayer/plugin.js',
'./modules/photoplayer/plugin.js',
'./modules/confirmstillplaying/plugin.js',
//'./modules/common/playback/playqueueconfirmation.js'
];
return getRequirePromise(addJsExtIfNeeded(customPaths.pluginloader)).then(function (pluginloader) {
return pluginloader.loadPlugins(forcedPlugins);
});
}
const list = [
'./modules/common/playback/playbackvalidation.js',
'./modules/common/playback/playaccessvalidation.js',
'./modules/common/playback/experimentalwarnings.js'
];
//list.push('./modules/common/playback/playqueueconfirmation.js');
if (appHost.supports('soundeffects')) {
list.push('./modules/soundeffects/defaultsoundeffects/plugin.js');
}
if (appHost.supports('screensaver')) {
list.push('./modules/logoscreensaver/plugin.js');
list.push('./modules/backdropscreensaver/plugin.js');
list.push('./modules/photoscreensaver/plugin.js');
}
if (appMode === 'android') {
list.push('native/android/mpvvideoplayer');
list.push('native/android/mpvaudioplayer');
} else if (appMode === 'ios') {
list.push('native/ios/mpvaudioplayer');
list.push('native/ios/mpvvideoplayer');
}
if (appMode !== 'android' && appMode !== 'ios') {
list.push('./modules/htmlaudioplayer/plugin.js');
}
if (appMode === 'ios') {
list.push('native/ios/chromecast');
}
if (appMode === 'android') {
// intent player
list.push('native/android/externalplayer');
list.push('native/android/chromecast');
}
if (globalThis.webapis && webapis.avplay) {
list.push('native/tizen/tizenavplayer/plugin');
} else {
if (appMode !== 'android' && appMode !== 'ios') {
list.push('./modules/htmlvideoplayer/plugin.js');
}
}
list.push('./modules/photoplayer/plugin.js');
if (appHost.supports('remotecontrol')) {
list.push('./modules/sessionplayer.js');
// test for globalThis.chrome to detect other chromium based browsers as well as shims such as this: https://github.com/hensm/fx_cast
// exclude old edge due to seeing the chrome global in uwp
if (globalThis.chrome && !browser.edge && !browser.electron && appMode !== 'android') {
list.push('./modules/chromecast/chromecastplayer.js');
}
}
if (appHost.supports('youtube') || browser.electron) {
if (appMode === 'winjs') {
list.push('native/windows/youtubeplayer/plugin');
} else {
list.push('./modules/youtubeplayer/plugin.js');
}
}
for (let i = 0, length = externalPlugins.length; i < length; i++) {
list.push(externalPlugins[i]);
}
if (browser.electron) {
list.push('./modules/externalplayer/plugin.js');
}
list.push('./modules/confirmstillplaying/plugin.js');
return Promise.all(list.map(loadPlugin));
});
}
function loadFirstLevelPresentationDependencies() {
console.log('loadFirstLevelPresentationDependencies');
return Promise.all([importFromPath('./modules/browser.js')]).then(function (responses) {
const browser = responses[0];
// This is a tizen performance guideline to remove all logging from released applications
// do this as late as possible to make it easier to debug startup problems
if (appMode || browser.tv) {
replaceConsoleLogger();
}
// do early to prevent flash of content
if (browser.osx) {
document.documentElement.classList.add('html-osx');
}
else if (appMode === 'winjs') {
document.documentElement.classList.add('html-winjs');
}
const promises = [];
promises.push(require(['flexStyles']));
promises.push(require(['css!modules/layout.css']));
const supportsCssVariables = CSS.supports('color', 'var(--fake-var)');
if (!supportsCssVariables) {
promises.push(require(['css!modules/layout_nocssvars.css']));
}
promises.push(require(['sectionsStyle']));
promises.push(require(['systemFontsCss']));
return Promise.all(promises);
});
}
function loadGlobalization() {
return Promise.all([
importFromPath('./modules/common/globalize.js'),
importFromPath('./modules/common/servicelocator.js')
]).then(function (responses) {
const globalize = responses[0];
const serviceLocator = responses[1];
const appHost = serviceLocator.appHost;
if (globalThis.urlCacheParam) {
globalize.setCacheParam(globalThis.urlCacheParam);
}
const stringPromises = [];
if (appHost.supports('serversetup')) {
stringPromises.push(loadCoreDictionary(globalize));
}
stringPromises.push(loadSharedComponentsDictionary(globalize));
return Promise.all(stringPromises);
});
}
function loadThirdLevelPolyfills() {
console.log('loadThirdLevelPolyfills');
return Promise.all([
importFromPath('./modules/browser.js'),
]).then(function (responses) {
const browser = responses[0];
if (appMode === 'embyclient' && globalThis.NativeAppHost?.supports('sync')) {
define("transfermanager", [], getNativeImport('NativeTransferManager'));
define("filerepository", [], getNativeImport('NativeFileRepository'));
define("localsync", [], getNativeImport('NativeLocalSync'));
define('itemrepository', [], getNativeImport('NativeItemRepository'));
define('useractionrepository', [], getNativeImport('NativeUserActionRepository'));
}
else if (appMode === 'winjs' && !browser.xboxOne) {
define('bgtaskregister', ['native/windows/bgtaskregister'], returnFirstDependency);
define('transfermanager', ['native/windows/transfermanager'], returnFirstDependency);
define('filerepository', ['native/windows/filerepository'], returnFirstDependency);
define("localsync", [], getWindowsLocalSync);
define("itemrepository", [], getDynamicImport('./modules/localdatabase/itemrepository.js'));
define("useractionrepository", [], getDynamicImport('./modules/localdatabase/useractionrepository.js'));
}
else if (appMode === 'ios') {
define("filerepository", ["native/ios/filerepository"], returnFirstDependency);
define("transfermanager", ["filerepository"], returnFirstDependency);
define("localsync", ["native/ios/localsync"], returnFirstDependency);
define('itemrepository', ['native/ios/itemrepository'], returnFirstDependency);
define('useractionrepository', ['native/ios/useractionrepository'], returnFirstDependency);
}
else if (appMode === 'android' && AndroidAppHost.supportsSync()) {
define("transfermanager", [], getDynamicImport('./modules/sync/transfermanager.js'));
define("filerepository", ["native/android/filerepository"], returnFirstDependency);
define("localsync", ["native/android/localsync"], returnFirstDependency);
define('itemrepository', ['native/android/itemrepository'], returnFirstDependency);
define('useractionrepository', ['native/android/useractionrepository'], returnFirstDependency);
} else {
define("transfermanager", [], getDynamicImport('./modules/sync/transfermanager.js'));
define("filerepository", [], getDynamicImport('./modules/sync/filerepository.js'));
define("localsync", [], getDynamicImport('./modules/sync/localsync.js'));
define("itemrepository", [], getDynamicImport('./modules/localdatabase/itemrepository.js'));
define("useractionrepository", [], getDynamicImport('./modules/localdatabase/useractionrepository.js'));
}
const promises = [];
// these two have to come last because they depend on others such as Map, Set, WeakMap and MutationObserver
if (!('customElements' in globalThis)) {
// tizen 2015 fails with the newer polyfill
if (globalThis.MutationObserver && globalThis.Reflect) {
promises.push(require(['modules/polyfills/custom-elements']));
} else {
promises.push(require(['modules/polyfills/document-register-element']));
}
}
else if (!(('customElements' in globalThis) && !browser.iOS && !browser.safari && customElements.upgrade)) {
promises.push(importFromPathWithoutExport('./modules/polyfills/custom-elements-builtin.js'));
}
if (isNativeTizen) {
promises.push(require(['native/tizen/tizeninfo']));
}
if (isNativeLG) {
promises.push(require(['native/webos/webosinfo']));
}
return Promise.all(promises);
});
}
function loadSecondLevelPolyfills() {
console.log('loadSecondLevelPolyfills');
const promises = [];
if (typeof SpeechRecognition === 'undefined') {
globalThis.SpeechRecognition = globalThis.webkitSpeechRecognition;
}
// Note that the "unfetch" minimal fetch polyfill defines fetch() without
// defining window.Request, and this polyfill need to work on top of unfetch
// so the below feature detection needs the !globalThis.AbortController part.
// The Request.prototype check is also needed because Safari versions 11.1.2
// up to and including 12.1.x has a window.AbortController present but still
// does NOT correctly implement abortable fetch:
// https://bugs.webkit.org/show_bug.cgi?id=174980#c2
const isMissingRequestSignalSupport = typeof globalThis.Request === 'function' && !Object.prototype.hasOwnProperty.call(globalThis.Request.prototype, 'signal');
if (typeof AbortSignal === 'undefined' || isMissingRequestSignalSupport || !AbortSignal.timeout || !AbortSignal.prototype?.throwIfAborted) {
promises.push(importFromPathWithoutExport('./modules/polyfills/abortsignal.js'));
}
if (typeof AbortController === 'undefined' || isMissingRequestSignalSupport) {
promises.push(importFromPathWithoutExport('./modules/polyfills/abortcontroller.js'));
}
if (typeof AbortController !== 'undefined' && isMissingRequestSignalSupport) {
promises.push(importFromPathWithoutExport('./modules/polyfills/abortablefetch.js'));
}
if (!Number.isInteger) {
promises.push(importFromPathWithoutExport('./modules/polyfills/number.js'));
}
if (typeof Intl === 'undefined' || !Intl.NumberFormat) {
promises.push(importFromPathWithoutExport('./modules/polyfills/numberformat.js'));
}
if (typeof Intl === 'undefined' || !Intl.RelativeTimeFormat) {
promises.push(importFromPathWithoutExport('./modules/polyfills/relativetimeformat.js'));
}
if (typeof Object.assign !== 'function' || typeof Object.create !== 'function' || typeof Object.hasOwn !== 'function') {
promises.push(importFromPathWithoutExport('./modules/polyfills/object.js'));
}
if (typeof Promise.any !== 'function' || typeof Promise.allSettled !== 'function') {
promises.push(importFromPathWithoutExport('./modules/polyfills/promise.js'));
}
if (!String.prototype.includes || !String.prototype.startsWith || !String.prototype.endsWith || !String.prototype.replaceAll) {
promises.push(importFromPathWithoutExport('./modules/polyfills/string.js'));
}
if (!Array.prototype.filter || !Array.prototype.includes || !Array.prototype.some || !Array.isArray || !Array.from) {
promises.push(importFromPathWithoutExport('./modules/polyfills/array.js'));
}
if (!Element.prototype.matches || !Element.prototype.closest || !Element.prototype.remove || !Element.prototype.replaceChildren || !Element.prototype.append) {
promises.push(importFromPathWithoutExport('./modules/polyfills/element.js'));
}
if (!HTMLFormElement.prototype.requestSubmit) {
promises.push(importFromPathWithoutExport('./modules/polyfills/form.js'));
}
if (!Function.prototype.bind) {
promises.push(importFromPathWithoutExport('./modules/polyfills/bind.js'));
}
if (typeof Map === 'undefined') {
promises.push(importFromPathWithoutExport('./modules/polyfills/map.js'));
}
if (typeof WeakMap === 'undefined') {
promises.push(importFromPathWithoutExport('./modules/polyfills/weakmap.js'));
}
if (typeof Set === 'undefined' || !Set.prototype.entries) {
promises.push(importFromPathWithoutExport('./modules/polyfills/set.js'));
}
if (typeof crypto === 'undefined' || !crypto.randomUUID) {
promises.push(importFromPathWithoutExport('./modules/polyfills/crypto.js'));
}
if (typeof CSS === 'undefined' || !CSS.supports) {
promises.push(importFromPathWithoutExport('./modules/polyfills/css.js'));
}
return Promise.all(promises);
}
function requiresUrlSearchParamsPolyfill() {
if (typeof URLSearchParams === 'undefined') {
return true;
}
try {
const plus = '+';
// test for broken support in iOS 10.3
// https://github.com/ungap/url-search-params/blob/master/index.js
// https://github.com/WebReflection/url-search-params#ios-10--other-platforms-bug
if (
new URLSearchParams('q=%2B').get('q') !== plus ||
new URLSearchParams({ q: plus }).get('q') !== plus ||
new URLSearchParams([['q', plus]]).get('q') !== plus ||
new URLSearchParams('q=\n').toString() !== 'q=%0A' ||
new URLSearchParams({ q: ' &' }).toString() !== 'q=+%26' ||
new URLSearchParams({ q: '%zx' }).toString() !== 'q=%25zx'
) {
return true;
}
return false;
}
catch (error) {
return true;
}
}
function requiresDataTransferPolyfill() {
if (typeof DataTransfer === 'undefined') {
return true;
}
try {
new DataTransfer();
return false;
}
catch (error) {
return true;
}
}
function loadFirstLevelPolyfills() {
const promises = [];
if (globalThis.Emby.requiresClassesPolyfill) {
promises.push(importFromPathWithoutExport('./modules/babelhelpers.js'));
}
if (typeof ResizeObserver === 'undefined') {
promises.push(importFromPathWithoutExport('./modules/polyfills/resizeobserver.js'));
}
if (typeof IntersectionObserver === 'undefined') {
promises.push(importFromPathWithoutExport('./modules/polyfills/intersection-observer.js'));
}
// technically this should be loaded before the fetch polyfill since fetch.js checks for support for URLSearchParams right at the top
// but that's only used when assigning a URLSearchParams instance to a request body which we shouldn't use anyway due to inconsistent support for it
if (requiresUrlSearchParamsPolyfill()) {
promises.push(importFromPathWithoutExport('./modules/polyfills/urlsearchparams.js'));
}
if (requiresDataTransferPolyfill()) {
promises.push(importFromPathWithoutExport('./modules/polyfills/datatransfer.js'));
}
if (typeof fetch === 'undefined') {
promises.push(importFromPathWithoutExport('./modules/polyfills/fetch.js'));
}
return Promise.all(promises);
}
function start(startInfo) {
enableNativeGamepadKeyMapping();
if (typeof Windows !== 'undefined' && Windows.UI) {
try {
Windows.UI.ViewManagement.ApplicationViewScaling.trySetDisableLayoutScaling(true);
}
catch (err) {
}
try {
Windows.UI.ViewManagement.ApplicationView.getForCurrentView().setDesiredBoundsMode(Windows.UI.ViewManagement.ApplicationViewBoundsMode.useCoreWindow);
} catch (err) {
}
}
startInfo = startInfo || globalThis.appStartInfo || {};
customPaths = startInfo.paths || {};
initRequire();
return loadFirstLevelPolyfills()
.then(loadSecondLevelPolyfills, loadSecondLevelPolyfills)
.then(loadThirdLevelPolyfills, loadThirdLevelPolyfills)
.then(loadServiceLocator)
.then(createConnectionManager)
.then(loadGlobalization)
.then(loadFirstLevelPresentationDependencies)
.then(loadPlugins.bind(startInfo))
.then(loadExternalScripts.bind(startInfo))
.then(loadHeader)
.then(onAppReady);
}
if (!globalThis.Emby) {
globalThis.Emby = {};
}
Emby.importModule = importFromPath;
Emby.App = {
start: start
};
if (globalThis.location.href.toString().toLowerCase().indexOf('autostart=false') === -1) {
start();
}
// closure end