diff --git a/src/bower_components/emby-apiclient/connectionmanager.js b/src/bower_components/emby-apiclient/connectionmanager.js index 39824d31f7..93e9673a46 100644 --- a/src/bower_components/emby-apiclient/connectionmanager.js +++ b/src/bower_components/emby-apiclient/connectionmanager.js @@ -269,12 +269,15 @@ define(["events", "apiclient", "appStorage"], function(events, apiClientFactory, }); resolve(servers) }; - require(["serverdiscovery"], function(serverDiscovery) { - serverDiscovery.findServers(1e3).then(onFinish, function() { + + if (window.NativeShell && typeof window.NativeShell.findServers === 'function') { + window.NativeShell.findServers(1e3).then(onFinish, function() { onFinish([]) - }) - }) - }) + }); + } else { + resolve([]); + } + }); } function convertEndpointAddressToManualAddress(info) { diff --git a/src/bower_components/emby-apiclient/fileupload.js b/src/bower_components/emby-apiclient/fileupload.js deleted file mode 100644 index 91f77395e6..0000000000 --- a/src/bower_components/emby-apiclient/fileupload.js +++ /dev/null @@ -1,8 +0,0 @@ -define([], function() { - "use strict"; - - function FileUpload() {} - return FileUpload.prototype.upload = function(file, url) { - return Promise.reject() - }, FileUpload -}); \ No newline at end of file diff --git a/src/bower_components/emby-apiclient/serverdiscovery.js b/src/bower_components/emby-apiclient/serverdiscovery.js deleted file mode 100644 index 4f18505ecc..0000000000 --- a/src/bower_components/emby-apiclient/serverdiscovery.js +++ /dev/null @@ -1,8 +0,0 @@ -define([], function() { - "use strict"; - return { - findServers: function(timeoutMs) { - return Promise.resolve([]) - } - } -}); \ No newline at end of file diff --git a/src/bower_components/emby-apiclient/wakeonlan.js b/src/bower_components/emby-apiclient/wakeonlan.js deleted file mode 100644 index 58c01e61a5..0000000000 --- a/src/bower_components/emby-apiclient/wakeonlan.js +++ /dev/null @@ -1,15 +0,0 @@ -define([], function() { - "use strict"; - - function send(info) { - return Promise.reject() - } - - function isSupported() { - return !1 - } - return { - send: send, - isSupported: isSupported - } -}); \ No newline at end of file diff --git a/src/components/apphost.js b/src/components/apphost.js index 9cffd2d8a4..369dff0f6f 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -1,109 +1,191 @@ -define(["appSettings", "browser", "events", "htmlMediaHelper"], function(appSettings, browser, events, htmlMediaHelper) { +define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSettings, browser, events, htmlMediaHelper) { "use strict"; function getBaseProfileOptions(item) { var disableHlsVideoAudioCodecs = []; - return item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType) && ((browser.edge || browser.msie) && disableHlsVideoAudioCodecs.push("mp3"), disableHlsVideoAudioCodecs.push("ac3"), disableHlsVideoAudioCodecs.push("eac3"), disableHlsVideoAudioCodecs.push("opus")), { - enableMkvProgressive: !1, - disableHlsVideoAudioCodecs: disableHlsVideoAudioCodecs + + if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) { + if (browser.edge || browser.msie) { + disableHlsVideoAudioCodecs.push("mp3"); + } + + disableHlsVideoAudioCodecs.push("ac3"); + disableHlsVideoAudioCodecs.push("eac3"); + disableHlsVideoAudioCodecs.push("opus"); } + + return { + enableMkvProgressive: false, + disableHlsVideoAudioCodecs: disableHlsVideoAudioCodecs + }; } function getDeviceProfileForWindowsUwp(item) { - return new Promise(function(resolve, reject) { - require(["browserdeviceprofile", "environments/windows-uwp/mediacaps"], function(profileBuilder, uwpMediaCaps) { + return new Promise(function (resolve, reject) { + require(["browserdeviceprofile", "environments/windows-uwp/mediacaps"], function (profileBuilder, uwpMediaCaps) { var profileOptions = getBaseProfileOptions(item); - profileOptions.supportsDts = uwpMediaCaps.supportsDTS(), profileOptions.supportsTrueHd = uwpMediaCaps.supportsDolby(), profileOptions.audioChannels = uwpMediaCaps.getAudioChannels(), resolve(profileBuilder(profileOptions)) - }) - }) + profileOptions.supportsDts = uwpMediaCaps.supportsDTS(); + profileOptions.supportsTrueHd = uwpMediaCaps.supportsDolby(); + profileOptions.audioChannels = uwpMediaCaps.getAudioChannels(); + resolve(profileBuilder(profileOptions)); + }); + }); } function getDeviceProfile(item, options) { - return options = options || {}, self.Windows ? getDeviceProfileForWindowsUwp(item) : new Promise(function(resolve, reject) { - require(["browserdeviceprofile"], function(profileBuilder) { - var profile = profileBuilder(getBaseProfileOptions(item)); - item && !options.isRetry && "allcomplexformats" !== appSettings.get("subtitleburnin") && (browser.orsay || browser.tizen || (profile.SubtitleProfiles.push({ - Format: "ass", - Method: "External" - }), profile.SubtitleProfiles.push({ - Format: "ssa", - Method: "External" - }))), resolve(profile) - }) - }) + options = options || {}; + + if (self.Windows) { + return getDeviceProfileForWindowsUwp(item); + } + + return new Promise(function (resolve) { + require(["browserdeviceprofile"], function (profileBuilder) { + var profile; + + if (window.NativeShell) { + profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); + } else { + profile = profileBuilder(getBaseProfileOptions(item)); + + if (item && !options.isRetry && "allcomplexformats" !== appSettings.get("subtitleburnin")) { + if (!browser.orsay && !browser.tizen) { + profile.SubtitleProfiles.push({ + Format: "ass", + Method: "External" + }); + profile.SubtitleProfiles.push({ + Format: "ssa", + Method: "External" + }); + } + } + } + + resolve(profile); + }); + }); } function escapeRegExp(str) { - return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1") + return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); } function replaceAll(originalString, strReplace, strWith) { - var strReplace2 = escapeRegExp(strReplace), - reg = new RegExp(strReplace2, "ig"); - return originalString.replace(reg, strWith) + var strReplace2 = escapeRegExp(strReplace); + var reg = new RegExp(strReplace2, "ig"); + return originalString.replace(reg, strWith); } function generateDeviceId() { var keys = []; - if (keys.push(navigator.userAgent), keys.push((new Date).getTime()), self.btoa) { + + if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), self.btoa) { var result = replaceAll(btoa(keys.join("|")), "=", "1"); - return Promise.resolve(result) + return Promise.resolve(result); } - return Promise.resolve((new Date).getTime()) + + return Promise.resolve(new Date().getTime()); } function getDeviceId() { - var key = "_deviceId2", - deviceId = appSettings.get(key); - return deviceId ? Promise.resolve(deviceId) : generateDeviceId().then(function(deviceId) { - return appSettings.set(key, deviceId), deviceId - }) + var key = "_deviceId2"; + var deviceId = appSettings.get(key); + + if (deviceId) { + return Promise.resolve(deviceId); + } + + return generateDeviceId().then(function (deviceId) { + appSettings.set(key, deviceId); + return deviceId; + }); } function getDeviceName() { var deviceName; - return deviceName = browser.tizen ? "Samsung Smart TV" : browser.web0s ? "LG Smart TV" : browser.operaTv ? "Opera TV" : browser.xboxOne ? "Xbox One" : browser.ps4 ? "Sony PS4" : browser.chrome ? "Chrome" : browser.edge ? "Edge" : browser.firefox ? "Firefox" : browser.msie ? "Internet Explorer" : browser.opera ? "Opera" : "Web Browser", browser.ipad ? deviceName += " Ipad" : browser.iphone ? deviceName += " Iphone" : browser.android && (deviceName += " Android"), deviceName + deviceName = browser.tizen ? "Samsung Smart TV" : browser.web0s ? "LG Smart TV" : browser.operaTv ? "Opera TV" : browser.xboxOne ? "Xbox One" : browser.ps4 ? "Sony PS4" : browser.chrome ? "Chrome" : browser.edge ? "Edge" : browser.firefox ? "Firefox" : browser.msie ? "Internet Explorer" : browser.opera ? "Opera" : "Web Browser"; + + if (browser.ipad) { + deviceName += " Ipad"; + } else { + if (browser.iphone) { + deviceName += " Iphone"; + } else { + if (browser.android) { + deviceName += " Android"; + } + } + } + + return deviceName; } function supportsVoiceInput() { - return !browser.tv && (window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.oSpeechRecognition || window.msSpeechRecognition) + if (!browser.tv) { + return window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.oSpeechRecognition || window.msSpeechRecognition; + } + + return false; } function supportsFullscreen() { - if (browser.tv) return !1; + if (browser.tv) { + return false; + } + var element = document.documentElement; - return !!(element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || !!document.createElement("video").webkitEnterFullscreen + return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement("video").webkitEnterFullscreen; } function getSyncProfile() { - return new Promise(function(resolve, reject) { - require(["browserdeviceprofile", "appSettings"], function(profileBuilder, appSettings) { - var profile = profileBuilder(); - profile.MaxStaticMusicBitrate = appSettings.maxStaticMusicBitrate(), resolve(profile) - }) - }) + return new Promise(function (resolve) { + require(["browserdeviceprofile", "appSettings"], function (profileBuilder, appSettings) { + var profile; + + if (window.NativeShell) { + profile = window.NativeShell.AppHost.getSyncProfile(profileBuilder, appSettings); + } else { + profile = profileBuilder(); + profile.MaxStaticMusicBitrate = appSettings.maxStaticMusicBitrate(); + } + + resolve(profile); + }); + }); } function getDefaultLayout() { - return "desktop" + return "desktop"; } function supportsHtmlMediaAutoplay() { - if (browser.edgeUwp || browser.tizen || browser.web0s || browser.orsay || browser.operaTv || browser.ps4 || browser.xboxOne) return !0; - if (browser.mobile) return !1; + if (browser.edgeUwp || browser.tizen || browser.web0s || browser.orsay || browser.operaTv || browser.ps4 || browser.xboxOne) { + return true; + } + + if (browser.mobile) { + return false; + } + var savedResult = appSettings.get(htmlMediaAutoplayAppStorageKey); - return "true" === savedResult || "false" !== savedResult && null + return "true" === savedResult || "false" !== savedResult && null; } function cueSupported() { try { - var video = document.createElement("video"), - style = document.createElement("style"); - style.textContent = "video::cue {background: inherit}", document.body.appendChild(style), document.body.appendChild(video); + var video = document.createElement("video"); + var style = document.createElement("style"); + style.textContent = "video::cue {background: inherit}"; + document.body.appendChild(style); + document.body.appendChild(video); var cue = window.getComputedStyle(video, "::cue").background; - return document.body.removeChild(style), document.body.removeChild(video), !!cue.length + document.body.removeChild(style); + document.body.removeChild(video); + return !!cue.length; } catch (err) { - return console.log("Error detecting cue support:" + err), !1 + console.log("Error detecting cue support:" + err); + return false; } } @@ -123,41 +205,104 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function(appSett } var htmlMediaAutoplayAppStorageKey = "supportshtmlmediaautoplay0"; - var supportedFeatures = function() { + + var supportedFeatures = function () { var features = []; - navigator.share && features.push("sharing"); - browser.edgeUwp || browser.tv || browser.xboxOne || browser.ps4 || features.push("filedownload"); - browser.operaTv || browser.tizen || browser.orsay || browser.web0s - ? features.push("exit") - : (features.push("exitmenu"), features.push("plugins")); - browser.operaTv || browser.tizen || browser.orsay || browser.web0s || browser.ps4 || (features.push("externallinks"), features.push("externalpremium")); - browser.operaTv || features.push("externallinkdisplay"); - supportsVoiceInput() && features.push("voiceinput"); - !browser.tv && !browser.xboxOne && browser.ps4, supportsHtmlMediaAutoplay() && (features.push("htmlaudioautoplay"), features.push("htmlvideoautoplay")); - browser.edgeUwp && features.push("sync"); - supportsFullscreen() && features.push("fullscreenchange"); - (browser.chrome || browser.edge && !browser.slow) && (browser.noAnimation || browser.edgeUwp || browser.xboxOne || features.push("imageanalysis")); - (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) && features.push("physicalvolumecontrol"); - browser.tv || browser.xboxOne || browser.ps4 || features.push("remotecontrol"); - browser.operaTv || browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp || features.push("remotevideo"); + + if (navigator.share) { + features.push("sharing"); + } + + if (!browser.edgeUwp && !browser.tv && !browser.xboxOne && !browser.ps4) { + features.push("filedownload"); + } + + if (browser.operaTv || browser.tizen || browser.orsay || browser.web0s) { + features.push("exit"); + } else { + features.push("exitmenu"); + features.push("plugins"); + } + + if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.ps4) { + features.push("externallinks"); + features.push("externalpremium"); + } + + if (!browser.operaTv) { + features.push("externallinkdisplay"); + } + + if (supportsVoiceInput()) { + features.push("voiceinput"); + } + + if (!browser.tv && !browser.xboxOne) { + browser.ps4; + } + + if (supportsHtmlMediaAutoplay()) { + features.push("htmlaudioautoplay"); + features.push("htmlvideoautoplay"); + } + + if (browser.edgeUwp) { + features.push("sync"); + } + + if (supportsFullscreen()) { + features.push("fullscreenchange"); + } + + if (browser.chrome || browser.edge && !browser.slow) { + if (!browser.noAnimation && !browser.edgeUwp && !browser.xboxOne) { + features.push("imageanalysis"); + } + } + + if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) { + features.push("physicalvolumecontrol"); + } + + if (!browser.tv && !browser.xboxOne && !browser.ps4) { + features.push("remotecontrol"); + } + + if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.edgeUwp) { + features.push("remotevideo"); + } + features.push("displaylanguage"); features.push("otherapppromotions"); - features.push("targetblank"); - // allows users to connect to more than one server + features.push("targetblank"); // allows users to connect to more than one server //features.push("multiserver"); - browser.orsay || browser.tizen || browser.msie || !(browser.firefox || browser.ps4 || browser.edge || cueSupported()) || features.push("subtitleappearancesettings"); - browser.orsay || browser.tizen || features.push("subtitleburnsettings"); - browser.tv || browser.ps4 || browser.xboxOne || features.push("fileinput"); - browser.chrome && features.push("chromecast"); + + if (!browser.orsay && !browser.tizen && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || cueSupported())) { + features.push("subtitleappearancesettings"); + } + + if (!browser.orsay && !browser.tizen) { + features.push("subtitleburnsettings"); + } + + if (!browser.tv && !browser.ps4 && !browser.xboxOne) { + features.push("fileinput"); + } + + if (browser.chrome) { + features.push("chromecast"); + } + return features; }(); + if (supportedFeatures.indexOf("htmlvideoautoplay") === -1 && supportsHtmlMediaAutoplay() !== false) { - require(["autoPlayDetect"], function(autoPlayDetect) { - autoPlayDetect.supportsHtmlMediaAutoplay().then(function() { + require(["autoPlayDetect"], function (autoPlayDetect) { + autoPlayDetect.supportsHtmlMediaAutoplay().then(function () { appSettings.set(htmlMediaAutoplayAppStorageKey, "true"); supportedFeatures.push("htmlvideoautoplay"); supportedFeatures.push("htmlaudioautoplay"); - }, function() { + }, function () { appSettings.set(htmlMediaAutoplayAppStorageKey, "false"); }); }); @@ -169,73 +314,171 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function(appSett var visibilityState; var appVersion = window.dashboardVersion || "3.0"; var appHost = { - getWindowState: function() { - return document.windowState || "Normal" + getWindowState: function () { + return document.windowState || "Normal"; }, - setWindowState: function(state) { - alert("setWindowState is not supported and should not be called") + setWindowState: function (state) { + alert("setWindowState is not supported and should not be called"); }, - exit: function() { - if (browser.tizen) try { - tizen.application.getCurrentApplication().exit() - } catch (err) { - console.log("error closing application: " + err) - } else window.close() + exit: function () { + if (window.NativeShell) { + window.NativeShell.AppHost.exit(); + } else if (browser.tizen) { + try { + tizen.application.getCurrentApplication().exit(); + } catch (err) { + console.log("error closing application: " + err); + } + } else { + window.close(); + } }, - supports: function(command) { - return -1 !== supportedFeatures.indexOf(command.toLowerCase()) + supports: function (command) { + if (window.NativeShell) { + return window.NativeShell.AppHost.supports(command); + } + + return -1 !== supportedFeatures.indexOf(command.toLowerCase()); }, preferVisualCards: browser.android || browser.chrome, moreIcon: browser.android ? "dots-vert" : "dots-horiz", getSyncProfile: getSyncProfile, - getDefaultLayout: getDefaultLayout, + getDefaultLayout: function () { + if (window.NativeShell) { + return window.NativeShell.AppHost.getDefaultLayout(); + } + + return getDefaultLayout() + }, getDeviceProfile: getDeviceProfile, - init: function() { - return deviceName = getDeviceName(), getDeviceId().then(function(resolvedDeviceId) { - deviceId = resolvedDeviceId - }) + init: function () { + if (window.NativeShell) { + return window.NativeShell.AppHost.init(); + } + + deviceName = getDeviceName(); + return getDeviceId().then(function (resolvedDeviceId) { + deviceId = resolvedDeviceId; + }); }, - deviceName: function() { - return deviceName + deviceName: function () { + return window.NativeShell ? window.NativeShell.AppHost.deviceName() : deviceName; }, - deviceId: function() { - return deviceId + deviceId: function () { + return window.NativeShell ? window.NativeShell.AppHost.deviceId() : deviceId; }, - appName: function() { - return "Jellyfin Web" + appName: function () { + return window.NativeShell ? window.NativeShell.AppHost.appName() : "Jellyfin Web"; }, - appVersion: function() { - return appVersion + appVersion: function () { + return window.NativeShell ? window.NativeShell.AppHost.appVersion() : appVersion; }, - getPushTokenInfo: function() { - return {} + getPushTokenInfo: function () { + return {}; }, - setThemeColor: function(color) { + setThemeColor: function (color) { var metaThemeColor = document.querySelector("meta[name=theme-color]"); - metaThemeColor && metaThemeColor.setAttribute("content", color) - }, - setUserScalable: function(scalable) { - if (!browser.tv) { - var 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) + + if (metaThemeColor) { + metaThemeColor.setAttribute("content", color); } }, - deviceIconUrl: function() { - return browser.edgeUwp, browser.edgeUwp ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/windowsrt.png" : browser.opera || browser.operaTv ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/opera.png" : browser.orsay || browser.tizen ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/samsungtv.png" : browser.web0s ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/lgtv.png" : browser.ps4 ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/ps4.png" : browser.chromecast ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/chromecast.png" : browser.chrome ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/chrome.png" : browser.edge ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/edge.png" : browser.firefox ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/firefox.png" : browser.msie ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/internetexplorer.png" : browser.safari ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/safari.png" : "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/html5.png" + setUserScalable: function (scalable) { + if (!browser.tv) { + var 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); + } + }, + deviceIconUrl: function () { + browser.edgeUwp; + + if (browser.edgeUwp) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/windowsrt.png"; + } + + if (browser.opera || browser.operaTv) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/opera.png"; + } + + if (browser.orsay || browser.tizen) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/samsungtv.png"; + } + + if (browser.web0s) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/lgtv.png"; + } + + if (browser.ps4) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/ps4.png"; + } + + if (browser.chromecast) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/chromecast.png"; + } + + if (browser.chrome) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/chrome.png"; + } + + if (browser.edge) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/edge.png"; + } + + if (browser.firefox) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/firefox.png"; + } + + if (browser.msie) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/internetexplorer.png"; + } + + if (browser.safari) { + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/safari.png"; + } + + return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/html5.png"; } }; - var doc = self.document; - doc && (void 0 !== doc.visibilityState ? (visibilityChange = "visibilitychange", visibilityState = "hidden") : void 0 !== doc.mozHidden ? (visibilityChange = "mozvisibilitychange", visibilityState = "mozVisibilityState") : void 0 !== doc.msHidden ? (visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState") : void 0 !== doc.webkitHidden && (visibilityChange = "webkitvisibilitychange", visibilityState = "webkitVisibilityState")); - var isHidden = false; + if (doc) { - doc.addEventListener(visibilityChange, function() { - document[visibilityState] ? onAppHidden() : onAppVisible() + if (void 0 !== doc.visibilityState) { + visibilityChange = "visibilitychange"; + visibilityState = "hidden"; + } else { + if (void 0 !== doc.mozHidden) { + visibilityChange = "mozvisibilitychange"; + visibilityState = "mozVisibilityState"; + } else { + if (void 0 !== doc.msHidden) { + visibilityChange = "msvisibilitychange"; + visibilityState = "msVisibilityState"; + } else { + if (void 0 !== doc.webkitHidden) { + visibilityChange = "webkitvisibilitychange"; + visibilityState = "webkitVisibilityState"; + } + } + } + } + } + + var isHidden = false; + + if (doc) { + doc.addEventListener(visibilityChange, function () { + if (document[visibilityState]) { + onAppHidden(); + } else { + onAppVisible(); + } }); } + if (self.addEventListener) { self.addEventListener("focus", onAppVisible); self.addEventListener("blur", onAppHidden); } + return appHost; }); diff --git a/src/components/filedownloader.js b/src/components/filedownloader.js index c8e3011be2..c5810b460e 100644 --- a/src/components/filedownloader.js +++ b/src/components/filedownloader.js @@ -4,9 +4,15 @@ define(['multi-download'], function (multiDownload) { return { download: function (items) { - multiDownload(items.map(function (item) { - return item.url; - })); + if (window.NativeShell) { + items.map(function (item) { + window.NativeShell.downloadFile(item.url); + }); + } else { + multiDownload(items.map(function (item) { + return item.url; + })); + } } }; }); \ No newline at end of file diff --git a/src/components/filesystem.js b/src/components/filesystem.js index 4489d2921f..e022a1c6d0 100644 --- a/src/components/filesystem.js +++ b/src/components/filesystem.js @@ -3,10 +3,14 @@ define([], function () { return { fileExists: function (path) { - return Promise.reject(); + if (window.NativeShell && window.NativeShell.FileSystem) { + return window.NativeShell.FileSystem.fileExists(path); + } }, directoryExists: function (path) { - return Promise.reject(); + if (window.NativeShell && window.NativeShell.FileSystem) { + return window.NativeShell.FileSystem.directoryExists(path); + } } }; }); \ No newline at end of file diff --git a/src/components/playback/mediasession.js b/src/components/playback/mediasession.js index 8e2f7d0c01..b92e581b25 100644 --- a/src/components/playback/mediasession.js +++ b/src/components/playback/mediasession.js @@ -1,6 +1,11 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], function (playbackManager, nowPlayingHelper, events, connectionManager) { "use strict"; + // no support for mediaSession + if (!navigator.mediaSession && !window.NativeShell) { + return; + } + // Reports media playback to the device for lock screen control var currentPlayer; @@ -64,15 +69,16 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f return null; } - function pushImageUrl(item, height, list) { - - var imageOptions = { - height: height - }; - + function pushImageUrl(item, imageOptions, list) { var url = seriesImageUrl(item, imageOptions) || imageUrl(item, imageOptions); + if (url) { - list.push({ src: url, sizes: height + 'x' + height }); + var height = imageOptions.height || imageOptions.maxHeight; + + list.push({ + src: url, + sizes: height + 'x' + height + }); } } @@ -80,12 +86,12 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f var list = []; - pushImageUrl(item, 96, list); - pushImageUrl(item, 128, list); - pushImageUrl(item, 192, list); - pushImageUrl(item, 256, list); - pushImageUrl(item, 384, list); - pushImageUrl(item, 512, list); + pushImageUrl(item, {height: 96}, list); + pushImageUrl(item, {height: 128}, list); + pushImageUrl(item, {height: 192}, list); + pushImageUrl(item, {height: 256}, list); + pushImageUrl(item, {height: 384}, list); + pushImageUrl(item, {height: 512}, list); return list; } @@ -99,6 +105,18 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f return; } + if (eventName == 'init') { // transform "init" event into "timeupdate" to restraint update rate + eventName = 'timeupdate'; + } + + var isVideo = item.MediaType === 'Video'; + var isLocalPlayer = player.isLocalPlayer || false; + + // Local players do their own notifications + if (isLocalPlayer && isVideo) { + return; + } + var playState = state.PlayState || {}; var parts = nowPlayingHelper.getNowPlayingNames(item); @@ -106,8 +124,6 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f var artist = parts.length === 1 ? '' : parts[0].text; var title = parts[parts.length - 1].text; - var isVideo = item.MediaType === 'Video'; - // Switch these two around for video if (isVideo && parts.length > 1) { var temp = artist; @@ -131,18 +147,54 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f var isPaused = playState.IsPaused || false; var canSeek = playState.CanSeek || false; - navigator.mediaSession.metadata = new MediaMetadata({ - title: title, - artist: artist, - album: album, - artwork: getImageUrls(item), - albumArtist: albumArtist, - currentTime: currentTime, - duration: duration, - paused: isPaused, - itemId: itemId, - mediaType: item.MediaType - }); + var now = new Date().getTime(); + + // Don't go crazy reporting position changes + if (eventName == 'timeupdate' && (now - lastUpdateTime) < 5000) { + // Only report if this item hasn't been reported yet, or if there's an actual playback change. + // Don't report on simple time updates + return; + } + + lastUpdateTime = now; + + if (navigator.mediaSession){ + navigator.mediaSession.metadata = new MediaMetadata({ + title: title, + artist: artist, + album: album, + artwork: getImageUrls(item), + albumArtist: albumArtist, + currentTime: currentTime, + duration: duration, + paused: isPaused, + itemId: itemId, + mediaType: item.MediaType + }); + } else { + var imageUrl = []; + pushImageUrl(item, {maxHeight: 400}, imageUrl); + + if (imageUrl.length) { + imageUrl = imageUrl[0].src; + } else { + imageUrl = null; + } + + window.NativeShell.updateMediaSession({ + action: eventName, + isLocalPlayer: isLocalPlayer, + itemId: itemId, + title: title, + artist: artist, + album: album, + duration: duration, + position: currentTime, + imageUrl: imageUrl, + canSeek: canSeek, + isPaused: isPaused + }); + } } function onGeneralEvent(e) { @@ -191,7 +243,13 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f } function hideMediaControls() { - navigator.mediaSession.metadata = null; + lastUpdateTime = 0; + + if (navigator.mediaSession) { + navigator.mediaSession.metadata = null; + } else { + window.NativeShell.hideMediaSession(); + } } function bindToPlayer(player) { @@ -215,34 +273,37 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f events.on(currentPlayer, 'timeupdate', onGeneralEvent); } - function execute(name) { - playbackManager[name](currentPlayer); + if (navigator.mediaSession) { + + function execute(name) { + playbackManager[name](currentPlayer); + } + + navigator.mediaSession.setActionHandler('previoustrack', function () { + execute('previousTrack'); + }); + + navigator.mediaSession.setActionHandler('nexttrack', function () { + execute('nextTrack'); + }); + + navigator.mediaSession.setActionHandler('play', function () { + execute('unpause'); + }); + + navigator.mediaSession.setActionHandler('pause', function () { + execute('pause'); + }); + + navigator.mediaSession.setActionHandler('seekbackward', function () { + execute('rewind'); + }); + + navigator.mediaSession.setActionHandler('seekforward', function () { + execute('fastForward'); + }); } - navigator.mediaSession.setActionHandler('previoustrack', function () { - execute('previousTrack'); - }); - - navigator.mediaSession.setActionHandler('nexttrack', function () { - execute('nextTrack'); - }); - - navigator.mediaSession.setActionHandler('play', function () { - execute('unpause'); - }); - - navigator.mediaSession.setActionHandler('pause', function () { - execute('pause'); - }); - - navigator.mediaSession.setActionHandler('seekbackward', function () { - execute('rewind'); - }); - - navigator.mediaSession.setActionHandler('seekforward', function () { - execute('fastForward'); - }); - events.on(playbackManager, 'playerchange', function () { bindToPlayer(playbackManager.getCurrentPlayer()); diff --git a/src/components/shell.js b/src/components/shell.js index 0083404e19..762039ac42 100644 --- a/src/components/shell.js +++ b/src/components/shell.js @@ -2,8 +2,13 @@ define([], function () { 'use strict'; return { - openUrl: function (url) { - window.open(url, '_blank'); + openUrl: function (url, target) { + if (window.NativeShell) { + window.NativeShell.openUrl(url, target); + } else { + window.open(url, target || '_blank'); + } + }, canExec: false, exec: function (options) { @@ -12,10 +17,14 @@ define([], function () { return Promise.reject(); }, enableFullscreen: function () { - // do nothing since this is for native apps + if (window.NativeShell) { + window.NativeShell.enableFullscreen(); + } }, disableFullscreen: function () { - // do nothing since this is for native apps + if (window.NativeShell) { + window.NativeShell.disableFullscreen(); + } } }; }); \ No newline at end of file diff --git a/src/scripts/site.js b/src/scripts/site.js index f684a01c3e..ae9e04553c 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -429,11 +429,7 @@ var AppInfo = {}; var apiClientBowerPath = bowerPath + "/emby-apiclient"; var componentsPath = "components"; - if ("android" === self.appMode) { - define("filesystem", ["cordova/filesystem"], returnFirstDependency); - } else { - define("filesystem", [componentsPath + "/filesystem"], returnFirstDependency); - } + define("filesystem", [componentsPath + "/filesystem"], returnFirstDependency); if (window.IntersectionObserver && !browser.edge) { define("lazyLoader", [componentsPath + "/lazyloader/lazyloader-intersectionobserver"], returnFirstDependency); @@ -441,11 +437,7 @@ var AppInfo = {}; define("lazyLoader", [componentsPath + "/lazyloader/lazyloader-scroll"], returnFirstDependency); } - if ("android" === self.appMode) { - define("shell", ["cordova/shell"], returnFirstDependency); - } else { - define("shell", [componentsPath + "/shell"], returnFirstDependency); - } + define("shell", [componentsPath + "/shell"], returnFirstDependency); if ("cordova" === self.appMode || "android" === self.appMode) { define("apiclientcore", ["bower_components/emby-apiclient/apiclient"], returnFirstDependency); @@ -462,17 +454,7 @@ var AppInfo = {}; define("registerElement", [bowerPath + "/document-register-element/build/document-register-element"], returnFirstDependency); } - if ("cordova" === self.appMode || "android" === self.appMode) { - define("serverdiscovery", ["cordova/serverdiscovery"], returnFirstDependency); - } else { - define("serverdiscovery", [apiClientBowerPath + "/serverdiscovery"], returnFirstDependency); - } - - if ("cordova" === self.appMode && browser.iOSVersion && browser.iOSVersion < 11) { - define("imageFetcher", ["cordova/imagestore"], returnFirstDependency); - } else { - define("imageFetcher", [componentsPath + "/images/basicimagefetcher"], returnFirstDependency); - } + define("imageFetcher", [componentsPath + "/images/basicimagefetcher"], returnFirstDependency); var preferNativeAlerts = browser.tv; @@ -500,13 +482,7 @@ var AppInfo = {}; } define("multi-download", [componentsPath + "/multidownload"], returnFirstDependency); - - if ("android" === self.appMode) { - define("fileDownloader", ["cordova/filedownloader"], returnFirstDependency); - } else { - define("fileDownloader", [componentsPath + "/filedownloader"], returnFirstDependency); - } - + define("fileDownloader", [componentsPath + "/filedownloader"], returnFirstDependency); define("localassetmanager", [apiClientBowerPath + "/localassetmanager"], returnFirstDependency); if ("cordova" === self.appMode || "android" === self.appMode) { @@ -515,33 +491,12 @@ var AppInfo = {}; define("castSenderApiLoader", [], getCastSenderApiLoader); } - if (self.Windows) { - define("bgtaskregister", ["environments/windows-uwp/bgtaskregister"], returnFirstDependency); - define("transfermanager", ["environments/windows-uwp/transfermanager"], returnFirstDependency); - define("filerepository", ["environments/windows-uwp/filerepository"], returnFirstDependency); - } else if ("cordova" === self.appMode) { - define("filerepository", ["cordova/filerepository"], returnFirstDependency); - define("transfermanager", ["filerepository"], returnFirstDependency); - } else if ("android" === self.appMode) { - define("transfermanager", ["cordova/transfermanager"], returnFirstDependency); - define("filerepository", ["cordova/filerepository"], returnFirstDependency); - } else { - define("transfermanager", [apiClientBowerPath + "/sync/transfermanager"], returnFirstDependency); - define("filerepository", [apiClientBowerPath + "/sync/filerepository"], returnFirstDependency); - } - - if ("android" === self.appMode) { - define("localsync", ["cordova/localsync"], returnFirstDependency); - } else { - define("localsync", [apiClientBowerPath + "/sync/localsync"], returnFirstDependency); - } + define("transfermanager", [apiClientBowerPath + "/sync/transfermanager"], returnFirstDependency); + define("filerepository", [apiClientBowerPath + "/sync/filerepository"], returnFirstDependency); + define("localsync", [apiClientBowerPath + "/sync/localsync"], returnFirstDependency); } function init() { - if ("android" === self.appMode) { - define("nativedirectorychooser", ["cordova/nativedirectorychooser"], returnFirstDependency); - } - define("livetvcss", ["css!css/livetv.css"], returnFirstDependency); define("detailtablecss", ["css!css/detailtable.css"], returnFirstDependency); define("buttonenabled", ["legacy/buttonenabled"], returnFirstDependency); @@ -1139,14 +1094,6 @@ var AppInfo = {}; "components/youtubeplayer/plugin" ]; - if ("cordova" === self.appMode) { - list.push("cordova/chromecast"); - } - - if ("android" === self.appMode) { - list.push("cordova/externalplayer"); - } - if (appHost.supports("remotecontrol")) { list.push("components/sessionplayer"); @@ -1207,14 +1154,6 @@ var AppInfo = {}; require(["components/thememediaplayer", "scripts/autobackdrops"]); - if ("cordova" === self.appMode || "android" === self.appMode) { - if (browser.android) { - require(["cordova/mediasession", "cordova/chromecast", "cordova/appshortcuts"]); - } else if (browser.safari) { - require(["cordova/mediasession", "cordova/volume", "cordova/statusbar", "cordova/backgroundfetch"]); - } - } - if (!browser.tv && !browser.xboxOne && !browser.ps4) { require(["components/nowplayingbar/nowplayingbar"]); } @@ -1227,11 +1166,7 @@ var AppInfo = {}; require(["components/playback/volumeosd"]); } - if (navigator.mediaSession) { - require(["mediaSession"]); - } - - require(["serverNotifications"]); + require(["mediaSession", "serverNotifications"]); if (!browser.tv && !browser.xboxOne) { require(["components/playback/playbackorientation"]); @@ -1436,13 +1371,9 @@ var AppInfo = {}; return viewManager; }); - if ("cordova" === self.appMode || "android" === self.appMode) { - paths.apphost = "cordova/apphost"; - } else { - paths.apphost = "components/apphost"; - } - + paths.apphost = "components/apphost"; paths.appStorage = getAppStorage(apiClientBowerPath); + requirejs.config({ waitSeconds: 0, map: { @@ -1475,13 +1406,6 @@ var AppInfo = {}; define("viewSettings", [componentsPath + "/viewsettings/viewsettings"], returnFirstDependency); define("filterMenu", [componentsPath + "/filtermenu/filtermenu"], returnFirstDependency); define("sortMenu", [componentsPath + "/sortmenu/sortmenu"], returnFirstDependency); - - if ("cordova" === self.appMode || "android" === self.appMode) { - define("fileupload", ["cordova/fileupload"], returnFirstDependency); - } else { - define("fileupload", [apiClientBowerPath + "/fileupload"], returnFirstDependency); - } - define("connectionmanager", [apiClientBowerPath + "/connectionmanager"]); define("serversync", [apiClientBowerPath + "/sync/serversync"], returnFirstDependency); define("multiserversync", [apiClientBowerPath + "/sync/multiserversync"], returnFirstDependency);