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

458 lines
13 KiB
JavaScript
Raw Normal View History

2021-08-10 12:43:24 -04:00
import Package from '../../package.json';
2020-08-14 08:46:34 +02:00
import appSettings from '../scripts/settings/appSettings';
import browser from '../scripts/browser';
2020-09-08 02:05:02 -04:00
import { Events } from 'jellyfin-apiclient';
2020-08-14 08:46:34 +02:00
import * as htmlMediaHelper from '../components/htmlMediaHelper';
import * as webSettings from '../scripts/settings/webSettings';
import globalize from '../scripts/globalize';
import profileBuilder from '../scripts/browserDeviceProfile';
2018-10-23 00:48:22 +03:00
const appName = 'Jellyfin Web';
function getBaseProfileOptions(item) {
const disableHlsVideoAudioCodecs = [];
2020-08-02 15:25:20 +03:00
if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) {
if (browser.edge) {
disableHlsVideoAudioCodecs.push('mp3');
2018-10-23 00:48:22 +03:00
}
2019-03-17 21:52:56 +00:00
disableHlsVideoAudioCodecs.push('ac3');
disableHlsVideoAudioCodecs.push('eac3');
disableHlsVideoAudioCodecs.push('opus');
2018-10-23 00:48:22 +03:00
}
return {
enableMkvProgressive: false,
disableHlsVideoAudioCodecs: disableHlsVideoAudioCodecs
};
}
2019-03-17 21:52:56 +00:00
2020-12-09 12:33:46 -05:00
function getDeviceProfile(item) {
return new Promise(function (resolve) {
let profile;
2019-03-17 21:52:56 +00:00
if (window.NativeShell) {
2021-08-10 12:43:24 -04:00
profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder, Package.version);
} else {
const builderOpts = getBaseProfileOptions(item);
profile = profileBuilder(builderOpts);
}
2019-03-17 21:52:56 +00:00
const maxVideoWidth = appSettings.maxVideoWidth();
const maxTranscodingVideoWidth = maxVideoWidth < 0 ? appHost.screen()?.maxAllowedWidth : maxVideoWidth;
if (maxTranscodingVideoWidth) {
profile.TranscodingProfiles.forEach((transcodingProfile) => {
if (transcodingProfile.Type === 'Video') {
transcodingProfile.Conditions = (transcodingProfile.Conditions || []).filter((condition) => {
return condition.Property !== 'Width';
});
transcodingProfile.Conditions.push({
Condition: 'LessThanEqual',
Property: 'Width',
Value: maxTranscodingVideoWidth.toString(),
IsRequired: false
});
}
});
}
resolve(profile);
});
}
2018-10-23 00:48:22 +03:00
function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1');
}
2018-10-23 00:48:22 +03:00
function replaceAll(originalString, strReplace, strWith) {
const strReplace2 = escapeRegExp(strReplace);
const reg = new RegExp(strReplace2, 'ig');
return originalString.replace(reg, strWith);
}
function generateDeviceId() {
const keys = [];
2022-05-20 01:37:11 -07:00
keys.push(navigator.userAgent);
keys.push(new Date().getTime());
if (window.btoa) {
2022-09-30 17:57:47 -04:00
return replaceAll(btoa(keys.join('|')), '=', '1');
2018-10-23 00:48:22 +03:00
}
return new Date().getTime();
}
2019-03-17 21:52:56 +00:00
function getDeviceId() {
if (!deviceId) {
const key = '_deviceId2';
2019-03-17 21:52:56 +00:00
deviceId = appSettings.get(key);
if (!deviceId) {
deviceId = generateDeviceId();
appSettings.set(key, deviceId);
}
2018-10-23 00:48:22 +03:00
}
return deviceId;
}
2019-03-17 21:52:56 +00:00
function getDeviceName() {
if (!deviceName) {
if (browser.tizen) {
deviceName = 'Samsung Smart TV';
} else if (browser.web0s) {
deviceName = 'LG Smart TV';
} else if (browser.operaTv) {
deviceName = 'Opera TV';
} else if (browser.xboxOne) {
deviceName = 'Xbox One';
} else if (browser.ps4) {
deviceName = 'Sony PS4';
} else if (browser.chrome) {
deviceName = 'Chrome';
} else if (browser.edgeChromium) {
deviceName = 'Edge Chromium';
} else if (browser.edge) {
deviceName = 'Edge';
} else if (browser.firefox) {
deviceName = 'Firefox';
} else if (browser.opera) {
deviceName = 'Opera';
} else if (browser.safari) {
deviceName = 'Safari';
} else {
deviceName = 'Web Browser';
}
2019-03-17 21:52:56 +00:00
if (browser.ipad) {
deviceName += ' iPad';
} else if (browser.iphone) {
deviceName += ' iPhone';
} else if (browser.android) {
deviceName += ' Android';
}
2018-10-23 00:48:22 +03:00
}
return deviceName;
}
2019-03-17 21:52:56 +00:00
function supportsVoiceInput() {
if (!browser.tv) {
return window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.oSpeechRecognition || window.msSpeechRecognition;
2018-10-23 00:48:22 +03:00
}
return false;
}
2019-03-17 21:52:56 +00:00
function supportsFullscreen() {
if (browser.tv) {
2019-03-17 21:52:56 +00:00
return false;
2018-10-23 00:48:22 +03:00
}
const element = document.documentElement;
return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement('video').webkitEnterFullscreen;
}
2018-10-23 00:48:22 +03:00
function getDefaultLayout() {
return 'desktop';
}
2019-03-17 21:52:56 +00:00
function supportsHtmlMediaAutoplay() {
if (browser.edgeUwp || browser.tizen || browser.web0s || browser.orsay || browser.operaTv || browser.ps4 || browser.xboxOne) {
2020-01-24 18:45:37 +09:00
return true;
2018-10-23 00:48:22 +03:00
}
if (browser.mobile) {
return false;
}
return true;
}
function supportsCue() {
try {
const video = document.createElement('video');
const style = document.createElement('style');
style.textContent = 'video::cue {background: inherit}';
document.body.appendChild(style);
document.body.appendChild(video);
2018-10-23 00:48:22 +03:00
const cue = window.getComputedStyle(video, '::cue').background;
document.body.removeChild(style);
document.body.removeChild(video);
return !!cue.length;
} catch (err) {
console.error('error detecting cue support: ' + err);
return false;
2018-10-23 00:48:22 +03:00
}
}
2018-10-23 00:48:22 +03:00
function onAppVisible() {
if (isHidden) {
isHidden = false;
2020-09-08 02:05:02 -04:00
Events.trigger(appHost, 'resume');
2018-10-23 00:48:22 +03:00
}
}
function onAppHidden() {
if (!isHidden) {
isHidden = true;
}
}
2019-03-17 21:52:56 +00:00
const supportedFeatures = function () {
const features = [];
2019-03-17 21:52:56 +00:00
if (navigator.share) {
features.push('sharing');
}
2019-03-17 21:52:56 +00:00
if (!browser.edgeUwp && !browser.tv && !browser.xboxOne && !browser.ps4) {
features.push('filedownload');
}
2019-03-17 21:52:56 +00:00
if (browser.operaTv || browser.tizen || browser.orsay || browser.web0s) {
features.push('exit');
} else {
features.push('plugins');
}
2019-03-17 21:52:56 +00:00
if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.ps4) {
features.push('externallinks');
features.push('externalpremium');
}
2019-03-17 21:52:56 +00:00
if (!browser.operaTv) {
features.push('externallinkdisplay');
}
2019-03-17 21:52:56 +00:00
if (supportsVoiceInput()) {
features.push('voiceinput');
}
2019-03-17 21:52:56 +00:00
if (supportsHtmlMediaAutoplay()) {
features.push('htmlaudioautoplay');
features.push('htmlvideoautoplay');
}
2019-03-17 21:52:56 +00:00
if (browser.edgeUwp) {
features.push('sync');
}
2019-03-17 21:52:56 +00:00
if (supportsFullscreen()) {
features.push('fullscreenchange');
}
2019-03-17 21:52:56 +00:00
if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) {
features.push('physicalvolumecontrol');
}
2019-03-17 21:52:56 +00:00
if (!browser.tv && !browser.xboxOne && !browser.ps4) {
features.push('remotecontrol');
}
2019-03-17 21:52:56 +00:00
if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.edgeUwp) {
features.push('remotevideo');
}
2019-03-17 21:52:56 +00:00
features.push('displaylanguage');
features.push('otherapppromotions');
features.push('displaymode');
features.push('targetblank');
features.push('screensaver');
2020-02-16 12:17:13 +09:00
2020-08-06 22:09:24 +02:00
webSettings.getMultiServer().then(enabled => {
if (enabled) features.push('multiserver');
});
2019-03-17 21:52:56 +00:00
if (!browser.orsay && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) {
features.push('subtitleappearancesettings');
}
2019-03-17 21:52:56 +00:00
if (!browser.orsay) {
features.push('subtitleburnsettings');
}
2019-03-17 21:52:56 +00:00
if (!browser.tv && !browser.ps4 && !browser.xboxOne) {
features.push('fileinput');
}
2019-03-17 21:52:56 +00:00
if (browser.chrome || browser.edgeChromium) {
features.push('chromecast');
}
2019-03-17 21:52:56 +00:00
return features;
}();
/**
* Do exit according to platform
*/
function doExit() {
try {
if (window.NativeShell?.AppHost?.exit) {
window.NativeShell.AppHost.exit();
} else if (browser.tizen) {
tizen.application.getCurrentApplication().exit();
} else if (browser.web0s) {
webOS.platformBack();
} else {
window.close();
2020-01-21 12:51:33 +03:00
}
} catch (err) {
console.error('error closing application: ' + err);
2020-01-21 12:51:33 +03:00
}
}
2020-01-21 12:51:33 +03:00
let exitPromise;
2020-01-21 12:51:33 +03:00
/**
* Ask user for exit
*/
function askForExit() {
if (exitPromise) {
return;
2020-01-21 12:51:33 +03:00
}
2020-08-14 08:46:34 +02:00
import('../components/actionSheet/actionSheet').then((actionsheet) => {
exitPromise = actionsheet.show({
title: globalize.translate('MessageConfirmAppExit'),
items: [
{id: 'yes', name: globalize.translate('Yes')},
{id: 'no', name: globalize.translate('No')}
]
}).then(function (value) {
if (value === 'yes') {
2020-01-21 12:51:33 +03:00
doExit();
2019-03-17 21:52:56 +00:00
}
}).finally(function () {
exitPromise = null;
});
});
}
let deviceId;
let deviceName;
2020-08-16 20:24:45 +02:00
export const appHost = {
getWindowState: function () {
return document.windowState || 'Normal';
},
setWindowState: function () {
alert('setWindowState is not supported and should not be called');
},
exit: function () {
if (!!window.appMode && browser.tizen) {
askForExit();
} else {
doExit();
}
},
supports: function (command) {
if (window.NativeShell) {
return window.NativeShell.AppHost.supports(command);
}
2019-03-17 21:52:56 +00:00
return supportedFeatures.indexOf(command.toLowerCase()) !== -1;
},
preferVisualCards: browser.android || browser.chrome,
getDefaultLayout: function () {
if (window.NativeShell) {
return window.NativeShell.AppHost.getDefaultLayout();
}
2019-03-17 21:52:56 +00:00
return getDefaultLayout();
},
getDeviceProfile: getDeviceProfile,
init: function () {
if (window.NativeShell) {
return window.NativeShell.AppHost.init();
}
2019-03-17 21:52:56 +00:00
return {
deviceId: getDeviceId(),
deviceName: getDeviceName()
};
},
deviceName: function () {
return window.NativeShell?.AppHost?.deviceName
2021-04-10 22:01:37 -04:00
? window.NativeShell.AppHost.deviceName() : getDeviceName();
},
deviceId: function () {
return window.NativeShell?.AppHost?.deviceId
2021-04-10 22:01:37 -04:00
? window.NativeShell.AppHost.deviceId() : getDeviceId();
},
appName: function () {
return window.NativeShell?.AppHost?.appName
? window.NativeShell.AppHost.appName() : appName;
},
appVersion: function () {
return window.NativeShell?.AppHost?.appVersion
2021-08-10 12:43:24 -04:00
? window.NativeShell.AppHost.appVersion() : Package.version;
},
getPushTokenInfo: function () {
return {};
},
setUserScalable: function (scalable) {
if (!browser.tv) {
const att = scalable ? 'width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes' : 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no';
document.querySelector('meta[name=viewport]').setAttribute('content', att);
}
},
screen: () => {
let hostScreen = null;
const appHostImpl = window.NativeShell?.AppHost;
if (appHostImpl?.screen) {
hostScreen = appHostImpl.screen();
} else if (window.screen && !browser.tv) {
hostScreen = {
width: Math.floor(window.screen.width * window.devicePixelRatio),
height: Math.floor(window.screen.height * window.devicePixelRatio)
};
}
if (hostScreen) {
// Use larger dimension to account for screen orientation changes
hostScreen.maxAllowedWidth = Math.max(hostScreen.width, hostScreen.height);
}
return hostScreen;
}
};
let isHidden = false;
let hidden;
let visibilityChange;
if (typeof document.hidden !== 'undefined') { /* eslint-disable-line compat/compat */
hidden = 'hidden';
visibilityChange = 'visibilitychange';
} else if (typeof document.webkitHidden !== 'undefined') {
hidden = 'webkitHidden';
visibilityChange = 'webkitvisibilitychange';
}
document.addEventListener(visibilityChange, function () {
/* eslint-disable-next-line compat/compat */
if (document[hidden]) {
onAppHidden();
} else {
onAppVisible();
2019-01-24 22:26:24 +09:00
}
}, false);
2020-08-25 10:12:35 +09:00
if (window.addEventListener) {
window.addEventListener('focus', onAppVisible);
window.addEventListener('blur', onAppHidden);
}
2019-03-17 21:52:56 +00:00
// load app host on module load
appHost.init();