From 02baff1fe0c5a22d9b1563f7101d0a01824e34cd Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 9 Apr 2020 00:35:54 +0800 Subject: [PATCH 0001/2132] add more separate hw decoding toggles --- src/controllers/dashboard/dashboard.js | 12 +++--------- src/controllers/encodingsettings.js | 2 ++ src/encodingsettings.html | 23 ++++++++++++++++------- src/strings/en-us.json | 4 +++- src/strings/zh-cn.json | 4 +++- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/controllers/dashboard/dashboard.js b/src/controllers/dashboard/dashboard.js index 78f5cdca01..addcfc6e7d 100644 --- a/src/controllers/dashboard/dashboard.js +++ b/src/controllers/dashboard/dashboard.js @@ -258,12 +258,6 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa html += ""; html += ""; - if (session.TranscodingInfo && session.TranscodingInfo.Framerate) { - html += '
' + session.TranscodingInfo.Framerate + " fps
"; - } else { - html += '
'; - } - html += '
'; var nowPlayingName = DashboardPage.getNowPlayingName(session); html += '
'; @@ -398,9 +392,9 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } else if (displayPlayMethod === "Transcode") { html += globalize.translate("Transcoding"); - if (session.TranscodingInfo && session.TranscodingInfo.Framerate) { - html += " (" + session.TranscodingInfo.Framerate + " fps)"; - } + if (session.TranscodingInfo && session.TranscodingInfo.Framerate) { + html += " (" + session.TranscodingInfo.Framerate + " fps)"; + } showTranscodingInfo = true; } else if (displayPlayMethod === "DirectPlay") { diff --git a/src/controllers/encodingsettings.js b/src/controllers/encodingsettings.js index 1b27188665..5a540a5e2f 100644 --- a/src/controllers/encodingsettings.js +++ b/src/controllers/encodingsettings.js @@ -5,6 +5,7 @@ define(["jQuery", "loading", "globalize", "dom", "libraryMenu"], function ($, lo Array.prototype.forEach.call(page.querySelectorAll(".chkDecodeCodec"), function (c) { c.checked = -1 !== (config.HardwareDecodingCodecs || []).indexOf(c.getAttribute("data-codec")); }); + page.querySelector("#chkDecodingColorDepth10").checked = config.EnableDecodingColorDepth10; page.querySelector("#chkHardwareEncoding").checked = config.EnableHardwareEncoding; $("#selectVideoDecoder", page).val(config.HardwareAccelerationType); $("#selectThreadCount", page).val(config.EncodingThreadCount); @@ -67,6 +68,7 @@ define(["jQuery", "loading", "globalize", "dom", "libraryMenu"], function ($, lo }), function (c) { return c.getAttribute("data-codec"); }); + config.EnableDecodingColorDepth10 = form.querySelector("#chkDecodingColorDepth10").checked; config.EnableHardwareEncoding = form.querySelector("#chkHardwareEncoding").checked; ApiClient.updateNamedConfiguration("encoding", config).then(function () { updateEncoder(form); diff --git a/src/encodingsettings.html b/src/encodingsettings.html index 5a005d8001..b3c9f42b22 100644 --- a/src/encodingsettings.html +++ b/src/encodingsettings.html @@ -35,36 +35,44 @@

${LabelEnableHardwareDecodingFor}

+
+ +
${EnableDecodingColorDepth10Help}
+
+
+ diff --git a/src/strings/en-us.json b/src/strings/en-us.json index d5fc40ef01..a3b4257ae8 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1496,5 +1496,7 @@ "EveryXMinutes": "Every {0} minutes", "EveryHour": "Every hour", "EveryXHours": "Every {0} hours", - "OnApplicationStartup": "On application startup" + "OnApplicationStartup": "On application startup", + "EnableDecodingColorDepth10": "Enable 10-Bit hardware decoding", + "EnableDecodingColorDepth10Help" : "Enable 10-Bit hardware decoding on supported hardware. Only works for HEVC and VP9 formats. Turn this off if you experience playback issues." } diff --git a/src/strings/zh-cn.json b/src/strings/zh-cn.json index acdfb2d5ed..80ab759081 100644 --- a/src/strings/zh-cn.json +++ b/src/strings/zh-cn.json @@ -1502,5 +1502,7 @@ "YadifBob": "Yadif Bob", "Yadif": "Yadif", "LabelDeinterlaceMethod": "反交错方法:", - "DeinterlaceMethodHelp": "选择对隔行扫描内容进行转码时所用的反交错方法。" + "DeinterlaceMethodHelp": "选择对隔行扫描内容进行转码时所用的反交错方法。", + "EnableDecodingColorDepth10": "启用 10-Bit 硬件解码", + "EnableDecodingColorDepth10Help" : "在支持的硬件上启用 10-Bit 硬件解码。仅对 HEVC 和 VP9 格式起作用。如果你遇到了播放问题,请关闭这个选项。" } From 45ced60cc1340a1c25640ead454e225352db69ac Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 9 Apr 2020 01:15:43 +0800 Subject: [PATCH 0002/2132] fix lint --- src/controllers/dashboard/dashboard.js | 6 +++--- src/encodingsettings.html | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/controllers/dashboard/dashboard.js b/src/controllers/dashboard/dashboard.js index addcfc6e7d..2901a69668 100644 --- a/src/controllers/dashboard/dashboard.js +++ b/src/controllers/dashboard/dashboard.js @@ -392,9 +392,9 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } else if (displayPlayMethod === "Transcode") { html += globalize.translate("Transcoding"); - if (session.TranscodingInfo && session.TranscodingInfo.Framerate) { - html += " (" + session.TranscodingInfo.Framerate + " fps)"; - } + if (session.TranscodingInfo && session.TranscodingInfo.Framerate) { + html += " (" + session.TranscodingInfo.Framerate + " fps)"; + } showTranscodingInfo = true; } else if (displayPlayMethod === "DirectPlay") { diff --git a/src/encodingsettings.html b/src/encodingsettings.html index b3c9f42b22..8e8c8fc5a2 100644 --- a/src/encodingsettings.html +++ b/src/encodingsettings.html @@ -177,4 +177,3 @@ - From b43adb740625f76304423a6c4e563a290981c07a Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Sat, 11 Apr 2020 16:24:40 -0500 Subject: [PATCH 0003/2132] Add quick connect --- src/controllers/auth/login.js | 48 ++++++++++++++++++++++++ src/libraries/apiclient/apiclientcore.js | 23 +++++++++++- src/login.html | 4 ++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/controllers/auth/login.js b/src/controllers/auth/login.js index 4296b8bfb3..746384a64c 100644 --- a/src/controllers/auth/login.js +++ b/src/controllers/auth/login.js @@ -150,6 +150,53 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout }); } + function loginQuickConnect() { + var apiClient = getApiClient(); + var identifier = "" + var interval = 0; + var friendlyName = "test"; + $.get('/QuickConnect/Initiate?FriendlyName=' + friendlyName).then(json => { + if (!json.Secret || !json.Code) { + Dashboard.alert({ + message: json.Error, + title: "Error" + }); + return; + } + + Dashboard.alert({ + message: "Authorize request " + json.Code + " to continue", + title: "Quick Connect Code" + }); + + loading.show(); + + identifier = json.Secret; + interval = setInterval(() => { + $.get('/QuickConnect/Connect?Secret=' + identifier).then(x => { + if(x.Authenticated) { + apiClient.quickConnect(x.Authentication).then((result) => { + var user = result.User; + var serverId = getParameterByName("serverid"); + var newUrl; + + if (user.Policy.IsAdministrator && !serverId) { + newUrl = "dashboard.html"; + } else { + newUrl = "home.html"; + } + + loading.hide(); + Dashboard.onServerChanged(user.Id, result.AccessToken, apiClient); + Dashboard.navigate(newUrl); + clearInterval(interval); + }); + } + }); + }, 5000); + }); + } + view.querySelector("#divUsers").addEventListener("click", function (e) { var card = dom.parentWithClass(e.target, "card"); var cardContent = card ? card.querySelector(".cardContent") : null; @@ -183,6 +230,7 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout Dashboard.navigate("forgotpassword.html"); }); view.querySelector(".btnCancel").addEventListener("click", showVisualForm); + view.querySelector(".btnQuick").addEventListener("click", loginQuickConnect); view.querySelector(".btnManual").addEventListener("click", function () { view.querySelector("#txtManualName").value = ""; showManualForm(view, true); diff --git a/src/libraries/apiclient/apiclientcore.js b/src/libraries/apiclient/apiclientcore.js index 557a4e1033..80be946a60 100644 --- a/src/libraries/apiclient/apiclientcore.js +++ b/src/libraries/apiclient/apiclientcore.js @@ -356,7 +356,28 @@ define(["events", "appStorage"], function(events, appStorage) { instance.onAuthenticated ? instance.onAuthenticated(instance, result).then(afterOnAuthenticated) : afterOnAuthenticated() }, reject) }) - }, ApiClient.prototype.ensureWebSocket = function() { + }, ApiClient.prototype.quickConnect = function (token) { + if (!token) return Promise.reject(); + var url = this.getUrl("Users/AuthenticateWithQuickConnect"), + instance = this; + return new Promise(function(resolve, reject) { + var postData = { + Token: token + }; + instance.ajax({ + type: "POST", + url: url, + data: JSON.stringify(postData), + dataType: "json", + contentType: "application/json" + }).then(function(result) { + var afterOnAuthenticated = function() { + redetectBitrate(instance), resolve(result) + }; + instance.onAuthenticated ? instance.onAuthenticated(instance, result).then(afterOnAuthenticated) : afterOnAuthenticated() + }, reject) + }) + }, ApiClient.prototype.ensureWebSocket = function() { if (!this.isWebSocketOpenOrConnecting() && this.isWebSocketSupported()) try { this.openWebSocket() } catch (err) { diff --git a/src/login.html b/src/login.html index 8e48901c11..343b85906e 100644 --- a/src/login.html +++ b/src/login.html @@ -42,6 +42,10 @@ + + + + Incoming login requests: +
+
    +
  • Failed to load incoming requests
  • +
+
+ diff --git a/src/quickconnect.html b/src/quickconnect.html new file mode 100644 index 0000000000..770796a10e --- /dev/null +++ b/src/quickconnect.html @@ -0,0 +1,29 @@ +
+
+
+
+
+

Quick Connect

+
+
+ + Quick connect is: Failed to load status + +
+ + +
If unchecked, users will have to click the Activate button in their profile before initiating a quick connect login.
+
+ + +
+
+
\ No newline at end of file diff --git a/src/scripts/librarymenu.js b/src/scripts/librarymenu.js index 11c89b01da..930df8be49 100644 --- a/src/scripts/librarymenu.js +++ b/src/scripts/librarymenu.js @@ -349,6 +349,12 @@ define(["dom", "layoutManager", "inputManager", "connectionManager", "events", " href: "devices.html", pageIds: ["devicesPage", "devicePage"], icon: "devices" + }); + links.push({ + name: "Quick Connect", + href: "quickconnect.html", + pageIds: ["quickConnectPage", "quickConnectPage"], + icon: "devices" }); links.push({ name: globalize.translate("HeaderActivity"), diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 9c3db58a7f..1520e96632 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -72,6 +72,12 @@ define([ transition: "fade", controller: "user/subtitles" }); + defineRoute({ + path: "/mypreferencesquickconnect.html", + autoFocus: false, + transition: "fade", + controller: "user/quickconnect" + }); defineRoute({ path: "/dashboard.html", @@ -103,6 +109,12 @@ define([ roles: "admin", controller: "device" }); + defineRoute({ + path: "/quickconnect.html", + autoFocus: false, + roles: "admin", + controller: "quickconnect" + }); defineRoute({ path: "/dlnaprofile.html", autoFocus: false, diff --git a/src/scripts/site.js b/src/scripts/site.js index 3b992eaf2b..311478c019 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -829,6 +829,7 @@ var AppInfo = {}; define("displaySettings", [componentsPath + "/displaysettings/displaysettings"], returnFirstDependency); define("playbackSettings", [componentsPath + "/playbacksettings/playbacksettings"], returnFirstDependency); define("homescreenSettings", [componentsPath + "/homescreensettings/homescreensettings"], returnFirstDependency); + define("quickConnectSettings", [componentsPath + "/quickconnectsettings/quickconnectsettings"], returnFirstDependency); define("playbackManager", [componentsPath + "/playback/playbackmanager"], getPlaybackManager); define("layoutManager", [componentsPath + "/layoutManager", "apphost"], getLayoutManager); define("homeSections", [componentsPath + "/homesections/homesections"], returnFirstDependency); From fdfdcd60fe3d7a66cfc334f0692978fc3bb8e27c Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Mon, 13 Apr 2020 16:23:16 -0500 Subject: [PATCH 0005/2132] Switch off of jQuery --- CONTRIBUTORS.md | 1 + .../quickconnectsettings/quickconnectsettings.js | 8 +++++--- src/controllers/auth/login.js | 12 ++++++------ src/controllers/quickconnect.js | 16 +++++++++------- src/controllers/user/menu.js | 2 +- src/controllers/user/quickconnect.js | 9 +++++++++ src/mypreferencesquickconnect.html | 6 +++--- src/quickconnect.html | 2 +- 8 files changed, 35 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index aa3ec707e3..ffbf054829 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -34,6 +34,7 @@ - [Ryan Hartzell](https://github.com/ryan-hartzell) - [Thibault Nocchi](https://github.com/ThibaultNocchi) - [MrTimscampi](https://github.com/MrTimscampi) + - [ConfusedPolarBear](https://github.com/ConfusedPolarBear) # Emby Contributors diff --git a/src/components/quickconnectsettings/quickconnectsettings.js b/src/components/quickconnectsettings/quickconnectsettings.js index a30db441e6..ed7985dcfa 100644 --- a/src/components/quickconnectsettings/quickconnectsettings.js +++ b/src/components/quickconnectsettings/quickconnectsettings.js @@ -15,12 +15,14 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa } function list(apiClient) { - var elem = $("#quickConnectIncoming"); - elem.html(""); + console.debug("getting json"); apiClient.getJSON("/QuickConnect/List").then(json => { - console.debug("raw json", json); + var elem = $("#quickConnectIncoming"); + elem.html(""); + console.debug("raw json", json, "length is", json.length); for(var i = 0; i < json.length; i++) { var current = json[i]; + console.debug("current is", current); var html = "
  • " + current.Code + " - " + current.FriendlyName + " - "; if(!current.Authenticated) { diff --git a/src/controllers/auth/login.js b/src/controllers/auth/login.js index f5dc9b487b..845ae9a38f 100644 --- a/src/controllers/auth/login.js +++ b/src/controllers/auth/login.js @@ -153,10 +153,10 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout function loginQuickConnect() { var apiClient = getApiClient(); var friendlyName = "test"; - - var url = apiClient.getUrl("/QuickConnect/Initiate?FriendlyName=" + friendlyName); - apiClient.getJSON(url) - .then(json => { + + var url = apiClient.getUrl("/QuickConnect/Initiate?FriendlyName=" + friendlyName); + apiClient.getJSON(url) + .then(json => { if (!json.Secret || !json.Code) { Dashboard.alert({ message: json.Error, @@ -173,9 +173,9 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout loading.show(); var interval = setInterval(() => { - var url = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret); + var url = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret); apiClient.getJSON(url) - .then(data => { + .then(data => { if(data.Authenticated) { apiClient.quickConnect(data.Authentication).then((result) => { var user = result.User; diff --git a/src/controllers/quickconnect.js b/src/controllers/quickconnect.js index e3d1f76054..5c3c8f9a18 100644 --- a/src/controllers/quickconnect.js +++ b/src/controllers/quickconnect.js @@ -2,13 +2,15 @@ define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, "use strict"; function loadPage(page, status) { + console.debug("status is \"" + status + "\""); + var active = (status == "Active"); var available = (status == "Available") || active; - - $("#quickConnectStatus").text(status.toLocaleLowerCase()); - $("#chkQuickConnectAvailable").checked(available); - $("#chkQuickConnectActive").checked(active); - + + page.querySelector("#quickConnectStatus").textContent = status.toLocaleLowerCase(); + page.querySelector("#chkQuickConnectAvailable").checked = available; + page.querySelector("#chkQuickConnectActive").checked = active; + loading.hide(); } @@ -44,8 +46,8 @@ define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, } $(document).on("pageinit", "#quickConnectPage", function () { - $("#quickConnectPage").off("submit", onSubmit).on("submit", onSubmit); - $("#btnQuickConnectSubmit").click(onSubmit); + document.querySelector("#quickConnectPage").onsubmit = onSubmit; + document.querySelector("#btnQuickConnectSubmit").onclick = onSubmit; }).on("pageshow", "#quickConnectPage", function () { loading.show(); var page = this; diff --git a/src/controllers/user/menu.js b/src/controllers/user/menu.js index ff8db72681..f5d05c5be3 100644 --- a/src/controllers/user/menu.js +++ b/src/controllers/user/menu.js @@ -24,7 +24,7 @@ define(["apphost", "connectionManager", "layoutManager", "listViewStyle", "emby- page.querySelector(".lnkHomePreferences").setAttribute("href", "mypreferenceshome.html?userId=" + userId); page.querySelector(".lnkPlaybackPreferences").setAttribute("href", "mypreferencesplayback.html?userId=" + userId); page.querySelector(".lnkSubtitlePreferences").setAttribute("href", "mypreferencessubtitles.html?userId=" + userId); - page.querySelector(".lnkQuickConnectPreferences").setAttribute("href", "mypreferencesquickconnect.html?userId=" + userId); + page.querySelector(".lnkQuickConnectPreferences").setAttribute("href", "mypreferencesquickconnect.html?userId=" + userId); if (window.NativeShell && window.NativeShell.AppHost.supports("clientsettings")) { page.querySelector(".clientSettings").classList.remove("hide"); diff --git a/src/controllers/user/quickconnect.js b/src/controllers/user/quickconnect.js index d6f2ae25c1..6e63bcdc08 100644 --- a/src/controllers/user/quickconnect.js +++ b/src/controllers/user/quickconnect.js @@ -2,6 +2,13 @@ define(["quickConnectSettings", "dom", "globalize", "loading", "userSettings", " "use strict"; return function (view, params) { + function notImplemented() { + Dashboard.alert({ + message: "This button is not implemented yet, you must check the checkbox labeled \"Always accept quick connect login requests\" in the dashboard", + title: "Not implemented" + }); + } + var quickConnectSettingsInstance = null; var hasChanges; var userId = params.userId || ApiClient.getCurrentUserId(); @@ -9,6 +16,8 @@ define(["quickConnectSettings", "dom", "globalize", "loading", "userSettings", " view.addEventListener("viewshow", function () { console.debug("defining instance"); + $("#btnQuickConnectActivate").click(notImplemented); + quickConnectSettingsInstance = new QuickConnectSettings({ serverId: ApiClient.serverId(), userId: userId, diff --git a/src/mypreferencesquickconnect.html b/src/mypreferencesquickconnect.html index 326ce51b48..b9ffdfaccd 100644 --- a/src/mypreferencesquickconnect.html +++ b/src/mypreferencesquickconnect.html @@ -1,12 +1,12 @@
    - Incoming login requests:
      -
    • Failed to load incoming requests
    • -
    +
  • Failed to load incoming requests
  • + diff --git a/src/quickconnect.html b/src/quickconnect.html index 770796a10e..875716285c 100644 --- a/src/quickconnect.html +++ b/src/quickconnect.html @@ -16,7 +16,7 @@
    If unchecked, users will have to click the Activate button in their profile before initiating a quick connect login.
    From c37e8f2f1bcb827893fe32a3fcf77107964a853e Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Sat, 18 Apr 2020 19:20:15 -0500 Subject: [PATCH 0006/2132] Implement auto refresh and activate button --- .../quickconnectsettings.js | 85 +++++++++++++------ src/controllers/auth/login.js | 75 ++++++++-------- src/controllers/quickconnect.js | 54 +++++++----- src/controllers/user/quickconnect.js | 63 +++++++------- src/mypreferencesmenu.html | 4 +- src/mypreferencesquickconnect.html | 2 +- src/quickconnect.html | 6 +- src/scripts/librarymenu.js | 2 +- src/scripts/site.js | 2 +- 9 files changed, 170 insertions(+), 123 deletions(-) diff --git a/src/components/quickconnectsettings/quickconnectsettings.js b/src/components/quickconnectsettings/quickconnectsettings.js index ed7985dcfa..fc167802d8 100644 --- a/src/components/quickconnectsettings/quickconnectsettings.js +++ b/src/components/quickconnectsettings/quickconnectsettings.js @@ -1,11 +1,10 @@ -define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loading', 'connectionManager', 'homeSections', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-checkbox'], function (require, appHost, layoutManager, focusManager, globalize, loading, connectionManager, homeSections, dom, events) { +define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loading', 'connectionManager', 'listViewStyle', 'emby-select', 'emby-checkbox'], function (require, appHost, layoutManager, focusManager, globalize, loading, connectionManager) { "use strict"; function authorizeRequest(event) { var lookup = event.data.lookup; - var apiClient = event.data.apiClient; var url = ApiClient.getUrl("/QuickConnect/Authorize"); - apiClient.ajax({ + ApiClient.ajax({ type: "POST", url: url, data: { @@ -13,46 +12,72 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa } }, true); } - - function list(apiClient) { - console.debug("getting json"); - apiClient.getJSON("/QuickConnect/List").then(json => { - var elem = $("#quickConnectIncoming"); + + QuickConnectSettings.prototype.list = function(argPage) { + ApiClient.getJSON("/QuickConnect/List").then(json => { + var elem = $(argPage.querySelector("#quickConnectIncoming")); elem.html(""); - console.debug("raw json", json, "length is", json.length); - for(var i = 0; i < json.length; i++) { + for (var i = 0; i < json.length; i++) { var current = json[i]; - console.debug("current is", current); var html = "
  • " + current.Code + " - " + current.FriendlyName + " - "; - - if(!current.Authenticated) { + + if (!current.Authenticated) { html += "authorize"; - } - else { + } else { html += " (already authorized)"; } - + html += "
  • "; elem.append(html); - $("#qc" + current.Lookup).click({ lookup: current.Lookup, apiClient: apiClient}, authorizeRequest); + $("#qc" + current.Lookup).click({ lookup: current.Lookup }, authorizeRequest); } + + return true; + }).catch((e) => { + console.error("Unable to get quick connect login requests. error:", e); }); - } + }; + + QuickConnectSettings.prototype.activate = function() { + var url = ApiClient.getUrl("/QuickConnect/Activate"); + ApiClient.ajax({ + type: "POST", + url: url, + contentType: "application/json", + dataType: "json" + }).then((json) => { + let message = json.Error; + + console.log("message is \"" + message + "\""); + if (message && message !== "") { + console.error("Error activating quick connect. Error: ", json.Error); + + Dashboard.alert({ + title: "Unable to activate quick connect", + message: message + }); + + return false; + } + + Dashboard.alert({ + message: "Already active" + }); + + return true; + }).catch((e) => { + console.error("Error activating quick connect. Error:", e); + throw e; + }); + }; function QuickConnectSettings(options) { this.options = options; } QuickConnectSettings.prototype.loadData = function () { - loading.show(); - - var apiClient = connectionManager.getApiClient(this.options.serverId); - - list(apiClient); - - console.debug("request list finished"); - - loading.hide(); + this.options.interval = setInterval(this.list, 5000, this.options.page); + this.list(this.options.page); }; QuickConnectSettings.prototype.submit = function () { @@ -60,8 +85,14 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa }; QuickConnectSettings.prototype.destroy = function () { + console.debug("clearing refresh interval", this.options.interval); + clearInterval(this.options.interval); this.options = null; }; + QuickConnectSettings.prototype.interval = function (interval) { + this.options.interval = interval; + }; + return QuickConnectSettings; }); diff --git a/src/controllers/auth/login.js b/src/controllers/auth/login.js index 845ae9a38f..94fa15d412 100644 --- a/src/controllers/auth/login.js +++ b/src/controllers/auth/login.js @@ -152,51 +152,54 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout function loginQuickConnect() { var apiClient = getApiClient(); - var friendlyName = "test"; - + var friendlyName = navigator.userAgent; // TODO: what should this be changed to? + var url = apiClient.getUrl("/QuickConnect/Initiate?FriendlyName=" + friendlyName); apiClient.getJSON(url) - .then(json => { - if (!json.Secret || !json.Code) { + .then(json => { + if (!json.Secret || !json.Code) { + Dashboard.alert({ + message: json.Error, + title: "Error" + }); + + return false; + } + Dashboard.alert({ - message: json.Error, - title: "Error" + message: "Authorize request " + json.Code + " to continue", + title: "Quick Connect Code" }); - return; - } - Dashboard.alert({ - message: "Authorize request " + json.Code + " to continue", - title: "Quick Connect Code" - }); + loading.show(); - loading.show(); + var interval = setInterval(async function() { + let connectUrl = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret); + let data = await apiClient.getJSON(connectUrl); + if (data.Authenticated) { + let result = await apiClient.quickConnect(data.Authentication); + var user = result.User; + var serverId = getParameterByName("serverid"); + var newUrl = "home.html"; - var interval = setInterval(() => { - var url = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret); - apiClient.getJSON(url) - .then(data => { - if(data.Authenticated) { - apiClient.quickConnect(data.Authentication).then((result) => { - var user = result.User; - var serverId = getParameterByName("serverid"); - var newUrl; + if (user.Policy.IsAdministrator && !serverId) { + newUrl = "dashboard.html"; + } - if (user.Policy.IsAdministrator && !serverId) { - newUrl = "dashboard.html"; - } else { - newUrl = "home.html"; - } + loading.hide(); + Dashboard.onServerChanged(user.Id, result.AccessToken, apiClient); + Dashboard.navigate(newUrl); + clearInterval(interval); - loading.hide(); - Dashboard.onServerChanged(user.Id, result.AccessToken, apiClient); - Dashboard.navigate(newUrl); - clearInterval(interval); - }); + return true; } - }); - }, 5000); - }); + return false; + }, 5000); + + return true; + }).catch((e) => { + console.error("Unable to initiate quick connect login request. Error:", e); + }); } view.querySelector("#divUsers").addEventListener("click", function (e) { @@ -240,7 +243,7 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout view.querySelector(".btnSelectServer").addEventListener("click", function () { Dashboard.selectServer(); }); - view.addEventListener("viewshow", function (e) { + view.addEventListener("viewshow", function () { loading.show(); if (!appHost.supports('multiserver')) { diff --git a/src/controllers/quickconnect.js b/src/controllers/quickconnect.js index 5c3c8f9a18..a6ebc381ce 100644 --- a/src/controllers/quickconnect.js +++ b/src/controllers/quickconnect.js @@ -1,9 +1,8 @@ -define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, libraryMenu) { +define(["jQuery", "loading", "fnchecked"], function ($, loading) { "use strict"; + var page; function loadPage(page, status) { - console.debug("status is \"" + status + "\""); - var active = (status == "Active"); var available = (status == "Available") || active; @@ -16,44 +15,53 @@ define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, function onSubmit() { loading.show(); - - var available = $("#chkQuickConnectAvailable").is(":checked") ? "Available" : "Unavailable"; + + var newStatus = page.querySelector("#chkQuickConnectAvailable").checked ? "Available" : "Unavailable"; + if (newStatus && page.querySelector("#chkQuickConnectActive").checked) { + newStatus = "Active"; + } + var url = ApiClient.getUrl("/QuickConnect/Available"); - + ApiClient.ajax({ type: "POST", data: { - "Status": available + "Status": newStatus }, url: url }, true).then(() => { - if($("#chkQuickConnectActive").is(":checked")) { - url = ApiClient.getUrl("/QuickConnect/Activate"); - ApiClient.ajax({ - type: "POST", - url: url - }, true); - } - Dashboard.alert({ message: "Settings saved", title: "Saved" }); + + setTimeout(updatePage, 500); + + return true; + }).catch((e) => { + console.error("Unable to set quick connect status. error:", e); }); - + loading.hide(); return false; } - $(document).on("pageinit", "#quickConnectPage", function () { - document.querySelector("#quickConnectPage").onsubmit = onSubmit; - document.querySelector("#btnQuickConnectSubmit").onclick = onSubmit; - }).on("pageshow", "#quickConnectPage", function () { - loading.show(); - var page = this; + function updatePage() { var promise1 = ApiClient.getQuickConnect("Status"); - Promise.all([promise1]).then(function (responses) { + Promise.all([promise1]).then((responses) => { loadPage(page, responses[0]); + return true; + }).catch((e) => { + console.error("Unable to get quick connect status. error:", e); }); + } + + $(document).on("pageshow", "#quickConnectPage", function () { + loading.show(); + page = this; + + page.querySelector("#btnQuickConnectSubmit").onclick = onSubmit; + + updatePage(); }); }); diff --git a/src/controllers/user/quickconnect.js b/src/controllers/user/quickconnect.js index 6e63bcdc08..fae5d49865 100644 --- a/src/controllers/user/quickconnect.js +++ b/src/controllers/user/quickconnect.js @@ -1,50 +1,55 @@ define(["quickConnectSettings", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (QuickConnectSettings, dom, globalize, loading, userSettings, autoFocuser) { "use strict"; - return function (view, params) { - function notImplemented() { - Dashboard.alert({ - message: "This button is not implemented yet, you must check the checkbox labeled \"Always accept quick connect login requests\" in the dashboard", - title: "Not implemented" - }); - } - + return function (view) { var quickConnectSettingsInstance = null; - var hasChanges; - var userId = params.userId || ApiClient.getCurrentUserId(); - var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings(); + view.addEventListener("viewshow", function () { - console.debug("defining instance"); - - $("#btnQuickConnectActivate").click(notImplemented); - quickConnectSettingsInstance = new QuickConnectSettings({ - serverId: ApiClient.serverId(), - userId: userId, - element: view.querySelector(".quickConnectSettingsContainer"), - userSettings: currentSettings, - enableSaveButton: false, - enableSaveConfirmation: false, - autoFocus: autoFocuser.isEnabled() + page: view, + interval: 0 }); - + + view.querySelector("#btnQuickConnectActivate").addEventListener("click", () => { + quickConnectSettingsInstance.activate(quickConnectSettingsInstance); + }); + quickConnectSettingsInstance.loadData(); - }); - view.addEventListener("change", function () { - hasChanges = true; + + ApiClient.getQuickConnect("Status").then((status) => { + let btn = view.querySelector("#btnQuickConnectActivate"); + + if (status === "Unavailable") { + btn.textContent = "Quick connect is not available on this server"; + btn.disabled = true; + return false; + } + + else if (status === "Available") { + return false; + } + + btn.style.display = "none"; + return true; + }).catch((e) => { + throw e; + }); }); view.addEventListener("viewbeforehide", function () { - hasChanges = false; - if (quickConnectSettingsInstance) { quickConnectSettingsInstance.submit(); } + onDestroy(); }); view.addEventListener("viewdestroy", function () { + onDestroy(); + }); + + function onDestroy() { if (quickConnectSettingsInstance) { quickConnectSettingsInstance.destroy(); quickConnectSettingsInstance = null; } - }); + } }; }); diff --git a/src/mypreferencesmenu.html b/src/mypreferencesmenu.html index c8db1d17f3..bfc5e793ef 100644 --- a/src/mypreferencesmenu.html +++ b/src/mypreferencesmenu.html @@ -51,13 +51,13 @@
    - +
    Quick Connect
    - +
    diff --git a/src/mypreferencesquickconnect.html b/src/mypreferencesquickconnect.html index b9ffdfaccd..d92e49a427 100644 --- a/src/mypreferencesquickconnect.html +++ b/src/mypreferencesquickconnect.html @@ -2,7 +2,7 @@ - + Incoming login requests:
      diff --git a/src/quickconnect.html b/src/quickconnect.html index 875716285c..9a7dfdfb3e 100644 --- a/src/quickconnect.html +++ b/src/quickconnect.html @@ -6,9 +6,9 @@

      Quick Connect

    - + Quick connect is: Failed to load status - +
    If unchecked, users will have to click the Activate button in their profile before initiating a quick connect login.
    - + diff --git a/src/scripts/librarymenu.js b/src/scripts/librarymenu.js index 930df8be49..2accf4948d 100644 --- a/src/scripts/librarymenu.js +++ b/src/scripts/librarymenu.js @@ -350,7 +350,7 @@ define(["dom", "layoutManager", "inputManager", "connectionManager", "events", " pageIds: ["devicesPage", "devicePage"], icon: "devices" }); - links.push({ + links.push({ name: "Quick Connect", href: "quickconnect.html", pageIds: ["quickConnectPage", "quickConnectPage"], diff --git a/src/scripts/site.js b/src/scripts/site.js index 311478c019..3b8abf2bfe 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -829,7 +829,7 @@ var AppInfo = {}; define("displaySettings", [componentsPath + "/displaysettings/displaysettings"], returnFirstDependency); define("playbackSettings", [componentsPath + "/playbacksettings/playbacksettings"], returnFirstDependency); define("homescreenSettings", [componentsPath + "/homescreensettings/homescreensettings"], returnFirstDependency); - define("quickConnectSettings", [componentsPath + "/quickconnectsettings/quickconnectsettings"], returnFirstDependency); + define("quickConnectSettings", [componentsPath + "/quickconnectsettings/quickconnectsettings"], returnFirstDependency); define("playbackManager", [componentsPath + "/playback/playbackmanager"], getPlaybackManager); define("layoutManager", [componentsPath + "/layoutManager", "apphost"], getLayoutManager); define("homeSections", [componentsPath + "/homesections/homesections"], returnFirstDependency); From 43d01146a23b9ee9c4044a5d0b545ee6fe6ce692 Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Sun, 19 Apr 2020 01:39:05 -0500 Subject: [PATCH 0007/2132] Fix linting --- .../quickconnectsettings/quickconnectsettings.js | 2 +- src/controllers/quickconnect.js | 4 ++-- src/controllers/user/quickconnect.js | 4 +--- src/libraries/apiclient/apiclientcore.js | 7 ++++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/components/quickconnectsettings/quickconnectsettings.js b/src/components/quickconnectsettings/quickconnectsettings.js index fc167802d8..c65782cafe 100644 --- a/src/components/quickconnectsettings/quickconnectsettings.js +++ b/src/components/quickconnectsettings/quickconnectsettings.js @@ -61,7 +61,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa } Dashboard.alert({ - message: "Already active" + message: "Successfully activated" }); return true; diff --git a/src/controllers/quickconnect.js b/src/controllers/quickconnect.js index a6ebc381ce..35e5516f45 100644 --- a/src/controllers/quickconnect.js +++ b/src/controllers/quickconnect.js @@ -2,7 +2,7 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) { "use strict"; var page; - function loadPage(page, status) { + function loadPage(status) { var active = (status == "Active"); var available = (status == "Available") || active; @@ -49,7 +49,7 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) { function updatePage() { var promise1 = ApiClient.getQuickConnect("Status"); Promise.all([promise1]).then((responses) => { - loadPage(page, responses[0]); + loadPage(responses[0]); return true; }).catch((e) => { console.error("Unable to get quick connect status. error:", e); diff --git a/src/controllers/user/quickconnect.js b/src/controllers/user/quickconnect.js index fae5d49865..2b03dec415 100644 --- a/src/controllers/user/quickconnect.js +++ b/src/controllers/user/quickconnect.js @@ -23,9 +23,7 @@ define(["quickConnectSettings", "dom", "globalize", "loading", "userSettings", " btn.textContent = "Quick connect is not available on this server"; btn.disabled = true; return false; - } - - else if (status === "Available") { + } else if (status === "Available") { return false; } diff --git a/src/libraries/apiclient/apiclientcore.js b/src/libraries/apiclient/apiclientcore.js index 04421b8372..e1892505fc 100644 --- a/src/libraries/apiclient/apiclientcore.js +++ b/src/libraries/apiclient/apiclientcore.js @@ -358,8 +358,8 @@ define(["events", "appStorage"], function(events, appStorage) { }) }, ApiClient.prototype.quickConnect = function (token) { if (!token) return Promise.reject(); - var url = this.getUrl("Users/AuthenticateWithQuickConnect"), - instance = this; + var url = this.getUrl("Users/AuthenticateWithQuickConnect"); + var instance = this; return new Promise(function(resolve, reject) { var postData = { Token: token @@ -372,7 +372,8 @@ define(["events", "appStorage"], function(events, appStorage) { contentType: "application/json" }).then(function(result) { var afterOnAuthenticated = function() { - redetectBitrate(instance), resolve(result) + redetectBitrate(instance); + return resolve(result); }; instance.onAuthenticated ? instance.onAuthenticated(instance, result).then(afterOnAuthenticated) : afterOnAuthenticated() }, reject) From bf03a7ba57af89f1a38c41457dbe7c70f501a04d Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Sat, 25 Apr 2020 15:46:22 -0500 Subject: [PATCH 0008/2132] Improve UI --- .../quickconnectsettings.js | 42 ++++++++++++++----- src/controllers/auth/login.js | 42 ++++++++++++------- src/controllers/quickconnect.js | 12 ++---- src/mypreferencesmenu.html | 2 +- src/mypreferencesquickconnect.html | 5 +-- src/quickconnect.html | 5 --- src/scripts/librarymenu.js | 2 +- 7 files changed, 65 insertions(+), 45 deletions(-) diff --git a/src/components/quickconnectsettings/quickconnectsettings.js b/src/components/quickconnectsettings/quickconnectsettings.js index c65782cafe..59250df76a 100644 --- a/src/components/quickconnectsettings/quickconnectsettings.js +++ b/src/components/quickconnectsettings/quickconnectsettings.js @@ -11,25 +11,45 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa "Lookup": lookup } }, true); + + require(["toast"], function (toast) { + toast("Request authorized"); + }); + + // prevent bubbling + return false; } QuickConnectSettings.prototype.list = function(argPage) { ApiClient.getJSON("/QuickConnect/List").then(json => { + let found = false; var elem = $(argPage.querySelector("#quickConnectIncoming")); - elem.html(""); - for (var i = 0; i < json.length; i++) { - var current = json[i]; - var html = "
  • " + current.Code + " - " + current.FriendlyName + " - "; + elem.text("No pending login requests"); - if (!current.Authenticated) { - html += "authorize"; - } else { - html += " (already authorized)"; + for (var i = 0; i < json.length; i++) { + if (!found) { + elem.html(""); + found = true; } - html += "
  • "; + var current = json[i]; + + let html = '
    '; + html += '
    ' + current.Code + '
    '; + html += '
    ' + current.FriendlyName + '
    '; + html += '
    '; + + if (!current.Authenticated) { + html += 'authorize'; + } else { + html += " (authorized)"; + } + + html += '
    '; elem.append(html); + $("#qc" + current.Lookup).click({ lookup: current.Lookup }, authorizeRequest); + $("#div" + current.Lookup).click({ lookup: current.Lookup }, authorizeRequest); } return true; @@ -60,8 +80,8 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa return false; } - Dashboard.alert({ - message: "Successfully activated" + require(["toast"], function (toast) { + toast("Successfully activated"); }); return true; diff --git a/src/controllers/auth/login.js b/src/controllers/auth/login.js index 94fa15d412..ff36939fb2 100644 --- a/src/controllers/auth/login.js +++ b/src/controllers/auth/login.js @@ -152,7 +152,7 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout function loginQuickConnect() { var apiClient = getApiClient(); - var friendlyName = navigator.userAgent; // TODO: what should this be changed to? + var friendlyName = navigator.userAgent; var url = apiClient.getUrl("/QuickConnect/Initiate?FriendlyName=" + friendlyName); apiClient.getJSON(url) @@ -174,25 +174,37 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout loading.show(); var interval = setInterval(async function() { - let connectUrl = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret); - let data = await apiClient.getJSON(connectUrl); - if (data.Authenticated) { - let result = await apiClient.quickConnect(data.Authentication); - var user = result.User; - var serverId = getParameterByName("serverid"); - var newUrl = "home.html"; + try { + let connectUrl = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret); + let data = await apiClient.getJSON(connectUrl); + if (data.Authenticated) { + let result = await apiClient.quickConnect(data.Authentication); + var user = result.User; + var serverId = getParameterByName("serverid"); + var newUrl = "home.html"; - if (user.Policy.IsAdministrator && !serverId) { - newUrl = "dashboard.html"; + if (user.Policy.IsAdministrator && !serverId) { + newUrl = "dashboard.html"; + } + + loading.hide(); + Dashboard.onServerChanged(user.Id, result.AccessToken, apiClient); + Dashboard.navigate(newUrl); + clearInterval(interval); + + return true; } + } catch (e) { + Dashboard.alert({ + message: "Quick connect was deactivated before the login request could be approved", + title: "Unexpected error" + }); - loading.hide(); - Dashboard.onServerChanged(user.Id, result.AccessToken, apiClient); - Dashboard.navigate(newUrl); + console.error("Unable to login with quick connect", e); clearInterval(interval); - - return true; + loading.hide(); } + return false; }, 5000); diff --git a/src/controllers/quickconnect.js b/src/controllers/quickconnect.js index 35e5516f45..3432e79231 100644 --- a/src/controllers/quickconnect.js +++ b/src/controllers/quickconnect.js @@ -3,12 +3,10 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) { var page; function loadPage(status) { - var active = (status == "Active"); - var available = (status == "Available") || active; + var available = status === "Available" || status === "Active"; page.querySelector("#quickConnectStatus").textContent = status.toLocaleLowerCase(); page.querySelector("#chkQuickConnectAvailable").checked = available; - page.querySelector("#chkQuickConnectActive").checked = active; loading.hide(); } @@ -17,9 +15,6 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) { loading.show(); var newStatus = page.querySelector("#chkQuickConnectAvailable").checked ? "Available" : "Unavailable"; - if (newStatus && page.querySelector("#chkQuickConnectActive").checked) { - newStatus = "Active"; - } var url = ApiClient.getUrl("/QuickConnect/Available"); @@ -30,9 +25,8 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) { }, url: url }, true).then(() => { - Dashboard.alert({ - message: "Settings saved", - title: "Saved" + require(["toast"], function (toast) { + toast("Settings saved"); }); setTimeout(updatePage, 500); diff --git a/src/mypreferencesmenu.html b/src/mypreferencesmenu.html index bfc5e793ef..6f753e5554 100644 --- a/src/mypreferencesmenu.html +++ b/src/mypreferencesmenu.html @@ -51,7 +51,7 @@
    - + tap_and_play
    Quick Connect
    diff --git a/src/mypreferencesquickconnect.html b/src/mypreferencesquickconnect.html index d92e49a427..b58ca8074f 100644 --- a/src/mypreferencesquickconnect.html +++ b/src/mypreferencesquickconnect.html @@ -3,10 +3,9 @@ Activate - Incoming login requests:
    -
      -
    • Failed to load incoming requests
    • +
      +
      Failed to load incoming requests
    diff --git a/src/quickconnect.html b/src/quickconnect.html index 9a7dfdfb3e..7d7ec879a6 100644 --- a/src/quickconnect.html +++ b/src/quickconnect.html @@ -14,11 +14,6 @@ Enable quick connect on this server - -
    If unchecked, users will have to click the Activate button in their profile before initiating a quick connect login.
    -
    Failed to load incoming requests
    +
    ${MessagePleaseWait}
    diff --git a/src/quickconnect.html b/src/quickconnect.html index 7d7ec879a6..dff74eb836 100644 --- a/src/quickconnect.html +++ b/src/quickconnect.html @@ -3,16 +3,16 @@
    -

    Quick Connect

    +

    ${QuickConnect}

    - Quick connect is: Failed to load status + ${LabelCurrentStatus}
    diff --git a/src/scripts/librarymenu.js b/src/scripts/librarymenu.js index 131dcf7472..e3ef65f22f 100644 --- a/src/scripts/librarymenu.js +++ b/src/scripts/librarymenu.js @@ -351,7 +351,7 @@ define(["dom", "layoutManager", "inputManager", "connectionManager", "events", " icon: "devices" }); links.push({ - name: "Quick Connect", + name: globalize.translate("QuickConnect"), href: "quickconnect.html", pageIds: ["quickConnectPage", "quickConnectPage"], icon: "tap_and_play" diff --git a/src/strings/en-us.json b/src/strings/en-us.json index d5fc40ef01..ad390a0fe2 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -48,6 +48,7 @@ "AuthProviderHelp": "Select an Authentication Provider to be used to authenticate this user's password.", "Auto": "Auto", "AutoBasedOnLanguageSetting": "Auto (based on language setting)", + "Authorize": "authorize", "Backdrop": "Backdrop", "Backdrops": "Backdrops", "Banner": "Banner", @@ -63,6 +64,7 @@ "Browse": "Browse", "BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins.", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when transcoding videos. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VOBSUB, PGS, SUB, IDX) and certain ASS or SSA subtitles.", + "ButtonActivate": "Activate", "ButtonAdd": "Add", "ButtonAddImage": "Add Image", "ButtonAddMediaLibrary": "Add Media Library", @@ -140,6 +142,7 @@ "ButtonTrailer": "Trailer", "ButtonUninstall": "Uninstall", "ButtonUp": "Up", + "ButtonUseQuickConnect": "Use Quick Connect", "ButtonViewWebsite": "View website", "ButtonWebsite": "Website", "CancelRecording": "Cancel recording", @@ -231,6 +234,7 @@ "EnableNextVideoInfoOverlayHelp": "At the end of a video, display info about the next video coming up in the current playlist.", "EnablePhotos": "Display photos", "EnablePhotosHelp": "Images will be detected and displayed alongside other media files.", + "EnableQuickConnect": "Enable quick connect on this server", "EnableStreamLooping": "Auto-loop live streams", "EnableStreamLoopingHelp": "Enable this if live streams only contain a few seconds of data and need to be continuously requested. Enabling this when not needed may cause problems.", "EnableThemeSongs": "Theme songs", @@ -587,6 +591,7 @@ "LabelCountry": "Country:", "LabelCriticRating": "Critic rating:", "LabelCurrentPassword": "Current password:", + "LabelCurrentStatus": "Current status:", "LabelCustomCertificatePath": "Custom SSL certificate path:", "LabelCustomCertificatePathHelp": "Path to a PKCS #12 file containing a certificate and private key to enable TLS support on a custom domain.", "LabelCustomCss": "Custom CSS:", @@ -1260,6 +1265,11 @@ "Programs": "Programs", "Quality": "Quality", "QueueAllFromHere": "Queue all from here", + "QuickConnect": "Quick Connect", + "QuickConnectActivationSuccessful": "Successfully activated", + "QuickConnectAuthorizeCode": "Authorize request {0} to continue", + "QuickConnectNoPending": "No pending login requests", + "QuickConnectNotAvailable": "Quick connect is not available on this server", "Raised": "Raised", "Rate": "Rate", "RecentlyWatched": "Recently watched", From a6d37e763319c1e65f1a7358a1b8ac0895b6492c Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Sun, 26 Apr 2020 17:47:31 -0500 Subject: [PATCH 0010/2132] Change missed var declarations into let --- .../quickconnectsettings/quickconnectsettings.js | 8 ++++---- src/controllers/auth/login.js | 14 +++++++------- src/controllers/quickconnect.js | 10 +++++----- src/controllers/user/quickconnect.js | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/quickconnectsettings/quickconnectsettings.js b/src/components/quickconnectsettings/quickconnectsettings.js index 6010fab5c3..45bfe815cc 100644 --- a/src/components/quickconnectsettings/quickconnectsettings.js +++ b/src/components/quickconnectsettings/quickconnectsettings.js @@ -23,16 +23,16 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa QuickConnectSettings.prototype.list = function(argPage) { ApiClient.getJSON("/QuickConnect/List").then(json => { let found = false; - var elem = argPage.querySelector('#quickConnectIncoming'); + let elem = argPage.querySelector('#quickConnectIncoming'); elem.innerText = globalize.translate('QuickConnectNoPending'); - for (var i = 0; i < json.length; i++) { + for (let i = 0; i < json.length; i++) { if (!found) { elem.innerHTML = ""; found = true; } - var current = json[i]; + let current = json[i]; let html = '
    '; html += '
    ' + current.Code + '
    '; @@ -57,7 +57,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa }; QuickConnectSettings.prototype.activate = function() { - var url = ApiClient.getUrl("/QuickConnect/Activate"); + let url = ApiClient.getUrl("/QuickConnect/Activate"); ApiClient.ajax({ type: "POST", url: url, diff --git a/src/controllers/auth/login.js b/src/controllers/auth/login.js index 86e8874e7e..8b0bcdd28d 100644 --- a/src/controllers/auth/login.js +++ b/src/controllers/auth/login.js @@ -151,10 +151,10 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout } function loginQuickConnect() { - var apiClient = getApiClient(); - var friendlyName = navigator.userAgent; + let apiClient = getApiClient(); + let friendlyName = navigator.userAgent; - var url = apiClient.getUrl("/QuickConnect/Initiate?FriendlyName=" + friendlyName); + let url = apiClient.getUrl("/QuickConnect/Initiate?FriendlyName=" + friendlyName); apiClient.getJSON(url) .then(json => { if (!json.Secret || !json.Code) { @@ -173,15 +173,15 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout loading.show(); - var interval = setInterval(async function() { + let interval = setInterval(async function() { try { let connectUrl = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret); let data = await apiClient.getJSON(connectUrl); if (data.Authenticated) { let result = await apiClient.quickConnect(data.Authentication); - var user = result.User; - var serverId = getParameterByName("serverid"); - var newUrl = "home.html"; + let user = result.User; + let serverId = getParameterByName("serverid"); + let newUrl = "home.html"; if (user.Policy.IsAdministrator && !serverId) { newUrl = "dashboard.html"; diff --git a/src/controllers/quickconnect.js b/src/controllers/quickconnect.js index 3432e79231..93b74a1b52 100644 --- a/src/controllers/quickconnect.js +++ b/src/controllers/quickconnect.js @@ -1,9 +1,9 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) { "use strict"; - var page; + let page; function loadPage(status) { - var available = status === "Available" || status === "Active"; + let available = status === "Available" || status === "Active"; page.querySelector("#quickConnectStatus").textContent = status.toLocaleLowerCase(); page.querySelector("#chkQuickConnectAvailable").checked = available; @@ -14,9 +14,9 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) { function onSubmit() { loading.show(); - var newStatus = page.querySelector("#chkQuickConnectAvailable").checked ? "Available" : "Unavailable"; + let newStatus = page.querySelector("#chkQuickConnectAvailable").checked ? "Available" : "Unavailable"; - var url = ApiClient.getUrl("/QuickConnect/Available"); + let url = ApiClient.getUrl("/QuickConnect/Available"); ApiClient.ajax({ type: "POST", @@ -41,7 +41,7 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) { } function updatePage() { - var promise1 = ApiClient.getQuickConnect("Status"); + let promise1 = ApiClient.getQuickConnect("Status"); Promise.all([promise1]).then((responses) => { loadPage(responses[0]); return true; diff --git a/src/controllers/user/quickconnect.js b/src/controllers/user/quickconnect.js index 035aef8f2f..bee0c1b817 100644 --- a/src/controllers/user/quickconnect.js +++ b/src/controllers/user/quickconnect.js @@ -2,7 +2,7 @@ define(["quickConnectSettings", "dom", "globalize", "loading", "userSettings", " "use strict"; return function (view) { - var quickConnectSettingsInstance = null; + let quickConnectSettingsInstance = null; view.addEventListener("viewshow", function () { quickConnectSettingsInstance = new QuickConnectSettings({ From 8655de4469c3aade0e1f949169c8f536a43e210b Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Fri, 1 May 2020 13:49:06 -0500 Subject: [PATCH 0011/2132] Rename to camel case --- .../quickConnectSettings.js} | 0 src/controllers/{quickconnect.js => quickConnect.js} | 0 src/controllers/user/{quickconnect.js => quickConnect.js} | 0 ...referencesquickconnect.html => myPreferencesQuickConnect.html} | 0 src/{quickconnect.html => quickConnect.html} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename src/components/{quickconnectsettings/quickconnectsettings.js => quickConnectSettings/quickConnectSettings.js} (100%) rename src/controllers/{quickconnect.js => quickConnect.js} (100%) rename src/controllers/user/{quickconnect.js => quickConnect.js} (100%) rename src/{mypreferencesquickconnect.html => myPreferencesQuickConnect.html} (100%) rename src/{quickconnect.html => quickConnect.html} (100%) diff --git a/src/components/quickconnectsettings/quickconnectsettings.js b/src/components/quickConnectSettings/quickConnectSettings.js similarity index 100% rename from src/components/quickconnectsettings/quickconnectsettings.js rename to src/components/quickConnectSettings/quickConnectSettings.js diff --git a/src/controllers/quickconnect.js b/src/controllers/quickConnect.js similarity index 100% rename from src/controllers/quickconnect.js rename to src/controllers/quickConnect.js diff --git a/src/controllers/user/quickconnect.js b/src/controllers/user/quickConnect.js similarity index 100% rename from src/controllers/user/quickconnect.js rename to src/controllers/user/quickConnect.js diff --git a/src/mypreferencesquickconnect.html b/src/myPreferencesQuickConnect.html similarity index 100% rename from src/mypreferencesquickconnect.html rename to src/myPreferencesQuickConnect.html diff --git a/src/quickconnect.html b/src/quickConnect.html similarity index 100% rename from src/quickconnect.html rename to src/quickConnect.html From d8cfe064e60379033fdf61a963efbc27296130af Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Fri, 1 May 2020 14:18:04 -0500 Subject: [PATCH 0012/2132] Changed code to camel case --- src/controllers/user/menu.js | 2 +- src/scripts/librarymenu.js | 2 +- src/scripts/routes.js | 8 ++++---- src/scripts/site.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/controllers/user/menu.js b/src/controllers/user/menu.js index f5d05c5be3..9482b26cb5 100644 --- a/src/controllers/user/menu.js +++ b/src/controllers/user/menu.js @@ -24,7 +24,7 @@ define(["apphost", "connectionManager", "layoutManager", "listViewStyle", "emby- page.querySelector(".lnkHomePreferences").setAttribute("href", "mypreferenceshome.html?userId=" + userId); page.querySelector(".lnkPlaybackPreferences").setAttribute("href", "mypreferencesplayback.html?userId=" + userId); page.querySelector(".lnkSubtitlePreferences").setAttribute("href", "mypreferencessubtitles.html?userId=" + userId); - page.querySelector(".lnkQuickConnectPreferences").setAttribute("href", "mypreferencesquickconnect.html?userId=" + userId); + page.querySelector(".lnkQuickConnectPreferences").setAttribute("href", "myPreferencesQuickConnect.html?userId=" + userId); if (window.NativeShell && window.NativeShell.AppHost.supports("clientsettings")) { page.querySelector(".clientSettings").classList.remove("hide"); diff --git a/src/scripts/librarymenu.js b/src/scripts/librarymenu.js index e3ef65f22f..d123395ca0 100644 --- a/src/scripts/librarymenu.js +++ b/src/scripts/librarymenu.js @@ -352,7 +352,7 @@ define(["dom", "layoutManager", "inputManager", "connectionManager", "events", " }); links.push({ name: globalize.translate("QuickConnect"), - href: "quickconnect.html", + href: "quickConnect.html", pageIds: ["quickConnectPage", "quickConnectPage"], icon: "tap_and_play" }); diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 1520e96632..f0806f56e0 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -73,10 +73,10 @@ define([ controller: "user/subtitles" }); defineRoute({ - path: "/mypreferencesquickconnect.html", + path: "/myPreferencesQuickConnect.html", autoFocus: false, transition: "fade", - controller: "user/quickconnect" + controller: "user/quickConnect" }); defineRoute({ @@ -110,10 +110,10 @@ define([ controller: "device" }); defineRoute({ - path: "/quickconnect.html", + path: "/quickConnect.html", autoFocus: false, roles: "admin", - controller: "quickconnect" + controller: "quickConnect" }); defineRoute({ path: "/dlnaprofile.html", diff --git a/src/scripts/site.js b/src/scripts/site.js index 3b8abf2bfe..ca8a828b0b 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -829,7 +829,7 @@ var AppInfo = {}; define("displaySettings", [componentsPath + "/displaysettings/displaysettings"], returnFirstDependency); define("playbackSettings", [componentsPath + "/playbacksettings/playbacksettings"], returnFirstDependency); define("homescreenSettings", [componentsPath + "/homescreensettings/homescreensettings"], returnFirstDependency); - define("quickConnectSettings", [componentsPath + "/quickconnectsettings/quickconnectsettings"], returnFirstDependency); + define("quickConnectSettings", [componentsPath + "/quickConnectSettings/quickConnectSettings"], returnFirstDependency); define("playbackManager", [componentsPath + "/playback/playbackmanager"], getPlaybackManager); define("layoutManager", [componentsPath + "/layoutManager", "apphost"], getLayoutManager); define("homeSections", [componentsPath + "/homesections/homesections"], returnFirstDependency); From 257ce4974ebee7c0977ae95cba4269805f6f8b77 Mon Sep 17 00:00:00 2001 From: Christoph Potas Date: Sun, 3 May 2020 20:30:35 +0200 Subject: [PATCH 0013/2132] ~ migrate subtitlesettings.js to es6 ~ migrate subtitleappearancehelper.js to es6 ~ replace duplicated "populateLanguages" function ( playbacksettings.js:18 ) ~ replace duplicated "onSubmit" function ( playbacksettings.js:266 ) - remove empty function getWindowStyles Signed-off-by: Christoph Potas --- package.json | 2 + .../subtitleappearancehelper.js | 242 ++++++------- .../subtitlesettings/subtitlesettings.js | 330 +++++++++--------- 3 files changed, 276 insertions(+), 298 deletions(-) diff --git a/package.json b/package.json index 53f3d225a8..fda29b810c 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,8 @@ "test": [ "src/components/autoFocuser.js", "src/components/cardbuilder/cardBuilder.js", + "src/components/subtitlesettings/subtitlesettings.js", + "src/components/subtitlesettings/subtitleappearancehelper.js", "src/scripts/dom.js", "src/components/filedownloader.js", "src/scripts/filesystem.js", diff --git a/src/components/subtitlesettings/subtitleappearancehelper.js b/src/components/subtitlesettings/subtitleappearancehelper.js index fdc64f0dfa..7bf4737831 100644 --- a/src/components/subtitlesettings/subtitleappearancehelper.js +++ b/src/components/subtitlesettings/subtitleappearancehelper.js @@ -1,159 +1,149 @@ -define([], function () { - "use strict"; +function getTextStyles(settings, isCue) { - function getTextStyles(settings, isCue) { + let list = []; - var list = []; + if (isCue) { + switch (settings.textSize || '') { - if (isCue) { - switch (settings.textSize || '') { - - case 'smaller': - list.push({ name: 'font-size', value: '.5em' }); - break; - case 'small': - list.push({ name: 'font-size', value: '.7em' }); - break; - case 'large': - list.push({ name: 'font-size', value: '1.3em' }); - break; - case 'larger': - list.push({ name: 'font-size', value: '1.72em' }); - break; - case 'extralarge': - list.push({ name: 'font-size', value: '2em' }); - break; - default: - case 'medium': - break; - } - } else { - switch (settings.textSize || '') { - - case 'smaller': - list.push({ name: 'font-size', value: '.8em' }); - break; - case 'small': - list.push({ name: 'font-size', value: 'inherit' }); - break; - case 'larger': - list.push({ name: 'font-size', value: '2em' }); - break; - case 'extralarge': - list.push({ name: 'font-size', value: '2.2em' }); - break; - case 'large': - list.push({ name: 'font-size', value: '1.72em' }); - break; - default: - case 'medium': - list.push({ name: 'font-size', value: '1.36em' }); - break; - } - } - - switch (settings.dropShadow || '') { - - case 'raised': - list.push({ name: 'text-shadow', value: '-1px -1px white, 0px -1px white, -1px 0px white, 1px 1px black, 0px 1px black, 1px 0px black' }); + case 'smaller': + list.push({ name: 'font-size', value: '.5em' }); break; - case 'depressed': - list.push({ name: 'text-shadow', value: '1px 1px white, 0px 1px white, 1px 0px white, -1px -1px black, 0px -1px black, -1px 0px black' }); + case 'small': + list.push({ name: 'font-size', value: '.7em' }); break; - case 'uniform': - list.push({ name: 'text-shadow', value: '-1px 0px #000000, 0px 1px #000000, 1px 0px #000000, 0px -1px #000000' }); + case 'large': + list.push({ name: 'font-size', value: '1.3em' }); break; - case 'none': - list.push({ name: 'text-shadow', value: 'none' }); + case 'larger': + list.push({ name: 'font-size', value: '1.72em' }); + break; + case 'extralarge': + list.push({ name: 'font-size', value: '2em' }); break; default: - case 'dropshadow': - list.push({ name: 'text-shadow', value: '#000000 0px 0px 7px' }); + case 'medium': break; } + } else { + switch (settings.textSize || '') { - var background = settings.textBackground || 'transparent'; - if (background) { - list.push({ name: 'background-color', value: background }); - } - - var textColor = settings.textColor || '#ffffff'; - if (textColor) { - list.push({ name: 'color', value: textColor }); - } - - switch (settings.font || '') { - - case 'typewriter': - list.push({ name: 'font-family', value: '"Courier New",monospace' }); - list.push({ name: 'font-variant', value: 'none' }); + case 'smaller': + list.push({ name: 'font-size', value: '.8em' }); break; - case 'print': - list.push({ name: 'font-family', value: 'Georgia,Times New Roman,Arial,Helvetica,serif' }); - list.push({ name: 'font-variant', value: 'none' }); + case 'small': + list.push({ name: 'font-size', value: 'inherit' }); break; - case 'console': - list.push({ name: 'font-family', value: 'Consolas,Lucida Console,Menlo,Monaco,monospace' }); - list.push({ name: 'font-variant', value: 'none' }); + case 'larger': + list.push({ name: 'font-size', value: '2em' }); break; - case 'cursive': - list.push({ name: 'font-family', value: 'Lucida Handwriting,Brush Script MT,Segoe Script,cursive,Quintessential,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' }); - list.push({ name: 'font-variant', value: 'none' }); + case 'extralarge': + list.push({ name: 'font-size', value: '2.2em' }); break; - case 'casual': - list.push({ name: 'font-family', value: 'Gabriola,Segoe Print,Comic Sans MS,Chalkboard,Short Stack,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' }); - list.push({ name: 'font-variant', value: 'none' }); - break; - case 'smallcaps': - list.push({ name: 'font-family', value: 'Copperplate Gothic,Copperplate Gothic Bold,Copperplate,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' }); - list.push({ name: 'font-variant', value: 'small-caps' }); + case 'large': + list.push({ name: 'font-size', value: '1.72em' }); break; default: - list.push({ name: 'font-family', value: 'inherit' }); - list.push({ name: 'font-variant', value: 'none' }); + case 'medium': + list.push({ name: 'font-size', value: '1.36em' }); break; } - - return list; } - function getWindowStyles(settings) { + switch (settings.dropShadow || '') { - return []; + case 'raised': + list.push({ name: 'text-shadow', value: '-1px -1px white, 0px -1px white, -1px 0px white, 1px 1px black, 0px 1px black, 1px 0px black' }); + break; + case 'depressed': + list.push({ name: 'text-shadow', value: '1px 1px white, 0px 1px white, 1px 0px white, -1px -1px black, 0px -1px black, -1px 0px black' }); + break; + case 'uniform': + list.push({ name: 'text-shadow', value: '-1px 0px #000000, 0px 1px #000000, 1px 0px #000000, 0px -1px #000000' }); + break; + case 'none': + list.push({ name: 'text-shadow', value: 'none' }); + break; + default: + case 'dropshadow': + list.push({ name: 'text-shadow', value: '#000000 0px 0px 7px' }); + break; } - function getStyles(settings, isCue) { - - return { - text: getTextStyles(settings, isCue), - window: getWindowStyles(settings) - }; + const background = settings.textBackground || 'transparent'; + if (background) { + list.push({ name: 'background-color', value: background }); } - function applyStyleList(styles, elem) { - - for (var i = 0, length = styles.length; i < length; i++) { - - var style = styles[i]; - - elem.style[style.name] = style.value; - } + const textColor = settings.textColor || '#ffffff'; + if (textColor) { + list.push({ name: 'color', value: textColor }); } - function applyStyles(elements, appearanceSettings) { + switch (settings.font || '') { - var styles = getStyles(appearanceSettings); - - if (elements.text) { - applyStyleList(styles.text, elements.text); - } - if (elements.window) { - applyStyleList(styles.window, elements.window); - } + case 'typewriter': + list.push({ name: 'font-family', value: '"Courier New",monospace' }); + list.push({ name: 'font-variant', value: 'none' }); + break; + case 'print': + list.push({ name: 'font-family', value: 'Georgia,Times New Roman,Arial,Helvetica,serif' }); + list.push({ name: 'font-variant', value: 'none' }); + break; + case 'console': + list.push({ name: 'font-family', value: 'Consolas,Lucida Console,Menlo,Monaco,monospace' }); + list.push({ name: 'font-variant', value: 'none' }); + break; + case 'cursive': + list.push({ name: 'font-family', value: 'Lucida Handwriting,Brush Script MT,Segoe Script,cursive,Quintessential,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' }); + list.push({ name: 'font-variant', value: 'none' }); + break; + case 'casual': + list.push({ name: 'font-family', value: 'Gabriola,Segoe Print,Comic Sans MS,Chalkboard,Short Stack,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' }); + list.push({ name: 'font-variant', value: 'none' }); + break; + case 'smallcaps': + list.push({ name: 'font-family', value: 'Copperplate Gothic,Copperplate Gothic Bold,Copperplate,system-ui,-apple-system,BlinkMacSystemFont,sans-serif' }); + list.push({ name: 'font-variant', value: 'small-caps' }); + break; + default: + list.push({ name: 'font-family', value: 'inherit' }); + list.push({ name: 'font-variant', value: 'none' }); + break; } + return list; +} + +export function getStyles(settings, isCue) { + return { - getStyles: getStyles, - applyStyles: applyStyles + text: getTextStyles(settings, isCue), + window: [] }; -}); +} + +function applyStyleList(styles, elem) { + + for (let i = 0, length = styles.length; i < length; i++) { + + let style = styles[i]; + + elem.style[style.name] = style.value; + } +} + +export function applyStyles(elements, appearanceSettings) { + + let styles = getStyles(appearanceSettings); + + if (elements.text) { + applyStyleList(styles.text, elements.text); + } + if (elements.window) { + applyStyleList(styles.window, elements.window); + } +} +export default { + getStyles: getStyles, + applyStyles: applyStyles +}; diff --git a/src/components/subtitlesettings/subtitlesettings.js b/src/components/subtitlesettings/subtitlesettings.js index 2c86929192..8f5038fd65 100644 --- a/src/components/subtitlesettings/subtitlesettings.js +++ b/src/components/subtitlesettings/subtitlesettings.js @@ -1,213 +1,199 @@ -define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loading', 'connectionManager', 'subtitleAppearanceHelper', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-input', 'emby-checkbox', 'flexStyles'], function (require, globalize, appSettings, appHost, focusManager, loading, connectionManager, subtitleAppearanceHelper, dom, events) { - "use strict"; +import require from 'require'; +import globalize from 'globalize'; +import appHost from 'apphost'; +import appSettings from 'appSettings'; +import focusManager from 'focusManager'; +import loading from 'loading'; +import connectionManager from 'connectionmanager'; +import subtitleAppearanceHelper from 'subtitleappearancehelper'; +import playbacksettings from 'playbacksettings'; +import dom from 'dom'; +import events from 'events'; +import 'listViewStyle'; +import 'emby-select'; +import 'emby-input'; +import 'emby-checkbox'; +import 'flexStyles'; - function populateLanguages(select, languages) { - var html = ""; +function getSubtitleAppearanceObject(context) { + let appearanceSettings = {}; - html += ""; - for (var i = 0, length = languages.length; i < length; i++) { - var culture = languages[i]; - html += ""; + appearanceSettings.textSize = context.querySelector('#selectTextSize').value; + appearanceSettings.dropShadow = context.querySelector('#selectDropShadow').value; + appearanceSettings.font = context.querySelector('#selectFont').value; + appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value; + appearanceSettings.textColor = context.querySelector('#inputTextColor').value; + + return appearanceSettings; +} + +function loadForm(context, user, userSettings, appearanceSettings, apiClient) { + + apiClient.getCultures().then(function (allCultures) { + + if (appHost.supports('subtitleburnsettings') && user.Policy.EnableVideoPlaybackTranscoding) { + context.querySelector('.fldBurnIn').classList.remove('hide'); } - select.innerHTML = html; - } + let selectSubtitleLanguage = context.querySelector( '#selectSubtitleLanguage' ); - function getSubtitleAppearanceObject(context) { - var appearanceSettings = {}; + playbacksettings.populateLanguages(selectSubtitleLanguage, allCultures); - appearanceSettings.textSize = context.querySelector('#selectTextSize').value; - appearanceSettings.dropShadow = context.querySelector('#selectDropShadow').value; - appearanceSettings.font = context.querySelector('#selectFont').value; - appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value; - appearanceSettings.textColor = context.querySelector('#inputTextColor').value; + selectSubtitleLanguage.value = user.Configuration.SubtitleLanguagePreference || ""; + context.querySelector('#selectSubtitlePlaybackMode').value = user.Configuration.SubtitleMode || ""; - return appearanceSettings; - } + context.querySelector('#selectSubtitlePlaybackMode').dispatchEvent(new CustomEvent('change', {})); - function loadForm(context, user, userSettings, appearanceSettings, apiClient) { + context.querySelector('#selectTextSize').value = appearanceSettings.textSize || ''; + context.querySelector('#selectDropShadow').value = appearanceSettings.dropShadow || ''; + context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent'; + context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff'; + context.querySelector('#selectFont').value = appearanceSettings.font || ''; - apiClient.getCultures().then(function (allCultures) { + context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || ''; - if (appHost.supports('subtitleburnsettings') && user.Policy.EnableVideoPlaybackTranscoding) { - context.querySelector('.fldBurnIn').classList.remove('hide'); - } + onAppearanceFieldChange({ + target: context.querySelector('#selectTextSize') + }); - var selectSubtitleLanguage = context.querySelector('#selectSubtitleLanguage'); + loading.hide(); + }); +} - populateLanguages(selectSubtitleLanguage, allCultures); +function saveUser(context, user, userSettingsInstance, appearanceKey, apiClient) { - selectSubtitleLanguage.value = user.Configuration.SubtitleLanguagePreference || ""; - context.querySelector('#selectSubtitlePlaybackMode').value = user.Configuration.SubtitleMode || ""; + let appearanceSettings = userSettingsInstance.getSubtitleAppearanceSettings( appearanceKey ); + appearanceSettings = Object.assign(appearanceSettings, getSubtitleAppearanceObject(context)); - context.querySelector('#selectSubtitlePlaybackMode').dispatchEvent(new CustomEvent('change', {})); + userSettingsInstance.setSubtitleAppearanceSettings(appearanceSettings, appearanceKey); - context.querySelector('#selectTextSize').value = appearanceSettings.textSize || ''; - context.querySelector('#selectDropShadow').value = appearanceSettings.dropShadow || ''; - context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent'; - context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff'; - context.querySelector('#selectFont').value = appearanceSettings.font || ''; + user.Configuration.SubtitleLanguagePreference = context.querySelector('#selectSubtitleLanguage').value; + user.Configuration.SubtitleMode = context.querySelector('#selectSubtitlePlaybackMode').value; - context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || ''; + return apiClient.updateUserConfiguration(user.Id, user.Configuration); +} - onAppearanceFieldChange({ - target: context.querySelector('#selectTextSize') - }); +export function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) { + + loading.show(); + + appSettings.set('subtitleburnin', context.querySelector('#selectSubtitleBurnIn').value); + + apiClient.getUser(userId).then(function (user) { + + saveUser(context, user, userSettings, instance.appearanceKey, apiClient).then(function () { loading.hide(); + if (enableSaveConfirmation) { + require(['toast'], function (toast) { + toast(globalize.translate('SettingsSaved')); + }); + } + + events.trigger(instance, 'saved'); + + }, function () { + loading.hide(); }); + }); +} + +function onSubtitleModeChange(e) { + + let view = dom.parentWithClass( e.target, 'subtitlesettings' ); + + let subtitlesHelp = view.querySelectorAll( '.subtitlesHelp' ); + for (let i = 0, length = subtitlesHelp.length; i < length; i++) { + subtitlesHelp[i].classList.add('hide'); } + view.querySelector('.subtitles' + this.value + 'Help').classList.remove('hide'); +} - function saveUser(context, user, userSettingsInstance, appearanceKey, apiClient) { +function onAppearanceFieldChange(e) { - var appearanceSettings = userSettingsInstance.getSubtitleAppearanceSettings(appearanceKey); - appearanceSettings = Object.assign(appearanceSettings, getSubtitleAppearanceObject(context)); + let view = dom.parentWithClass( e.target, 'subtitlesettings' ); - userSettingsInstance.setSubtitleAppearanceSettings(appearanceSettings, appearanceKey); + let appearanceSettings = getSubtitleAppearanceObject( view ); - user.Configuration.SubtitleLanguagePreference = context.querySelector('#selectSubtitleLanguage').value; - user.Configuration.SubtitleMode = context.querySelector('#selectSubtitlePlaybackMode').value; + let elements = { + window: view.querySelector( '.subtitleappearance-preview-window' ), + text: view.querySelector( '.subtitleappearance-preview-text' ) + }; - return apiClient.updateUserConfiguration(user.Id, user.Configuration); - } + subtitleAppearanceHelper.applyStyles(elements, appearanceSettings); +} - function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) { +export function embed(options, self) { - loading.show(); + require(['text!./subtitlesettings.template.html'], function (template) { - appSettings.set('subtitleburnin', context.querySelector('#selectSubtitleBurnIn').value); + options.element.classList.add('subtitlesettings'); + options.element.innerHTML = globalize.translateDocument(template, 'core'); - apiClient.getUser(userId).then(function (user) { + options.element.querySelector('form').addEventListener('submit', playbacksettings.OnSubmit.bind(self)); - saveUser(context, user, userSettings, instance.appearanceKey, apiClient).then(function () { + options.element.querySelector('#selectSubtitlePlaybackMode').addEventListener('change', onSubtitleModeChange); + options.element.querySelector('#selectTextSize').addEventListener('change', onAppearanceFieldChange); + options.element.querySelector('#selectDropShadow').addEventListener('change', onAppearanceFieldChange); + options.element.querySelector('#selectFont').addEventListener('change', onAppearanceFieldChange); + options.element.querySelector('#inputTextColor').addEventListener('change', onAppearanceFieldChange); + options.element.querySelector('#inputTextBackground').addEventListener('change', onAppearanceFieldChange); - loading.hide(); - if (enableSaveConfirmation) { - require(['toast'], function (toast) { - toast(globalize.translate('SettingsSaved')); - }); - } + if (options.enableSaveButton) { + options.element.querySelector('.btnSave').classList.remove('hide'); + } - events.trigger(instance, 'saved'); + if (appHost.supports('subtitleappearancesettings')) { + options.element.querySelector('.subtitleAppearanceSection').classList.remove('hide'); + } - }, function () { - loading.hide(); - }); - }); - } + self.loadData(); - function onSubmit(e) { - var self = this; - var apiClient = connectionManager.getApiClient(self.options.serverId); - var userId = self.options.userId; - var userSettings = self.options.userSettings; + if (options.autoFocus) { + focusManager.autoFocus(options.element); + } + }); +} +export function SubtitleSettings(options) { + + this.options = options; + + embed(options, this); +} + +SubtitleSettings.prototype.loadData = function () { + + let self = this; + let context = self.options.element; + + loading.show(); + + let userId = self.options.userId; + let apiClient = connectionManager.getApiClient( self.options.serverId ); + let userSettings = self.options.userSettings; + + apiClient.getUser(userId).then(function (user) { userSettings.setUserInfo(userId, apiClient).then(function () { - var enableSaveConfirmation = self.options.enableSaveConfirmation; - save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation); + self.dataLoaded = true; + + let appearanceSettings = userSettings.getSubtitleAppearanceSettings( self.options.appearanceKey ); + + loadForm(context, user, userSettings, appearanceSettings, apiClient); }); + }); +}; - // Disable default form submission - if (e) { - e.preventDefault(); - } +SubtitleSettings.prototype.submit = function () { + playbacksettings.onSubmit.call(this); +}; - return false; - } +SubtitleSettings.prototype.destroy = function () { + this.options = null; +}; - function onSubtitleModeChange(e) { - - var view = dom.parentWithClass(e.target, 'subtitlesettings'); - - var subtitlesHelp = view.querySelectorAll('.subtitlesHelp'); - for (var i = 0, length = subtitlesHelp.length; i < length; i++) { - subtitlesHelp[i].classList.add('hide'); - } - view.querySelector('.subtitles' + this.value + 'Help').classList.remove('hide'); - } - - function onAppearanceFieldChange(e) { - - var view = dom.parentWithClass(e.target, 'subtitlesettings'); - - var appearanceSettings = getSubtitleAppearanceObject(view); - - var elements = { - window: view.querySelector('.subtitleappearance-preview-window'), - text: view.querySelector('.subtitleappearance-preview-text') - }; - - subtitleAppearanceHelper.applyStyles(elements, appearanceSettings); - } - - function embed(options, self) { - - require(['text!./subtitlesettings.template.html'], function (template) { - - options.element.classList.add('subtitlesettings'); - options.element.innerHTML = globalize.translateDocument(template, 'core'); - - options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self)); - - options.element.querySelector('#selectSubtitlePlaybackMode').addEventListener('change', onSubtitleModeChange); - options.element.querySelector('#selectTextSize').addEventListener('change', onAppearanceFieldChange); - options.element.querySelector('#selectDropShadow').addEventListener('change', onAppearanceFieldChange); - options.element.querySelector('#selectFont').addEventListener('change', onAppearanceFieldChange); - options.element.querySelector('#inputTextColor').addEventListener('change', onAppearanceFieldChange); - options.element.querySelector('#inputTextBackground').addEventListener('change', onAppearanceFieldChange); - - if (options.enableSaveButton) { - options.element.querySelector('.btnSave').classList.remove('hide'); - } - - if (appHost.supports('subtitleappearancesettings')) { - options.element.querySelector('.subtitleAppearanceSection').classList.remove('hide'); - } - - self.loadData(); - - if (options.autoFocus) { - focusManager.autoFocus(options.element); - } - }); - } - - function SubtitleSettings(options) { - - this.options = options; - - embed(options, this); - } - - SubtitleSettings.prototype.loadData = function () { - - var self = this; - var context = self.options.element; - - loading.show(); - - var userId = self.options.userId; - var apiClient = connectionManager.getApiClient(self.options.serverId); - var userSettings = self.options.userSettings; - - apiClient.getUser(userId).then(function (user) { - userSettings.setUserInfo(userId, apiClient).then(function () { - self.dataLoaded = true; - - var appearanceSettings = userSettings.getSubtitleAppearanceSettings(self.options.appearanceKey); - - loadForm(context, user, userSettings, appearanceSettings, apiClient); - }); - }); - }; - - SubtitleSettings.prototype.submit = function () { - onSubmit.call(this); - }; - - SubtitleSettings.prototype.destroy = function () { - this.options = null; - }; - - return SubtitleSettings; -}); +export default { + save: save, + embed: embed, + SubtitleSettings: SubtitleSettings +}; From 6f0843cc6d30629c6e8f190558389785b9037e3a Mon Sep 17 00:00:00 2001 From: Christoph Potas Date: Mon, 4 May 2020 03:20:38 +0200 Subject: [PATCH 0014/2132] ~ convert subtitlesettings to class ~ use base import name instead of relative file path ~ fix "new" calling Signed-off-by: Christoph Potas --- package.json | 1 + src/components/settingshelper.js | 24 ++++ .../subtitleappearancehelper.js | 4 + .../subtitlesettings/subtitlesettings.js | 112 +++++++++++------- src/controllers/user/subtitles.js | 2 +- 5 files changed, 97 insertions(+), 46 deletions(-) create mode 100644 src/components/settingshelper.js diff --git a/package.json b/package.json index fda29b810c..c11b6db913 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "src/components/cardbuilder/cardBuilder.js", "src/components/subtitlesettings/subtitlesettings.js", "src/components/subtitlesettings/subtitleappearancehelper.js", + "src/components/settingshelper.js", "src/scripts/dom.js", "src/components/filedownloader.js", "src/scripts/filesystem.js", diff --git a/src/components/settingshelper.js b/src/components/settingshelper.js new file mode 100644 index 0000000000..0c942ed040 --- /dev/null +++ b/src/components/settingshelper.js @@ -0,0 +1,24 @@ +import globalize from 'globalize'; +/** + * Helper for handling settings + * @module components/settingsHelper + */ +export function populateLanguages(select, languages) { + + let html = ""; + + html += ""; + + for (let i = 0, length = languages.length; i < length; i++) { + + const culture = languages[i]; + + html += ""; + } + + select.innerHTML = html; +} + +export default { + populateLanguages: populateLanguages +}; diff --git a/src/components/subtitlesettings/subtitleappearancehelper.js b/src/components/subtitlesettings/subtitleappearancehelper.js index 7bf4737831..d1c55e1aea 100644 --- a/src/components/subtitlesettings/subtitleappearancehelper.js +++ b/src/components/subtitlesettings/subtitleappearancehelper.js @@ -1,3 +1,7 @@ +/** + * Subtitle settings visual helper + * @module components/subtitleSettings/subtitleAppearanceHelper + */ function getTextStyles(settings, isCue) { let list = []; diff --git a/src/components/subtitlesettings/subtitlesettings.js b/src/components/subtitlesettings/subtitlesettings.js index 8f5038fd65..3e41da953f 100644 --- a/src/components/subtitlesettings/subtitlesettings.js +++ b/src/components/subtitlesettings/subtitlesettings.js @@ -4,9 +4,9 @@ import appHost from 'apphost'; import appSettings from 'appSettings'; import focusManager from 'focusManager'; import loading from 'loading'; -import connectionManager from 'connectionmanager'; -import subtitleAppearanceHelper from 'subtitleappearancehelper'; -import playbacksettings from 'playbacksettings'; +import connectionManager from 'connectionManager'; +import subtitleAppearanceHelper from 'subtitleAppearanceHelper'; +import settingsHelper from '../settingshelper'; import dom from 'dom'; import events from 'events'; import 'listViewStyle'; @@ -15,6 +15,11 @@ import 'emby-input'; import 'emby-checkbox'; import 'flexStyles'; +/** + * Subtitle settings + * @module components/subtitleSettings/subtitleSettings + */ + function getSubtitleAppearanceObject(context) { let appearanceSettings = {}; @@ -37,7 +42,7 @@ function loadForm(context, user, userSettings, appearanceSettings, apiClient) { let selectSubtitleLanguage = context.querySelector( '#selectSubtitleLanguage' ); - playbacksettings.populateLanguages(selectSubtitleLanguage, allCultures); + settingsHelper.populateLanguages(selectSubtitleLanguage, allCultures); selectSubtitleLanguage.value = user.Configuration.SubtitleLanguagePreference || ""; context.querySelector('#selectSubtitlePlaybackMode').value = user.Configuration.SubtitleMode || ""; @@ -73,7 +78,7 @@ function saveUser(context, user, userSettingsInstance, appearanceKey, apiClient) return apiClient.updateUserConfiguration(user.Id, user.Configuration); } -export function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) { +function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) { loading.show(); @@ -123,14 +128,14 @@ function onAppearanceFieldChange(e) { subtitleAppearanceHelper.applyStyles(elements, appearanceSettings); } -export function embed(options, self) { +function embed(options, self) { require(['text!./subtitlesettings.template.html'], function (template) { options.element.classList.add('subtitlesettings'); options.element.innerHTML = globalize.translateDocument(template, 'core'); - options.element.querySelector('form').addEventListener('submit', playbacksettings.OnSubmit.bind(self)); + options.element.querySelector('form').addEventListener('submit', self.onSubmit ); options.element.querySelector('#selectSubtitlePlaybackMode').addEventListener('change', onSubtitleModeChange); options.element.querySelector('#selectTextSize').addEventListener('change', onAppearanceFieldChange); @@ -155,45 +160,62 @@ export function embed(options, self) { }); } -export function SubtitleSettings(options) { +export class SubtitleSettings { - this.options = options; + constructor(options) { - embed(options, this); + this.options = options; + + embed(options, this); + } + + loadData() { + let self = this; + let context = self.options.element; + + loading.show(); + + let userId = self.options.userId; + let apiClient = connectionManager.getApiClient( self.options.serverId ); + let userSettings = self.options.userSettings; + + apiClient.getUser(userId).then(function (user) { + userSettings.setUserInfo(userId, apiClient).then(function () { + self.dataLoaded = true; + + let appearanceSettings = userSettings.getSubtitleAppearanceSettings( self.options.appearanceKey ); + + loadForm(context, user, userSettings, appearanceSettings, apiClient); + }); + }); + } + + submit() { + this.onSubmit( null ); + } + + destroy() { + this.options = null; + } + + onSubmit( e ) { + const self = this; + let apiClient = connectionManager.getApiClient(self.options.serverId); + let userId = self.options.userId; + let userSettings = self.options.userSettings; + + userSettings.setUserInfo(userId, apiClient).then(function () { + + let enableSaveConfirmation = self.options.enableSaveConfirmation; + save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation); + }); + + // Disable default form submission + if (e) { + e.preventDefault(); + } + return false; + } } -SubtitleSettings.prototype.loadData = function () { - - let self = this; - let context = self.options.element; - - loading.show(); - - let userId = self.options.userId; - let apiClient = connectionManager.getApiClient( self.options.serverId ); - let userSettings = self.options.userSettings; - - apiClient.getUser(userId).then(function (user) { - userSettings.setUserInfo(userId, apiClient).then(function () { - self.dataLoaded = true; - - let appearanceSettings = userSettings.getSubtitleAppearanceSettings( self.options.appearanceKey ); - - loadForm(context, user, userSettings, appearanceSettings, apiClient); - }); - }); -}; - -SubtitleSettings.prototype.submit = function () { - playbacksettings.onSubmit.call(this); -}; - -SubtitleSettings.prototype.destroy = function () { - this.options = null; -}; - -export default { - save: save, - embed: embed, - SubtitleSettings: SubtitleSettings -}; +export default SubtitleSettings; diff --git a/src/controllers/user/subtitles.js b/src/controllers/user/subtitles.js index e2b98dc2dd..f449b655c4 100644 --- a/src/controllers/user/subtitles.js +++ b/src/controllers/user/subtitles.js @@ -18,7 +18,7 @@ define(["subtitleSettings", "userSettings", "autoFocuser"], function (SubtitleSe if (subtitleSettingsInstance) { subtitleSettingsInstance.loadData(); } else { - subtitleSettingsInstance = new SubtitleSettings({ + subtitleSettingsInstance = new SubtitleSettings.default({ serverId: ApiClient.serverId(), userId: userId, element: view.querySelector(".settingsContainer"), From 2655b4dca3da200f9dc720f37b4d6e7f32b4d839 Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Mon, 4 May 2020 17:23:57 -0500 Subject: [PATCH 0015/2132] Migrate API changes to apiclient repo --- src/libraries/apiclient/apiclientcore.js | 25 ------------------------ 1 file changed, 25 deletions(-) diff --git a/src/libraries/apiclient/apiclientcore.js b/src/libraries/apiclient/apiclientcore.js index e1892505fc..557a4e1033 100644 --- a/src/libraries/apiclient/apiclientcore.js +++ b/src/libraries/apiclient/apiclientcore.js @@ -356,31 +356,6 @@ define(["events", "appStorage"], function(events, appStorage) { instance.onAuthenticated ? instance.onAuthenticated(instance, result).then(afterOnAuthenticated) : afterOnAuthenticated() }, reject) }) - }, ApiClient.prototype.quickConnect = function (token) { - if (!token) return Promise.reject(); - var url = this.getUrl("Users/AuthenticateWithQuickConnect"); - var instance = this; - return new Promise(function(resolve, reject) { - var postData = { - Token: token - }; - instance.ajax({ - type: "POST", - url: url, - data: JSON.stringify(postData), - dataType: "json", - contentType: "application/json" - }).then(function(result) { - var afterOnAuthenticated = function() { - redetectBitrate(instance); - return resolve(result); - }; - instance.onAuthenticated ? instance.onAuthenticated(instance, result).then(afterOnAuthenticated) : afterOnAuthenticated() - }, reject) - }) - }, ApiClient.prototype.getQuickConnect = function(verb) { - var url = this.getUrl("/QuickConnect/" + verb); - return this.getJSON(url); }, ApiClient.prototype.ensureWebSocket = function() { if (!this.isWebSocketOpenOrConnecting() && this.isWebSocketSupported()) try { this.openWebSocket() From 5b40232b7bf96f5468429f17f928d26eb169076f Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Thu, 7 May 2020 09:45:49 +0300 Subject: [PATCH 0016/2132] Use built-in swiper classes --- src/components/slideshow/slideshow.js | 5 ++--- src/components/slideshow/style.css | 21 --------------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index 4716135ceb..b92d956ff3 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -260,8 +260,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f loop: false, zoom: { minRatio: 1, - toggle: true, - containerClass: 'slider-zoom-container' + toggle: true }, autoplay: !options.interactive, keyboard: { @@ -328,7 +327,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f function getSwiperSlideHtmlFromSlide(item) { var html = ''; html += '
    '; - html += '
    '; + html += '
    '; html += ''; html += '
    '; if (item.title || item.subtitle) { diff --git a/src/components/slideshow/style.css b/src/components/slideshow/style.css index f1fea508d7..400c475664 100644 --- a/src/components/slideshow/style.css +++ b/src/components/slideshow/style.css @@ -40,16 +40,6 @@ text-shadow: 3px 3px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; } -.swiper-slide-img { - max-height: 100%; - max-width: 100%; - display: flex; - justify-content: center; - align-items: center; - text-align: center; - margin: auto; -} - .slideshowButtonIcon { color: #fff; opacity: 0.7; @@ -134,14 +124,3 @@ .slideSubtitle { color: #ccc; } - -.swiper-slide { - display: flex; - flex-direction: column; -} - -.slider-zoom-container { - margin: auto; - max-height: 100%; - max-width: 100%; -} From f516bee14798ddd151d5d2c105b5ad845ce48737 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Thu, 7 May 2020 23:11:19 +0300 Subject: [PATCH 0017/2132] Fix iOS blurry zoomed image --- src/components/slideshow/slideshow.js | 44 ++++++++++++++++++++++++++- src/components/slideshow/style.css | 16 ++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index b92d956ff3..e7302b734f 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -2,9 +2,14 @@ * Image viewer component * @module components/slideshow/slideshow */ -define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputManager, connectionManager, layoutManager, focusManager, browser, appHost) { +define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'dom', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputManager, connectionManager, layoutManager, focusManager, browser, appHost, dom) { 'use strict'; + /** + * Name of transition event. + */ + const transitionEndEventName = dom.whichTransitionEvent(); + /** * Retrieves an item's image URL from the API. * @param {object|string} item - Item used to generate the image URL. @@ -240,6 +245,41 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } } + /** + * Handles zoom changes. + */ + function onZoomChange(scale, imageEl, slideEl) { + const zoomImage = slideEl.querySelector('.swiper-zoom-fakeimg'); + + if (zoomImage) { + zoomImage.style.width = zoomImage.style.height = scale * 100 + '%'; + + if (scale > 1) { + if (zoomImage.classList.contains('swiper-zoom-fakeimg-hidden')) { + // Await for Swiper style changes + setTimeout(() => { + const callback = () => { + imageEl.removeEventListener(transitionEndEventName, callback); + zoomImage.classList.remove('swiper-zoom-fakeimg-hidden'); + }; + + // Swiper set 'transition-duration: 300ms' for auto zoom + // and 'transition-duration: 0s' for touch zoom + const transitionDuration = parseFloat(imageEl.style.transitionDuration.replace(/[a-z]/i, '')); + + if (transitionDuration > 0) { + imageEl.addEventListener(transitionEndEventName, callback); + } else { + callback(); + } + }, 0); + } + } else { + zoomImage.classList.add('swiper-zoom-fakeimg-hidden'); + } + } + } + /** * Initializes the Swiper instance and binds the relevant events. * @param {HTMLElement} dialog - Element containing the dialog. @@ -287,6 +327,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f swiperInstance.on('autoplayStart', onAutoplayStart); swiperInstance.on('autoplayStop', onAutoplayStop); + swiperInstance.on('zoomChange', onZoomChange); }); } @@ -328,6 +369,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f var html = ''; html += '
    '; html += '
    '; + html += `
    `; html += ''; html += '
    '; if (item.title || item.subtitle) { diff --git a/src/components/slideshow/style.css b/src/components/slideshow/style.css index 400c475664..af50eb9cd7 100644 --- a/src/components/slideshow/style.css +++ b/src/components/slideshow/style.css @@ -124,3 +124,19 @@ .slideSubtitle { color: #ccc; } + +.swiper-zoom-fakeimg { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: contain; + z-index: 1; + pointer-events: none; +} + +.swiper-zoom-fakeimg-hidden { + display: none; +} From 1fe17007d6ba2db4553919351967988380e435b0 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Fri, 8 May 2020 01:23:17 +0300 Subject: [PATCH 0018/2132] Enable fake zoom image on Safari (WebKit) --- src/components/slideshow/slideshow.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index e7302b734f..8e3f0d873c 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -10,6 +10,12 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f */ const transitionEndEventName = dom.whichTransitionEvent(); + /** + * Flag to use fake image to fix blurry zoomed image. + * At least WebKit doesn't restore quality for zoomed images. + */ + const useFakeZoomImage = browser.safari; + /** * Retrieves an item's image URL from the API. * @param {object|string} item - Item used to generate the image URL. @@ -327,7 +333,10 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f swiperInstance.on('autoplayStart', onAutoplayStart); swiperInstance.on('autoplayStop', onAutoplayStop); - swiperInstance.on('zoomChange', onZoomChange); + + if (useFakeZoomImage) { + swiperInstance.on('zoomChange', onZoomChange); + } }); } @@ -369,7 +378,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f var html = ''; html += '
    '; html += '
    '; - html += `
    `; + if (useFakeZoomImage) { + html += `
    `; + } html += ''; html += '
    '; if (item.title || item.subtitle) { From 658710e982db7a435d5396549d5712b955fb320b Mon Sep 17 00:00:00 2001 From: Christoph Potas Date: Sat, 9 May 2020 01:20:32 +0200 Subject: [PATCH 0019/2132] + added settingshelper to site defines ~ convert subtitles controller to es6 module + added support for class controller Signed-off-by: Christoph Potas --- .../subtitlesettings/subtitlesettings.js | 2 +- src/components/viewManager/viewManager.js | 4 + src/controllers/user/subtitles.js | 102 ++++++++++-------- src/scripts/site.js | 1 + 4 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/components/subtitlesettings/subtitlesettings.js b/src/components/subtitlesettings/subtitlesettings.js index 3e41da953f..e03c718fc4 100644 --- a/src/components/subtitlesettings/subtitlesettings.js +++ b/src/components/subtitlesettings/subtitlesettings.js @@ -6,7 +6,7 @@ import focusManager from 'focusManager'; import loading from 'loading'; import connectionManager from 'connectionManager'; import subtitleAppearanceHelper from 'subtitleAppearanceHelper'; -import settingsHelper from '../settingshelper'; +import settingsHelper from 'settingsHelper'; import dom from 'dom'; import events from 'events'; import 'listViewStyle'; diff --git a/src/components/viewManager/viewManager.js b/src/components/viewManager/viewManager.js index a8e514e06e..f39c4995fc 100644 --- a/src/components/viewManager/viewManager.js +++ b/src/components/viewManager/viewManager.js @@ -25,6 +25,10 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi // Use controller method var controller = new options.controllerFactory(newView, eventDetail.detail.params); + } else if (typeof options.controllerFactory === 'object') { + + // Use controller class + var controller = new options.controllerFactory.default(newView, eventDetail.detail.params); } if (!options.controllerFactory || dispatchPageEvents) { diff --git a/src/controllers/user/subtitles.js b/src/controllers/user/subtitles.js index f449b655c4..f1efe0c218 100644 --- a/src/controllers/user/subtitles.js +++ b/src/controllers/user/subtitles.js @@ -1,49 +1,63 @@ -define(["subtitleSettings", "userSettings", "autoFocuser"], function (SubtitleSettings, userSettings, autoFocuser) { - "use strict"; +import subtitleSettings from 'subtitleSettings'; +import * as userSettings from 'userSettings'; +import autoFocuser from 'autoFocuser'; - return function (view, params) { - function onBeforeUnload(e) { - if (hasChanges) { - e.returnValue = "You currently have unsaved changes. Are you sure you wish to leave?"; - } +export class SubtitleController { + constructor(view, params) { + this.userId = params.userId || ApiClient.getCurrentUserId(); + this.currentSettings = this.userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings(); + this.hasChanges = false; + this.subtitleSettingsInstance = null; + this.view = view; + + view.addEventListener("viewshow", this.viewShow.bind(this)); + view.addEventListener("change", this.change.bind(this)); + view.addEventListener("viewbeforehide", this.viewBeforeHide.bind(this)); + view.addEventListener("viewdestroy", this.viewDestroy.bind(this)); + } + + viewShow() { + window.addEventListener("beforeunload", this.beforeUnload.bind(this)); + + if (this.subtitleSettingsInstance) { + this.subtitleSettingsInstance.loadData(); + } else { + this.subtitleSettingsInstance = new subtitleSettings({ + serverId: ApiClient.serverId(), + userId: this.userId, + element: this.view.querySelector(".settingsContainer"), + userSettings: this.currentSettings, + enableSaveButton: false, + enableSaveConfirmation: false, + autoFocus: autoFocuser.isEnabled() + }); } + } - var subtitleSettingsInstance; - var hasChanges; - var userId = params.userId || ApiClient.getCurrentUserId(); - var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings(); - view.addEventListener("viewshow", function () { - window.addEventListener("beforeunload", onBeforeUnload); + viewDestroy() { + if (this.subtitleSettingsInstance) { + this.subtitleSettingsInstance.destroy(); + this.subtitleSettingsInstance = null; + } + } - if (subtitleSettingsInstance) { - subtitleSettingsInstance.loadData(); - } else { - subtitleSettingsInstance = new SubtitleSettings.default({ - serverId: ApiClient.serverId(), - userId: userId, - element: view.querySelector(".settingsContainer"), - userSettings: currentSettings, - enableSaveButton: false, - enableSaveConfirmation: false, - autoFocus: autoFocuser.isEnabled() - }); - } - }); - view.addEventListener("change", function () { - hasChanges = true; - }); - view.addEventListener("viewbeforehide", function () { - hasChanges = false; + viewBeforeHide() { + this.hasChanges = false; - if (subtitleSettingsInstance) { - subtitleSettingsInstance.submit(); - } - }); - view.addEventListener("viewdestroy", function () { - if (subtitleSettingsInstance) { - subtitleSettingsInstance.destroy(); - subtitleSettingsInstance = null; - } - }); - }; -}); + if (this.subtitleSettingsInstance) { + this.subtitleSettingsInstance.submit(); + } + } + + change() { + this.hasChanges = true; + } + + beforeUnload(e) { + if (this.hasChanges) { + e.returnValue = "You currently have unsaved changes. Are you sure you wish to leave?"; + } + } +} + +export default SubtitleController; diff --git a/src/scripts/site.js b/src/scripts/site.js index 5ce093e628..37832af5cd 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -831,6 +831,7 @@ var AppInfo = {}; define("upNextDialog", [componentsPath + "/upnextdialog/upnextdialog"], returnFirstDependency); define("subtitleAppearanceHelper", [componentsPath + "/subtitlesettings/subtitleappearancehelper"], returnFirstDependency); define("subtitleSettings", [componentsPath + "/subtitlesettings/subtitlesettings"], returnFirstDependency); + define("settingsHelper", [componentsPath + "/settingshelper"], returnFirstDependency); define("displaySettings", [componentsPath + "/displaysettings/displaysettings"], returnFirstDependency); define("playbackSettings", [componentsPath + "/playbacksettings/playbacksettings"], returnFirstDependency); define("homescreenSettings", [componentsPath + "/homescreensettings/homescreensettings"], returnFirstDependency); From e5bf9bc074d59af7b5e76c734e0af1c6741add52 Mon Sep 17 00:00:00 2001 From: Christoph Potas Date: Sat, 9 May 2020 01:53:13 +0200 Subject: [PATCH 0020/2132] + add controller to es6 module list Signed-off-by: Christoph Potas --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index c11b6db913..4d3d3047bc 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "src/components/subtitlesettings/subtitlesettings.js", "src/components/subtitlesettings/subtitleappearancehelper.js", "src/components/settingshelper.js", + "src/controllers/user/subtitles.js", "src/scripts/dom.js", "src/components/filedownloader.js", "src/scripts/filesystem.js", From 9469c208e19f34b97fda261ca58e7201c1359aed Mon Sep 17 00:00:00 2001 From: Christoph Potas Date: Sat, 9 May 2020 15:42:37 +0200 Subject: [PATCH 0021/2132] ~ switch all strings to single quotes to match ESLint requirements Signed-off-by: Christoph Potas --- src/components/settingshelper.js | 6 +++--- src/controllers/user/subtitles.js | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/settingshelper.js b/src/components/settingshelper.js index 0c942ed040..3bbff9d8ed 100644 --- a/src/components/settingshelper.js +++ b/src/components/settingshelper.js @@ -5,15 +5,15 @@ import globalize from 'globalize'; */ export function populateLanguages(select, languages) { - let html = ""; + let html = ''; - html += ""; + html += "'; for (let i = 0, length = languages.length; i < length; i++) { const culture = languages[i]; - html += ""; + html += "'; } select.innerHTML = html; diff --git a/src/controllers/user/subtitles.js b/src/controllers/user/subtitles.js index f1efe0c218..e88109cc6b 100644 --- a/src/controllers/user/subtitles.js +++ b/src/controllers/user/subtitles.js @@ -10,14 +10,14 @@ export class SubtitleController { this.subtitleSettingsInstance = null; this.view = view; - view.addEventListener("viewshow", this.viewShow.bind(this)); - view.addEventListener("change", this.change.bind(this)); - view.addEventListener("viewbeforehide", this.viewBeforeHide.bind(this)); - view.addEventListener("viewdestroy", this.viewDestroy.bind(this)); + view.addEventListener('viewshow', this.viewShow.bind(this)); + view.addEventListener('change', this.change.bind(this)); + view.addEventListener('viewbeforehide', this.viewBeforeHide.bind(this)); + view.addEventListener('viewdestroy', this.viewDestroy.bind(this)); } viewShow() { - window.addEventListener("beforeunload", this.beforeUnload.bind(this)); + window.addEventListener('beforeunload', this.beforeUnload.bind(this)); if (this.subtitleSettingsInstance) { this.subtitleSettingsInstance.loadData(); @@ -25,7 +25,7 @@ export class SubtitleController { this.subtitleSettingsInstance = new subtitleSettings({ serverId: ApiClient.serverId(), userId: this.userId, - element: this.view.querySelector(".settingsContainer"), + element: this.view.querySelector('.settingsContainer'), userSettings: this.currentSettings, enableSaveButton: false, enableSaveConfirmation: false, @@ -55,7 +55,7 @@ export class SubtitleController { beforeUnload(e) { if (this.hasChanges) { - e.returnValue = "You currently have unsaved changes. Are you sure you wish to leave?"; + e.returnValue = 'You currently have unsaved changes. Are you sure you wish to leave?'; } } } From 7e7613d5de732bc5b59164613252b680895fba77 Mon Sep 17 00:00:00 2001 From: Guilherme Danno Date: Fri, 8 May 2020 19:06:44 -0300 Subject: [PATCH 0022/2132] Update CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 2eae7e6933..65bb24e9fc 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -35,6 +35,7 @@ - [Thibault Nocchi](https://github.com/ThibaultNocchi) - [MrTimscampi](https://github.com/MrTimscampi) - [Sarab Singh](https://github.com/sarab97) + - [GuilhermeHideki](https://github.com/GuilhermeHideki) # Emby Contributors From 934797e07409e7e56cbed10f67504eaf4f0c9d41 Mon Sep 17 00:00:00 2001 From: Guilherme Danno Date: Sun, 3 May 2020 20:45:14 -0300 Subject: [PATCH 0023/2132] feat: add mixins file --- src/styles/_mixins.scss | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/styles/_mixins.scss diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss new file mode 100644 index 0000000000..ed23ab0833 --- /dev/null +++ b/src/styles/_mixins.scss @@ -0,0 +1,23 @@ +@mixin background-cover($position) { + background-position: $position; + background-repeat: no-repeat; + background-size: cover; +} + +@mixin circle($size) { + @include square($size); + border-radius: 100%; +} + +@mixin position($position, $top: null, $right: null, $bottom: null, $left: null) { + position: $position; + top: $top; + right: $right; + bottom: $bottom; + left: $left; +} + +@mixin square($size) { + height: $size; + width: $size; +} From d2bd631cbe93768d36f9bb797be1e1d4a2e9b5b8 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 14 May 2020 23:25:22 +0200 Subject: [PATCH 0024/2132] Fix more sonarqube bugs --- src/components/apphost.js | 40 ++++++++++++++----- src/components/htmlvideoplayer/plugin.js | 15 ------- .../libraryoptionseditor.js | 7 +++- src/components/skinManager.js | 1 - .../dashboard/metadataimagespage.js | 12 ++++-- .../dashboard/notifications/notification.js | 3 -- src/controllers/playback/videoosd.js | 2 +- src/dlnaprofiles.html | 2 +- src/elements/emby-slider/emby-slider.js | 2 +- src/scripts/browser.js | 22 +++------- src/scripts/browserdeviceprofile.js | 17 -------- src/scripts/editorsidebar.js | 4 +- src/scripts/librarymenu.js | 21 +++++----- src/scripts/playlists.js | 3 +- src/scripts/site.js | 17 +++----- 15 files changed, 72 insertions(+), 96 deletions(-) diff --git a/src/components/apphost.js b/src/components/apphost.js index 75e8ba17f1..2fe0cbd33a 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -5,7 +5,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g var disableHlsVideoAudioCodecs = []; if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) { - if (browser.edge || browser.msie) { + if (browser.edge) { disableHlsVideoAudioCodecs.push('mp3'); } @@ -93,18 +93,36 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g function getDeviceName() { var 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' : browser.safari ? 'Safari' : 'Web Browser'; + 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.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'; + } if (browser.ipad) { deviceName += ' iPad'; - } else { - if (browser.iphone) { - deviceName += ' iPhone'; - } else { - if (browser.android) { - deviceName += ' Android'; - } - } + } else if (browser.iphone) { + deviceName += ' iPhone'; + } else if (browser.android) { + deviceName += ' Android'; } return deviceName; @@ -267,7 +285,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g if (enabled) features.push('multiserver'); }); - if (!browser.orsay && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) { + if (!browser.orsay && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) { features.push('subtitleappearancesettings'); } diff --git a/src/components/htmlvideoplayer/plugin.js b/src/components/htmlvideoplayer/plugin.js index a1e51b44f1..bab41eb369 100644 --- a/src/components/htmlvideoplayer/plugin.js +++ b/src/components/htmlvideoplayer/plugin.js @@ -279,14 +279,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa } self.play = function (options) { - - if (browser.msie) { - if (options.playMethod === 'Transcode' && !window.MediaSource) { - alert('Playback of this content is not supported in Internet Explorer. For a better experience, try a modern browser such as Microsoft Edge, Google Chrome, Firefox or Opera.'); - return Promise.reject(); - } - } - self._started = false; self._timeUpdated = false; @@ -1411,13 +1403,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa var video = document.createElement('video'); if (video.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === 'function' || document.pictureInPictureEnabled) { list.push('PictureInPicture'); - } else if (browser.ipad) { - // Unfortunately this creates a false positive on devices where its' not actually supported - if (navigator.userAgent.toLowerCase().indexOf('os 9') === -1) { - if (video.webkitSupportsPresentationMode && video.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === 'function') { - list.push('PictureInPicture'); - } - } } else if (window.Windows) { if (Windows.UI.ViewManagement.ApplicationView.getForCurrentView().isViewModeSupported(Windows.UI.ViewManagement.ApplicationViewMode.compactOverlay)) { list.push('PictureInPicture'); diff --git a/src/components/libraryoptionseditor/libraryoptionseditor.js b/src/components/libraryoptionseditor/libraryoptionseditor.js index fec4656406..832a6ffc5e 100644 --- a/src/components/libraryoptionseditor/libraryoptionseditor.js +++ b/src/components/libraryoptionseditor/libraryoptionseditor.js @@ -120,7 +120,12 @@ define(['globalize', 'dom', 'emby-checkbox', 'emby-select', 'emby-input'], funct html += plugin.Name; html += ''; html += '
    '; - index > 0 ? html += '' : plugins.length > 1 && (html += ''), html += '
    '; + if (index > 0) { + html += ''; + } else if (plugins.length > 1) { + html += ''; + } + html += '
    '; }); html += '
    '; diff --git a/src/components/skinManager.js b/src/components/skinManager.js index 871b6d999f..5c9fe71a58 100644 --- a/src/components/skinManager.js +++ b/src/components/skinManager.js @@ -57,7 +57,6 @@ define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdr var selectedTheme; for (var i = 0, length = themes.length; i < length; i++) { - var theme = themes[i]; if (theme[isDefaultProperty]) { defaultTheme = theme; diff --git a/src/controllers/dashboard/metadataimagespage.js b/src/controllers/dashboard/metadataimagespage.js index 277eecb42a..3047736a68 100644 --- a/src/controllers/dashboard/metadataimagespage.js +++ b/src/controllers/dashboard/metadataimagespage.js @@ -29,14 +29,18 @@ define(['jQuery', 'dom', 'loading', 'libraryMenu', 'globalize', 'listViewStyle'] var promises = [ApiClient.getServerConfiguration(), populateLanguages(page.querySelector('#selectLanguage')), populateCountries(page.querySelector('#selectCountry'))]; Promise.all(promises).then(function(responses) { var config = responses[0]; - page.querySelector('#selectLanguage').value = config.PreferredMetadataLanguage || '', page.querySelector('#selectCountry').value = config.MetadataCountryCode || '', loading.hide(); + page.querySelector('#selectLanguage').value = config.PreferredMetadataLanguage || ''; + page.querySelector('#selectCountry').value = config.MetadataCountryCode || ''; + loading.hide(); }); } function onSubmit() { var form = this; return loading.show(), ApiClient.getServerConfiguration().then(function(config) { - config.PreferredMetadataLanguage = form.querySelector('#selectLanguage').value, config.MetadataCountryCode = form.querySelector('#selectCountry').value, ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult); + config.PreferredMetadataLanguage = form.querySelector('#selectLanguage').value; + config.MetadataCountryCode = form.querySelector('#selectCountry').value; + ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult); }), !1; } @@ -59,6 +63,8 @@ define(['jQuery', 'dom', 'loading', 'libraryMenu', 'globalize', 'listViewStyle'] $(document).on('pageinit', '#metadataImagesConfigurationPage', function() { $('.metadataImagesConfigurationForm').off('submit', onSubmit).on('submit', onSubmit); }).on('pageshow', '#metadataImagesConfigurationPage', function() { - libraryMenu.setTabs('metadata', 2, getTabs), loading.show(), loadPage(this); + libraryMenu.setTabs('metadata', 2, getTabs); + loading.show(); + loadPage(this); }); }); diff --git a/src/controllers/dashboard/notifications/notification.js b/src/controllers/dashboard/notifications/notification.js index 90d453cdae..2b6f7a148a 100644 --- a/src/controllers/dashboard/notifications/notification.js +++ b/src/controllers/dashboard/notifications/notification.js @@ -73,9 +73,6 @@ define(['jQuery', 'emby-checkbox', 'fnchecked'], function ($) { notificationOptions.Options.push(notificationConfig); } - types.filter(function (n) { - return n.Type == type; - })[0]; notificationConfig.Enabled = $('#chkEnabled', page).checked(); notificationConfig.SendToUserMode = $('#selectUsers', page).val(); notificationConfig.DisabledMonitorUsers = $('.chkMonitor', page).get().filter(function (c) { diff --git a/src/controllers/playback/videoosd.js b/src/controllers/playback/videoosd.js index e9923d779c..ac42554b39 100644 --- a/src/controllers/playback/videoosd.js +++ b/src/controllers/playback/videoosd.js @@ -157,7 +157,7 @@ define(['playbackManager', 'dom', 'inputManager', 'datetime', 'itemHelper', 'med }); if (!displayName) { - displayItem.Type; + displayName = displayItem.Type; } titleElement.innerHTML = displayName; diff --git a/src/dlnaprofiles.html b/src/dlnaprofiles.html index 9f2a5e129e..b47b2fd6bf 100644 --- a/src/dlnaprofiles.html +++ b/src/dlnaprofiles.html @@ -11,7 +11,7 @@ - ${Help} + ${Help}

    ${CustomDlnaProfilesHelp}

    diff --git a/src/elements/emby-slider/emby-slider.js b/src/elements/emby-slider/emby-slider.js index e37455dfe1..1b78fca0ae 100644 --- a/src/elements/emby-slider/emby-slider.js +++ b/src/elements/emby-slider/emby-slider.js @@ -148,7 +148,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli this.classList.add('mdl-slider'); this.classList.add('mdl-js-slider'); - if (browser.edge || browser.msie) { + if (browser.edge) { this.classList.add('slider-browser-edge'); } if (!layoutManager.mobile) { diff --git a/src/scripts/browser.js b/src/scripts/browser.js index b1912862b3..a377f08fdb 100644 --- a/src/scripts/browser.js +++ b/src/scripts/browser.js @@ -14,10 +14,6 @@ define([], function () { return true; } - if (userAgent.indexOf('nintendo') !== -1) { - return true; - } - if (userAgent.indexOf('viera') !== -1) { return true; } @@ -93,7 +89,7 @@ define([], function () { var _supportsCssAnimation; var _supportsCssAnimationWithPrefix; function supportsCssAnimation(allowPrefix) { - + // TODO: Assess if this is still needed, as all of our targets should natively support CSS animations. if (allowPrefix) { if (_supportsCssAnimationWithPrefix === true || _supportsCssAnimationWithPrefix === false) { return _supportsCssAnimationWithPrefix; @@ -145,7 +141,6 @@ define([], function () { /(chrome)[ \/]([\w.]+)/.exec(ua) || /(safari)[ \/]([\w.]+)/.exec(ua) || /(firefox)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || []; @@ -161,14 +156,6 @@ define([], function () { if (browser === 'edge') { platform_match = ['']; - } else { - if (ua.indexOf('windows phone') !== -1 || ua.indexOf('iemobile') !== -1) { - - // http://www.neowin.net/news/ie11-fakes-user-agent-to-fool-gmail-in-windows-phone-81-gdr1-update - browser = 'msie'; - } else if (ua.indexOf('like gecko') !== -1 && ua.indexOf('webkit') === -1 && ua.indexOf('opera') === -1 && ua.indexOf('chrome') === -1 && ua.indexOf('safari') === -1) { - browser = 'msie'; - } } if (browser === 'opr') { @@ -211,7 +198,7 @@ define([], function () { browser[matched.platform] = true; } - if (!browser.chrome && !browser.msie && !browser.edge && !browser.opera && userAgent.toLowerCase().indexOf('webkit') !== -1) { + if (!browser.chrome && !browser.edge && !browser.opera && userAgent.toLowerCase().indexOf('webkit') !== -1) { browser.safari = true; } @@ -224,7 +211,10 @@ define([], function () { browser.mobile = true; } - browser.xboxOne = userAgent.toLowerCase().indexOf('xbox') !== -1; + if (userAgent.toLowerCase().indexOf('xbox') !== -1) { + browser.xboxOne = true; + browser.tv = true; + } browser.animate = typeof document !== 'undefined' && document.documentElement.animate != null; browser.tizen = userAgent.toLowerCase().indexOf('tizen') !== -1 || self.tizen != null; browser.web0s = userAgent.toLowerCase().indexOf('Web0S'.toLowerCase()) !== -1; diff --git a/src/scripts/browserdeviceprofile.js b/src/scripts/browserdeviceprofile.js index 2668f20913..a550b69198 100644 --- a/src/scripts/browserdeviceprofile.js +++ b/src/scripts/browserdeviceprofile.js @@ -188,20 +188,6 @@ define(['browser'], function (browser) { return browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; } - function getFlvMseDirectPlayProfile() { - var videoAudioCodecs = ['aac']; - if (!browser.edge && !browser.msie) { - videoAudioCodecs.push('mp3'); - } - - return { - Container: 'flv', - Type: 'Video', - VideoCodec: 'h264', - AudioCodec: videoAudioCodecs.join(',') - }; - } - function getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options) { var supported = false; var profileContainer = container; @@ -230,9 +216,6 @@ define(['browser'], function (browser) { break; case 'flv': supported = browser.tizen || browser.orsay; - //if (!supported && window.MediaSource != null && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')) { - // return getFlvMseDirectPlayProfile(); - //} break; case '3gp': case 'mts': diff --git a/src/scripts/editorsidebar.js b/src/scripts/editorsidebar.js index ee7788407c..8168389566 100644 --- a/src/scripts/editorsidebar.js +++ b/src/scripts/editorsidebar.js @@ -209,7 +209,7 @@ define(['datetime', 'jQuery', 'globalize', 'material-icons'], function (datetime function onNodeOpen(event, data) { var page = $(this).parents('.page')[0]; var node = data.node; - if (node.children && node.children) { + if (node.children) { loadNodesToLoad(page, node); } if (node.li_attr && node.id != '#' && !node.li_attr.loadedFromServer) { @@ -221,7 +221,7 @@ define(['datetime', 'jQuery', 'globalize', 'material-icons'], function (datetime function onNodeLoad(event, data) { var page = $(this).parents('.page')[0]; var node = data.node; - if (node.children && node.children) { + if (node.children) { loadNodesToLoad(page, node); } if (node.li_attr && node.id != '#' && !node.li_attr.loadedFromServer) { diff --git a/src/scripts/librarymenu.js b/src/scripts/librarymenu.js index 81a381bff2..669284ec47 100644 --- a/src/scripts/librarymenu.js +++ b/src/scripts/librarymenu.js @@ -567,27 +567,25 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' if (libraryMenuOptions) { getUserViews(apiClient, userId).then(function (result) { var items = result; - var html = ''; - html += '

    '; - html += globalize.translate('HeaderMedia'); - html += '

    '; + var html = `

    ${globalize.translate('HeaderMedia')}

    `; html += items.map(function (i) { var icon = i.icon || imageHelper.getLibraryIcon(i.CollectionType); var itemId = i.Id; - if (i.onclick) { - i.onclick; - } + const linkHtml = ` + + ${i.Name} + `; - return '' + i.Name + ''; + return linkHtml; }).join(''); libraryMenuOptions.innerHTML = html; var elem = libraryMenuOptions; var sidebarLinks = elem.querySelectorAll('.navMenuOption'); - for (var i = 0, length = sidebarLinks.length; i < length; i++) { - sidebarLinks[i].removeEventListener('click', onSidebarLinkClick); - sidebarLinks[i].addEventListener('click', onSidebarLinkClick); + for (const sidebarLink of sidebarLinks) { + sidebarLink.removeEventListener('click', onSidebarLinkClick); + sidebarLink.addEventListener('click', onSidebarLinkClick); } }); } @@ -900,6 +898,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' updateMenuForPageType(isDashboardPage, isLibraryPage); + // TODO: Seems to do nothing? Check if needed (also in other views). if (!e.detail.isRestored) { window.scrollTo(0, 0); } diff --git a/src/scripts/playlists.js b/src/scripts/playlists.js index 52e7ccb3bd..974d00f8e6 100644 --- a/src/scripts/playlists.js +++ b/src/scripts/playlists.js @@ -69,10 +69,11 @@ define(['loading', 'listView', 'cardBuilder', 'libraryMenu', 'libraryBrowser', ' showLoadingMessage(); var query = getQuery(view); var promise1 = ApiClient.getItems(Dashboard.getCurrentUserId(), query); + // TODO: promise2 is unused, check if necessary. var promise2 = Dashboard.getCurrentUser(); Promise.all([promise1, promise2]).then(function (responses) { var result = responses[0]; - responses[1]; + // TODO: Is the scroll necessary? window.scrollTo(0, 0); var html = ''; var viewStyle = getPageData(view).view; diff --git a/src/scripts/site.js b/src/scripts/site.js index c00169d224..939d362da5 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -368,8 +368,7 @@ var AppInfo = {}; } } - function initRequireWithBrowser(browser) { - var bowerPath = getBowerPath(); + function initRequireWithBrowser() { var componentsPath = getComponentsPath(); var scriptsPath = getScriptsPath(); @@ -378,13 +377,7 @@ var AppInfo = {}; define('lazyLoader', [componentsPath + '/lazyloader/lazyloader-intersectionobserver'], returnFirstDependency); define('shell', [componentsPath + '/shell'], returnFirstDependency); - if ('registerElement' in document) { - define('registerElement', []); - } else if (browser.msie) { - define('registerElement', ['webcomponents'], returnFirstDependency); - } else { - define('registerElement', ['document-register-element'], returnFirstDependency); - } + define('registerElement', ['document-register-element'], returnFirstDependency); define('alert', [componentsPath + '/alert'], returnFirstDependency); @@ -609,8 +602,8 @@ var AppInfo = {}; /* eslint-enable compat/compat */ } - function onWebComponentsReady(browser) { - initRequireWithBrowser(browser); + function onWebComponentsReady() { + initRequireWithBrowser(); if (self.appMode === 'cordova' || self.appMode === 'android' || self.appMode === 'standalone') { AppInfo.isNativeApp = true; @@ -1127,7 +1120,7 @@ var AppInfo = {}; }); })(); - return require(['browser'], onWebComponentsReady); + return onWebComponentsReady(); }(); pageClassOn('viewshow', 'standalonePage', function () { From d4fbbea9915125142b7c41c999cbf9ce9476b487 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 14 May 2020 23:34:23 +0200 Subject: [PATCH 0025/2132] Remove code smells --- src/controllers/dashboard/notifications/notification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/dashboard/notifications/notification.js b/src/controllers/dashboard/notifications/notification.js index 2b6f7a148a..8654f3e991 100644 --- a/src/controllers/dashboard/notifications/notification.js +++ b/src/controllers/dashboard/notifications/notification.js @@ -58,10 +58,10 @@ define(['jQuery', 'emby-checkbox', 'fnchecked'], function ($) { function save(page) { var type = getParameterByName('type'); var promise1 = ApiClient.getNamedConfiguration(notificationsConfigurationKey); + // TODO: Check if this promise is really needed, as it's unused. var promise2 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types')); Promise.all([promise1, promise2]).then(function (responses) { var notificationOptions = responses[0]; - var types = responses[1]; var notificationConfig = notificationOptions.Options.filter(function (n) { return n.Type == type; })[0]; From ac060e1270a33c19e55ec8ae7c6ae2271f007b8f Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 14 May 2020 23:55:23 +0200 Subject: [PATCH 0026/2132] Fix some code smells --- .../accessschedule/accessschedule.js | 6 ++-- src/components/actionsheet/actionsheet.js | 33 ++++--------------- src/components/activitylog.js | 8 +++-- src/components/appRouter.js | 21 +++++------- 4 files changed, 24 insertions(+), 44 deletions(-) diff --git a/src/components/accessschedule/accessschedule.js b/src/components/accessschedule/accessschedule.js index 870231cf03..caf3e82045 100644 --- a/src/components/accessschedule/accessschedule.js +++ b/src/components/accessschedule/accessschedule.js @@ -72,12 +72,12 @@ define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-butt reject(); } }); - dlg.querySelector('.btnCancel').addEventListener('click', function (e) { + dlg.querySelector('.btnCancel').addEventListener('click', function () { dialogHelper.close(dlg); }); - dlg.querySelector('form').addEventListener('submit', function (e) { + dlg.querySelector('form').addEventListener('submit', function (event) { submitSchedule(dlg, options); - e.preventDefault(); + event.preventDefault(); return false; }); }; diff --git a/src/components/actionsheet/actionsheet.js b/src/components/actionsheet/actionsheet.js index e08fbf4a25..ab8998f1bc 100644 --- a/src/components/actionsheet/actionsheet.js +++ b/src/components/actionsheet/actionsheet.js @@ -11,20 +11,10 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'browser', 'dom', 'emby-bu } var box; - var elem; + for (let [index, elem] of elems) { + box = elem.getBoundingClientRect(); - for (var i = 0, length = elems.length; i < length; i++) { - - elem = elems[i]; - // Support: BlackBerry 5, iOS 3 (original iPhone) - // If we don't have gBCR, just use 0,0 rather than error - if (elem.getBoundingClientRect) { - box = elem.getBoundingClientRect(); - } else { - box = { top: 0, left: 0 }; - } - - results[i] = { + results[index] = { top: box.top, left: box.left, width: box.width, @@ -96,13 +86,11 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'browser', 'dom', 'emby-bu scrollY: false }; - var backButton = false; var isFullscreen; if (layoutManager.tv) { dialogOptions.size = 'fullscreen'; isFullscreen = true; - backButton = true; dialogOptions.autoFocus = true; } else { @@ -139,16 +127,10 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'browser', 'dom', 'emby-bu style += 'min-width:' + minWidth + 'px;'; } - var i; - var length; - var option; var renderIcon = false; var icons = []; var itemIcon; - for (i = 0, length = options.items.length; i < length; i++) { - - option = options.items[i]; - + for (let option of options.items) { itemIcon = option.icon || (option.selected ? 'check' : null); if (itemIcon) { @@ -206,10 +188,7 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'browser', 'dom', 'emby-bu menuItemClass += ' actionsheet-xlargeFont'; } - for (i = 0, length = options.items.length; i < length; i++) { - - option = options.items[i]; - + for (let [index, option] of options) { if (option.divider) { html += '
    '; @@ -222,7 +201,7 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'browser', 'dom', 'emby-bu var optionId = option.id == null || option.id === '' ? option.value : option.id; html += ''; - itemIcon = icons[i]; + itemIcon = icons[index]; if (itemIcon) { diff --git a/src/components/activitylog.js b/src/components/activitylog.js index a7b3f48bc2..bbb0995063 100644 --- a/src/components/activitylog.js +++ b/src/components/activitylog.js @@ -34,10 +34,14 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings', html += '
    '; if (entry.Overview) { - html += ''; + html += ``; } - return html += '
    '; + html += ''; + + return html; } function renderList(elem, apiClient, result, startIndex, limit) { diff --git a/src/components/appRouter.js b/src/components/appRouter.js index 2e11ef88d9..77b64f8e88 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -26,11 +26,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM connectionManager.connect({ enableAutoLogin: appSettings.enableAutoLogin() }).then(function (result) { - handleConnectionResult(result, loading); + handleConnectionResult(result); }); } - function handleConnectionResult(result, loading) { + function handleConnectionResult(result) { switch (result.State) { case 'SignedIn': loading.hide(); @@ -246,13 +246,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM } if (setQuality) { - - var quality = 100; - + var quality; var type = options.type || 'Primary'; if (browser.tv || browser.slow) { - + // TODO: wtf if (browser.chrome) { // webp support quality = type === 'Primary' ? 40 : 50; @@ -384,7 +382,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM if (firstResult.State !== 'SignedIn' && !route.anonymous) { - handleConnectionResult(firstResult, loading); + handleConnectionResult(firstResult); return; } } @@ -463,7 +461,6 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM return Promise.resolve(); } - var isHandlingBackToDefault; var isDummyBackToHome; function loadContent(ctx, route, html, request) { @@ -589,8 +586,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM path = '/' + path; } - var baseRoute = baseUrl(); - path = path.replace(baseRoute, ''); + path = path.replace(baseUrl(), ''); if (currentRouteInfo && currentRouteInfo.path === path) { // can't use this with home right now due to the back menu @@ -621,10 +617,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM } function showItem(item, serverId, options) { + // TODO: Refactor this so it only gets items, not strings. if (typeof (item) === 'string') { var apiClient = serverId ? connectionManager.getApiClient(serverId) : connectionManager.currentApiClient(); - apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (item) { - appRouter.showItem(item, options); + apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (itemObject) { + appRouter.showItem(itemObject, options); }); } else { if (arguments.length === 2) { From 594580ec2d50593f599dea02db4d126ffd75dfb3 Mon Sep 17 00:00:00 2001 From: Guilherme Danno Date: Sat, 16 May 2020 00:32:32 -0300 Subject: [PATCH 0027/2132] move files to scss creating this commit so git don't lose track of the changes --- .../css/{clearbutton.css => clearbutton.scss} | 0 .../css/{detailtable.css => detailtable.scss} | 0 .../css/{flexstyles.css => flexstyles.scss} | 0 src/assets/css/{fonts.css => fonts.scss} | 0 .../css/{fonts.sized.css => fonts.sized.scss} | 0 src/assets/css/{ios.css => ios.scss} | 0 src/assets/css/{livetv.css => livetv.scss} | 0 src/assets/css/{site.css => site.scss} | 63 +++++++++---------- 8 files changed, 29 insertions(+), 34 deletions(-) rename src/assets/css/{clearbutton.css => clearbutton.scss} (100%) rename src/assets/css/{detailtable.css => detailtable.scss} (100%) rename src/assets/css/{flexstyles.css => flexstyles.scss} (100%) rename src/assets/css/{fonts.css => fonts.scss} (100%) rename src/assets/css/{fonts.sized.css => fonts.sized.scss} (100%) rename src/assets/css/{ios.css => ios.scss} (100%) rename src/assets/css/{livetv.css => livetv.scss} (100%) rename src/assets/css/{site.css => site.scss} (76%) diff --git a/src/assets/css/clearbutton.css b/src/assets/css/clearbutton.scss similarity index 100% rename from src/assets/css/clearbutton.css rename to src/assets/css/clearbutton.scss diff --git a/src/assets/css/detailtable.css b/src/assets/css/detailtable.scss similarity index 100% rename from src/assets/css/detailtable.css rename to src/assets/css/detailtable.scss diff --git a/src/assets/css/flexstyles.css b/src/assets/css/flexstyles.scss similarity index 100% rename from src/assets/css/flexstyles.css rename to src/assets/css/flexstyles.scss diff --git a/src/assets/css/fonts.css b/src/assets/css/fonts.scss similarity index 100% rename from src/assets/css/fonts.css rename to src/assets/css/fonts.scss diff --git a/src/assets/css/fonts.sized.css b/src/assets/css/fonts.sized.scss similarity index 100% rename from src/assets/css/fonts.sized.css rename to src/assets/css/fonts.sized.scss diff --git a/src/assets/css/ios.css b/src/assets/css/ios.scss similarity index 100% rename from src/assets/css/ios.css rename to src/assets/css/ios.scss diff --git a/src/assets/css/livetv.css b/src/assets/css/livetv.scss similarity index 100% rename from src/assets/css/livetv.css rename to src/assets/css/livetv.scss diff --git a/src/assets/css/site.css b/src/assets/css/site.scss similarity index 76% rename from src/assets/css/site.css rename to src/assets/css/site.scss index 627145abc1..db179afb2a 100644 --- a/src/assets/css/site.css +++ b/src/assets/css/site.scss @@ -1,19 +1,19 @@ -body, -html { +@mixin fullpage { margin: 0; padding: 0; height: 100%; } -.clipForScreenReader { - clip: rect(1px, 1px, 1px, 1px); - clip-path: inset(50%); - height: 1px; - width: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; +html { + @include fullpage; + line-height: 1.35; +} + +body { + @include fullpage; + overflow-x: hidden; + background-color: transparent !important; + -webkit-font-smoothing: antialiased; } .material-icons { @@ -30,10 +30,6 @@ html { contain: strict; } -html { - line-height: 1.35; -} - .layout-mobile, .layout-tv { -webkit-touch-callout: none; @@ -44,12 +40,6 @@ html { user-select: none; } -body { - overflow-x: hidden; - background-color: transparent !important; - -webkit-font-smoothing: antialiased; -} - .mainAnimatedPage { contain: style size !important; } @@ -62,7 +52,7 @@ body { overflow-y: hidden !important; } -div[data-role=page] { +div[data-role="page"] { outline: 0; } @@ -75,10 +65,10 @@ div[data-role=page] { padding-left: 0.15em; font-weight: 400; white-space: normal !important; -} -.fieldDescription + .fieldDescription { - margin-top: 0.3em; + + .fieldDescription { + margin-top: 0.3em; + } } .content-primary, @@ -89,9 +79,14 @@ div[data-role=page] { padding-bottom: 5em !important; } -@media all and (min-width: 50em) { - .readOnlyContent, - form { +.readOnlyContent { + @media all and (min-width: 50em) { + max-width: 54em; + } +} + +form { + @media all and (min-width: 50em) { max-width: 54em; } } @@ -111,12 +106,12 @@ div[data-role=page] { .headroom { will-change: transform; transition: transform 200ms linear; -} -.headroom--pinned { - transform: translateY(0%); -} + &--pinned { + transform: translateY(0%); + } -.headroom--unpinned { - transform: translateY(-100%); + &--unpinned { + transform: translateY(-100%); + } } From 575dcd8334c9b635bb375eaec1b6d80b941ab380 Mon Sep 17 00:00:00 2001 From: Guilherme Danno Date: Sat, 16 May 2020 00:54:46 -0300 Subject: [PATCH 0028/2132] refactor: use scss features --- src/assets/css/fonts.scss | 23 ++++++++--------- src/assets/css/fonts.sized.scss | 44 ++++++++++++++++----------------- src/assets/css/livetv.scss | 4 +-- src/assets/css/site.scss | 11 +++++++++ 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/assets/css/fonts.scss b/src/assets/css/fonts.scss index cb0da0f80f..abffd3a0d2 100644 --- a/src/assets/css/fonts.scss +++ b/src/assets/css/fonts.scss @@ -1,5 +1,11 @@ -html { +@mixin font($weight: null, $size: null) { font-family: "Noto Sans", sans-serif; + font-weight: $weight; + font-size: $size; +} + +html { + @include font; font-size: 93%; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; @@ -7,25 +13,16 @@ html { text-rendering: optimizeLegibility; } -h1, -h2, -h3 { - font-family: "Noto Sans", sans-serif; -} - h1 { - font-weight: 400; - font-size: 1.8em; + @include font(400, 1.8em); } h2 { - font-weight: 400; - font-size: 1.5em; + @include font(400, 1.5em); } h3 { - font-weight: 400; - font-size: 1.17em; + @include font(400, 1.17em); } .layout-tv { diff --git a/src/assets/css/fonts.sized.scss b/src/assets/css/fonts.sized.scss index f60a94f236..1cec58a4a6 100644 --- a/src/assets/css/fonts.sized.scss +++ b/src/assets/css/fonts.sized.scss @@ -1,31 +1,31 @@ -h1 { +@mixin header-font($size: null) { font-weight: 400; - font-size: 1.8em; + font-size: $size; } -.layout-desktop h1 { - font-size: 2em; -} - -h2 { - font-weight: 400; - font-size: 1.5em; -} - -h3 { - font-weight: 400; - font-size: 1.17em; -} - -@media all and (min-height: 720px) { - html { +html { + @media all and (min-height: 720px) { font-size: 20px; } -} -/* This is supposed to be 1080p, but had to reduce the min height to account for possible browser chrome */ -@media all and (min-height: 1000px) { - html { + /* This is supposed to be 1080p, but had to reduce the min height to account for possible browser chrome */ + @media all and (min-height: 1000px) { font-size: 27px; } } + +h1 { + @include header-font(1.8em); + + .layout-desktop & { + font-size: 2em; + } +} + +h2 { + @include header-font(1.8em); +} + +h3 { + @include header-font(1.17em); +} diff --git a/src/assets/css/livetv.scss b/src/assets/css/livetv.scss index 695adff8c5..032bcddf48 100644 --- a/src/assets/css/livetv.scss +++ b/src/assets/css/livetv.scss @@ -2,8 +2,8 @@ padding-bottom: 15em; } -@media all and (min-width: 62.5em) { - #guideTab { +#guideTab { + @media all and (min-width: 62.5em) { padding-left: 0.5em; } } diff --git a/src/assets/css/site.scss b/src/assets/css/site.scss index db179afb2a..d900a8bb40 100644 --- a/src/assets/css/site.scss +++ b/src/assets/css/site.scss @@ -16,6 +16,17 @@ body { -webkit-font-smoothing: antialiased; } +.clipForScreenReader { + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(50%); + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; +} + .material-icons { /* Fix font ligatures on older WebOS versions */ -webkit-font-feature-settings: "liga"; From 5dd6f108518622703422443b03ebc988a566bb00 Mon Sep 17 00:00:00 2001 From: ferferga Date: Sat, 16 May 2020 15:21:07 +0200 Subject: [PATCH 0029/2132] Revert 9a47428 changes --- src/addserver.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addserver.html b/src/addserver.html index 02850fffb8..fc0145e25d 100644 --- a/src/addserver.html +++ b/src/addserver.html @@ -3,7 +3,7 @@

    ${HeaderConnectToServer}

    - +
    ${LabelServerHostHelp}

    From 8478b0ed569a3df0c538ecd18256e90a93da1a7c Mon Sep 17 00:00:00 2001 From: dkanada Date: Tue, 5 May 2020 23:02:05 +0900 Subject: [PATCH 0030/2132] basic skeleton for epub reader --- package.json | 1 + src/assets/css/site.css | 8 +++++ src/bundle.js | 5 ++++ src/components/bookplayer/plugin.js | 35 ++++++++++++++++++++++ src/components/playback/playbackmanager.js | 6 ++-- src/scripts/site.js | 2 ++ 6 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/components/bookplayer/plugin.js diff --git a/package.json b/package.json index 02d0c804d2..8a6ddcf2aa 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "core-js": "^3.6.5", "date-fns": "^2.13.0", "document-register-element": "^1.14.3", + "epubjs": "^0.3.85", "fast-text-encoding": "^1.0.1", "flv.js": "^1.5.0", "headroom.js": "^0.11.0", diff --git a/src/assets/css/site.css b/src/assets/css/site.css index 627145abc1..467f48d4fd 100644 --- a/src/assets/css/site.css +++ b/src/assets/css/site.css @@ -96,6 +96,14 @@ div[data-role=page] { } } +#bookPlayer { + position: relative; + height: 100%; + overflow: auto; + z-index: 100; + background: #fff; +} + .headerHelpButton { margin-left: 1.25em !important; padding-bottom: 0.4em !important; diff --git a/src/bundle.js b/src/bundle.js index d7ba6c6a51..d4a97247f8 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -102,6 +102,11 @@ _define('jellyfin-noto', function () { return noto; }); +var epubjs = require('epubjs'); +_define('epubjs', function () { + return epubjs; +}); + // page.js var page = require('page'); _define('page', function() { diff --git a/src/components/bookplayer/plugin.js b/src/components/bookplayer/plugin.js new file mode 100644 index 0000000000..c00b4b7c40 --- /dev/null +++ b/src/components/bookplayer/plugin.js @@ -0,0 +1,35 @@ +define(['connectionManager', 'dom'], function (connectionManager, dom) { + 'use strict'; + + function BookPlayer() { + var self = this; + + self.name = 'Book Player'; + self.type = 'mediaplayer'; + self.id = 'bookplayer'; + self.priority = 1; + } + + BookPlayer.prototype.play = function (values) { + var serverId = values.items[0].ServerId + var apiClient = connectionManager.getApiClient(serverId); + var options = values; + + require(['epubjs', 'appFooter-shared'], function (epubjs, appFooter) { + appFooter.element.insertAdjacentHTML('beforebegin', '
    '); + var element = document.getElementById('bookPlayer'); + + var downloadHref = apiClient.getItemDownloadUrl(options.items[0].Id); + + var book = epubjs.default(downloadHref, { openAs: 'epub' }); + var rendition = book.renderTo(element, { method: "continuous", width: "100%", height: "100%" }); + var displayed = rendition.display(); + }); + }; + + BookPlayer.prototype.canPlayMediaType = function (mediaType) { + return (mediaType || '').toLowerCase() === 'book'; + }; + + return BookPlayer; +}); diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index dea419c8cc..381ab78693 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -2182,7 +2182,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla // Only used internally self.getCurrentTicks = getCurrentTicks; - function playPhotos(items, options, user) { + function playOther(items, options, user) { var playStartIndex = options.startIndex || 0; var player = getPlayer(items[playStartIndex], options); @@ -2211,9 +2211,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return Promise.reject(); } - if (firstItem.MediaType === 'Photo') { + if (firstItem.MediaType === 'Photo' || firstItem.MediaType === 'Book') { - return playPhotos(items, options, user); + return playOther(items, options, user); } var apiClient = connectionManager.getApiClient(firstItem.ServerId); diff --git a/src/scripts/site.js b/src/scripts/site.js index ddb152eb4c..0672ae0db5 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -484,6 +484,7 @@ var AppInfo = {}; 'components/htmlAudioPlayer/plugin', 'components/htmlVideoPlayer/plugin', 'components/photoPlayer/plugin', + 'components/bookplayer/plugin', 'components/youtubeplayer/plugin', 'components/backdropScreensaver/plugin', 'components/logoScreensaver/plugin' @@ -670,6 +671,7 @@ var AppInfo = {}; 'fetch', 'flvjs', 'jstree', + 'epubjs', 'jQuery', 'hlsjs', 'howler', From 40e24bcaf0f5b4894c1f3087b9590da21dd2b0f3 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Sat, 16 May 2020 22:02:52 +1000 Subject: [PATCH 0031/2132] Implement minimalistic EPUB reader --- src/assets/css/site.css | 8 -- src/components/bookPlayer/plugin.js | 133 ++++++++++++++++++++++++++++ src/components/bookPlayer/style.css | 20 +++++ src/components/bookplayer/plugin.js | 35 -------- src/scripts/site.js | 2 +- yarn.lock | 94 +++++++++++++++++++- 6 files changed, 247 insertions(+), 45 deletions(-) create mode 100644 src/components/bookPlayer/plugin.js create mode 100644 src/components/bookPlayer/style.css delete mode 100644 src/components/bookplayer/plugin.js diff --git a/src/assets/css/site.css b/src/assets/css/site.css index 467f48d4fd..627145abc1 100644 --- a/src/assets/css/site.css +++ b/src/assets/css/site.css @@ -96,14 +96,6 @@ div[data-role=page] { } } -#bookPlayer { - position: relative; - height: 100%; - overflow: auto; - z-index: 100; - background: #fff; -} - .headerHelpButton { margin-left: 1.25em !important; padding-bottom: 0.4em !important; diff --git a/src/components/bookPlayer/plugin.js b/src/components/bookPlayer/plugin.js new file mode 100644 index 0000000000..d0f040c4fd --- /dev/null +++ b/src/components/bookPlayer/plugin.js @@ -0,0 +1,133 @@ +define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavigation', 'dialogHelper', 'apphost', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (connectionManager, dom, loading, playbackManager, keyboardnavigation, dialogHelper, appHost) { + 'use strict'; + + function BookPlayer() { + let self = this; + + self.name = 'Book Player'; + self.type = 'mediaplayer'; + self.id = 'bookplayer'; + self.priority = 1; + + self.play = function (options) { + loading.show(); + let elem = createMediaElement(); + return setCurrentSrc(elem, options); + }; + + self.stop = function () { + let elem = self._mediaElement; + let rendition = self._rendition; + + if (elem && rendition) { + rendition.destroy(); + + elem.remove(); + self._mediaElement = null; + } + }; + + function onWindowKeyUp(e) { + let key = keyboardnavigation.getKeyName(e); + let rendition = self._rendition; + let book = rendition.book; + + switch (key) { + case 'l': + case 'ArrowRight': + case 'Right': + book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next(); + break; + case 'j': + case 'ArrowLeft': + case 'Left': + book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev(); + break; + case 'Escape': + dialogHelper.close(self._mediaElement); + break; + } + } + + function onDialogClosed() { + self.stop(); + } + + function createMediaElement() { + let elem = self._mediaElement; + + if (elem) { + return elem; + } + + elem = document.getElementById('bookPlayer'); + + if (!elem) { + elem = dialogHelper.createDialog({ + exitAnimationDuration: 400, + size: 'fullscreen', + autoFocus: false, + scrollY: false, + exitAnimation: 'fadeout', + removeOnClose: true + }); + elem.id = 'bookPlayer'; + + let html = ''; + html += '
    '; + html += ''; + html += '
    '; + + elem.innerHTML = html; + + elem.querySelector('.btnBookplayerExit').addEventListener('click', function () { + dialogHelper.close(elem); + }); + + dialogHelper.open(elem); + + elem.addEventListener('close', onDialogClosed); + } + + self._mediaElement = elem; + + return elem; + } + + function setCurrentSrc(elem, options) { + let serverId = options.items[0].ServerId; + let apiClient = connectionManager.getApiClient(serverId); + + return new Promise(function (resolve, reject) { + require(['epubjs'], function (epubjs) { + let downloadHref = apiClient.getItemDownloadUrl(options.items[0].Id); + self._currentSrc = downloadHref; + + let book = epubjs.default(downloadHref, {openAs: 'epub'}); + + let rendition = book.renderTo(elem, {width: '100%', height: '97%'}); + self._rendition = rendition; + + return rendition.display().then(function () { + document.addEventListener('keyup', onWindowKeyUp); + // FIXME: I don't really get why document keyup event is not triggered when epub is in focus + self._rendition.on('keyup', onWindowKeyUp); + + loading.hide(); + + return resolve(); + }, function () { + console.error('Failed to display epub'); + return reject(); + }); + }); + }); + } + } + + BookPlayer.prototype.canPlayMediaType = function (mediaType) { + return (mediaType || '').toLowerCase() === 'book'; + }; + + return BookPlayer; +}); diff --git a/src/components/bookPlayer/style.css b/src/components/bookPlayer/style.css new file mode 100644 index 0000000000..295fae5c5b --- /dev/null +++ b/src/components/bookPlayer/style.css @@ -0,0 +1,20 @@ +#bookPlayer { + position: relative; + height: 100%; + width: 100%; + overflow: auto; + z-index: 100; + background: #fff; +} + +.topActionButtons { + right: 0.5vh; + top: 0.5vh; + z-index: 1002; + position: absolute; +} + +.bookplayerButtonIcon { + color: black; + opacity: 0.7; +} diff --git a/src/components/bookplayer/plugin.js b/src/components/bookplayer/plugin.js deleted file mode 100644 index c00b4b7c40..0000000000 --- a/src/components/bookplayer/plugin.js +++ /dev/null @@ -1,35 +0,0 @@ -define(['connectionManager', 'dom'], function (connectionManager, dom) { - 'use strict'; - - function BookPlayer() { - var self = this; - - self.name = 'Book Player'; - self.type = 'mediaplayer'; - self.id = 'bookplayer'; - self.priority = 1; - } - - BookPlayer.prototype.play = function (values) { - var serverId = values.items[0].ServerId - var apiClient = connectionManager.getApiClient(serverId); - var options = values; - - require(['epubjs', 'appFooter-shared'], function (epubjs, appFooter) { - appFooter.element.insertAdjacentHTML('beforebegin', '
    '); - var element = document.getElementById('bookPlayer'); - - var downloadHref = apiClient.getItemDownloadUrl(options.items[0].Id); - - var book = epubjs.default(downloadHref, { openAs: 'epub' }); - var rendition = book.renderTo(element, { method: "continuous", width: "100%", height: "100%" }); - var displayed = rendition.display(); - }); - }; - - BookPlayer.prototype.canPlayMediaType = function (mediaType) { - return (mediaType || '').toLowerCase() === 'book'; - }; - - return BookPlayer; -}); diff --git a/src/scripts/site.js b/src/scripts/site.js index 0672ae0db5..dd86701724 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -484,7 +484,7 @@ var AppInfo = {}; 'components/htmlAudioPlayer/plugin', 'components/htmlVideoPlayer/plugin', 'components/photoPlayer/plugin', - 'components/bookplayer/plugin', + 'components/bookPlayer/plugin', 'components/youtubeplayer/plugin', 'components/backdropScreensaver/plugin', 'components/logoScreensaver/plugin' diff --git a/yarn.lock b/yarn.lock index 2be9c6baf9..3dac9c2dfb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,6 +858,20 @@ resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880" integrity sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA== +"@types/jszip@^3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@types/jszip/-/jszip-3.4.1.tgz#e7a4059486e494c949ef750933d009684227846f" + integrity sha512-TezXjmf3lj+zQ651r6hPqvSScqBLvyPI9FxdXBqpEwBijNGQ2NXpaFW/7joGzveYkKQUil7iiDHLo6LV71Pc0A== + dependencies: + jszip "*" + +"@types/localforage@0.0.34": + version "0.0.34" + resolved "https://registry.yarnpkg.com/@types/localforage/-/localforage-0.0.34.tgz#5e31c32dd8791ec4b9ff3ef47c9cb55b2d0d9438" + integrity sha1-XjHDLdh5HsS5/z70fJy1Wy0NlDg= + dependencies: + localforage "*" + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -3822,6 +3836,23 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== +epubjs@^0.3.85: + version "0.3.87" + resolved "https://registry.yarnpkg.com/epubjs/-/epubjs-0.3.87.tgz#0a2a94e59777e04548deff49a1c713ccbf3378fc" + integrity sha512-UlzXj04JQaUJ4p6ux/glQcVC4ayBtnpHT7niw4ozGy8EOQTAr8+/z7UZEHUmqQj4yHIoPYC4qGXtmzNqImWx1A== + dependencies: + "@types/jszip" "^3.4.1" + "@types/localforage" "0.0.34" + event-emitter "^0.3.5" + jszip "^3.4.0" + localforage "^1.7.3" + lodash "^4.17.15" + marks-pane "^1.0.9" + path-webpack "0.0.3" + stream-browserify "^2.0.1" + url-polyfill "^1.1.9" + xmldom "^0.1.27" + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -5856,6 +5887,11 @@ imagemin@^7.0.0: p-pipe "^3.0.0" replace-ext "^1.0.0" +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + immutable@^3: version "3.8.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" @@ -6654,6 +6690,16 @@ jstree@^3.3.7: dependencies: jquery ">=1.9.1" +jszip@*, jszip@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350" + integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + set-immediate-shim "~1.0.1" + junk@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" @@ -6782,6 +6828,20 @@ levn@^0.3.0, levn@~0.3.0: version "4.0.0" resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#58e9a3f1a7f7883556ee002545f445a430120639" +lie@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4= + dependencies: + immediate "~3.0.5" + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + liftoff@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" @@ -6874,6 +6934,13 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" +localforage@*, localforage@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.7.3.tgz#0082b3ca9734679e1bd534995bdd3b24cf10f204" + integrity sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ== + dependencies: + lie "3.1.1" + localtunnel@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-1.9.2.tgz#0012fcabc29cf964c130a01858768aa2bb65b5af" @@ -7244,6 +7311,11 @@ markdown-table@^2.0.0: dependencies: repeat-string "^1.0.0" +marks-pane@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/marks-pane/-/marks-pane-1.0.9.tgz#c0b5ab813384d8cd81faaeb3bbf3397dc809c1b3" + integrity sha512-Ahs4oeG90tbdPWwAJkAAoHg2lRR8lAs9mZXETNPO9hYg3AkjUJBKi1NQ4aaIQZVGrig7c/3NUV1jANl8rFTeMg== + matchdep@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" @@ -8312,7 +8384,7 @@ page@^1.11.6: dependencies: path-to-regexp "~1.2.1" -pako@~1.0.5: +pako@~1.0.2, pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== @@ -8559,6 +8631,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path-webpack@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/path-webpack/-/path-webpack-0.0.3.tgz#ff6dec749eec5a94605c04d5f63fc55607a03a16" + integrity sha1-/23sdJ7sWpRgXATV9j/FVgegOhY= + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -10510,6 +10587,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-immediate-shim@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -12188,6 +12270,11 @@ url-parse@^1.4.3: querystringify "^2.1.1" requires-port "^1.0.0" +url-polyfill@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/url-polyfill/-/url-polyfill-1.1.9.tgz#2c8d4224889a5c942800f708f5585368085603d9" + integrity sha512-q/R5sowGuRfKHm497swkV+s9cPYtZRkHxzpDjRhqLO58FwdWTIkt6Y/fJlznUD/exaKx/XnDzCYXz0V16ND7ow== + url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" @@ -12745,6 +12832,11 @@ x-is-string@^0.1.0: resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI= +xmldom@^0.1.27: + version "0.1.31" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" + integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + xmlhttprequest-ssl@~1.5.4: version "1.5.5" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" From 05b9f4df3ebc0fadfd8d2309463515f46635052c Mon Sep 17 00:00:00 2001 From: dkanada Date: Tue, 19 May 2020 03:59:37 +0900 Subject: [PATCH 0032/2132] convert photo player to class --- package.json | 1 + src/components/photoPlayer/plugin.js | 39 +++++++++++++++------------- src/components/pluginManager.js | 2 +- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 02d0c804d2..7eab7662b0 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "src/components/actionSheet/actionSheet.js", "src/components/playmenu.js", "src/components/indicators/indicators.js", + "src/components/photoPlayer/plugin.js", "src/scripts/keyboardNavigation.js", "src/scripts/settings/appSettings.js", "src/scripts/settings/userSettings.js", diff --git a/src/components/photoPlayer/plugin.js b/src/components/photoPlayer/plugin.js index 4dc00809dc..aa5464d77c 100644 --- a/src/components/photoPlayer/plugin.js +++ b/src/components/photoPlayer/plugin.js @@ -1,19 +1,22 @@ -define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackManager', 'appRouter', 'appSettings', 'connectionManager'], function (browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager) { - 'use strict'; +import browser from 'browser'; +import require from 'require'; +import events from 'events'; +import appHost from 'apphost'; +import loading from 'loading'; +import dom from 'dom'; +import playbackManager from 'playbackManager'; +import appRouter from 'appRouter'; +import connectionManager from 'connectionManager'; - function PhotoPlayer() { - - var self = this; - - self.name = 'Photo Player'; - self.type = 'mediaplayer'; - self.id = 'photoplayer'; - - // Let any players created by plugins take priority - self.priority = 1; +export class PhotoPlayer { + constructor() { + this.name = 'Photo Player'; + this.type = 'mediaplayer'; + this.id = 'photoplayer'; + this.priority = 1; } - PhotoPlayer.prototype.play = function (options) { + play(options) { return new Promise(function (resolve, reject) { @@ -41,12 +44,12 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa }); }); }); - }; + } - PhotoPlayer.prototype.canPlayMediaType = function (mediaType) { + canPlayMediaType(mediaType) { return (mediaType || '').toLowerCase() === 'photo'; - }; + } +} - return PhotoPlayer; -}); +export default PhotoPlayer; diff --git a/src/components/pluginManager.js b/src/components/pluginManager.js index 2126d73b3c..35754a1094 100644 --- a/src/components/pluginManager.js +++ b/src/components/pluginManager.js @@ -34,7 +34,7 @@ define(['events'], function (events) { require([url, 'globalize', 'appRouter'], function (pluginFactory, globalize, appRouter) { - var plugin = new pluginFactory(); + var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory(); // See if it's already installed var existing = instance.pluginsList.filter(function (p) { From c94add0a623217339f82eb505f1cd85ace6720ff Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Tue, 19 May 2020 16:05:44 +1000 Subject: [PATCH 0033/2132] Add table of contents to EPUB reader --- src/components/bookPlayer/plugin.js | 61 ++++++++++++++++++++++++++++- src/components/bookPlayer/style.css | 17 +++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/components/bookPlayer/plugin.js b/src/components/bookPlayer/plugin.js index d0f040c4fd..1abbf778c8 100644 --- a/src/components/bookPlayer/plugin.js +++ b/src/components/bookPlayer/plugin.js @@ -45,6 +45,9 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig break; case 'Escape': dialogHelper.close(self._mediaElement); + if (self._tocElement) { + dialogHelper.close(self._tocElement); + } break; } } @@ -53,6 +56,19 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig self.stop(); } + function replaceLinks(contents, f) { + let links = contents.querySelectorAll('a[href]'); + + links.forEach((link) => { + let href = link.getAttribute('href'); + + link.onclick = function () { + f(href); + return false; + }; + }); + } + function createMediaElement() { let elem = self._mediaElement; @@ -74,9 +90,12 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig elem.id = 'bookPlayer'; let html = ''; - html += '
    '; + html += '
    '; html += ''; html += '
    '; + html += '
    '; + html += ''; + html += '
    '; elem.innerHTML = html; @@ -84,6 +103,46 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig dialogHelper.close(elem); }); + elem.querySelector('.btnBookplayerToc').addEventListener('click', function () { + let rendition = self._rendition; + if (rendition) { + let tocElement = dialogHelper.createDialog({ + size: 'small', + autoFocus: false, + removeOnClose: true + }); + tocElement.id = 'dialogToc'; + + let tocHtml = '
    '; + tocHtml += ''; + tocHtml += '
    '; + tocHtml += '
      '; + rendition.book.navigation.forEach((chapter) => { + tocHtml += '
    • '; + // Remove '../' from href + let link = chapter.href.startsWith('../') ? chapter.href.substr(3) : chapter.href; + tocHtml += `${chapter.label}`; + tocHtml += '
    • '; + }); + tocHtml += '
    '; + tocElement.innerHTML = tocHtml; + + tocElement.querySelector('.btnBookplayerTocClose').addEventListener('click', function () { + dialogHelper.close(tocElement); + }); + + replaceLinks(tocElement, (href) => { + let relative = rendition.book.path.relative(href); + rendition.display(relative); + dialogHelper.close(tocElement); + }); + + self._tocElement = tocElement; + + dialogHelper.open(tocElement); + } + }); + dialogHelper.open(elem); elem.addEventListener('close', onDialogClosed); diff --git a/src/components/bookPlayer/style.css b/src/components/bookPlayer/style.css index 295fae5c5b..581438a003 100644 --- a/src/components/bookPlayer/style.css +++ b/src/components/bookPlayer/style.css @@ -7,14 +7,29 @@ background: #fff; } -.topActionButtons { +.topRightActionButtons { right: 0.5vh; top: 0.5vh; z-index: 1002; position: absolute; } +.topLeftActionButtons { + left: 0.5vh; + top: 0.5vh; + z-index: 1002; + position: absolute; +} + .bookplayerButtonIcon { color: black; opacity: 0.7; } + +#dialogToc { + background-color: white; +} + +.toc li { + margin-bottom: 5px; +} From 5cfa0360ed17c883e217a2d5eff64e4c43ad7a92 Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 20 May 2020 15:37:58 +0200 Subject: [PATCH 0034/2132] Use novalidate for addserver.html form --- src/addserver.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/addserver.html b/src/addserver.html index fc0145e25d..d25a8eef98 100644 --- a/src/addserver.html +++ b/src/addserver.html @@ -1,9 +1,9 @@
    - +

    ${HeaderConnectToServer}

    - +
    ${LabelServerHostHelp}

    From c843bc9fb668d723da01d6017c87c5b0afc4e3f3 Mon Sep 17 00:00:00 2001 From: Delgan Date: Thu, 21 May 2020 15:28:19 +0200 Subject: [PATCH 0035/2132] Migrate components/playback functions to ES6 modules --- package.json | 18 +- src/components/playback/brightnessosd.js | 312 +++++----- src/components/playback/nowplayinghelper.js | 160 +++-- .../playback/playbackorientation.js | 90 +-- .../playback/playerSelectionMenu.js | 571 +++++++++--------- src/components/playback/playersettingsmenu.js | 502 +++++++-------- src/components/playback/playmethodhelper.js | 38 +- .../playback/remotecontrolautoplay.js | 68 +-- src/components/playback/volumeosd.js | 288 ++++----- 9 files changed, 1029 insertions(+), 1018 deletions(-) diff --git a/package.json b/package.json index 0dad2dc99d..25e3d91e38 100644 --- a/package.json +++ b/package.json @@ -89,23 +89,31 @@ "overrides": [ { "test": [ + "src/components/actionSheet/actionSheet.js", "src/components/autoFocuser.js", "src/components/cardbuilder/cardBuilder.js", - "src/scripts/fileDownloader.js", "src/components/images/imageLoader.js", + "src/components/indicators/indicators.js", "src/components/lazyLoader/lazyLoaderIntersectionObserver.js", + "src/components/playback/brightnessosd.js", "src/components/playback/mediasession.js", + "src/components/playback/nowplayinghelper.js", + "src/components/playback/playbackorientation.js", + "src/components/playback/playerSelectionMenu.js", + "src/components/playback/playersettingsmenu.js", + "src/components/playback/playmethodhelper.js", + "src/components/playback/remotecontrolautoplay.js", + "src/components/playback/volumeosd.js", + "src/components/playmenu.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", + "src/scripts/deleteHelper.js", "src/scripts/dfnshelper.js", "src/scripts/dom.js", + "src/scripts/fileDownloader.js", "src/scripts/filesystem.js", "src/scripts/imagehelper.js", "src/scripts/inputManager.js", - "src/scripts/deleteHelper.js", - "src/components/actionSheet/actionSheet.js", - "src/components/playmenu.js", - "src/components/indicators/indicators.js", "src/scripts/keyboardNavigation.js", "src/scripts/settings/appSettings.js", "src/scripts/settings/userSettings.js", diff --git a/src/components/playback/brightnessosd.js b/src/components/playback/brightnessosd.js index 5ed2b8f81f..3a0e270257 100644 --- a/src/components/playback/brightnessosd.js +++ b/src/components/playback/brightnessosd.js @@ -1,171 +1,173 @@ -define(['events', 'playbackManager', 'dom', 'browser', 'css!./iconosd', 'material-icons'], function (events, playbackManager, dom, browser) { - 'use strict'; +import events from 'events'; +import playbackManager from 'playbackManager'; +import dom from 'dom'; +import browser from 'browser'; +import 'css!./iconosd'; +import 'material-icons'; - var currentPlayer; - var osdElement; - var iconElement; - var progressElement; +var currentPlayer; +var osdElement; +var iconElement; +var progressElement; - var enableAnimation; +var enableAnimation; - function getOsdElementHtml() { - var html = ''; +function getOsdElementHtml() { + var html = ''; - html += ''; + html += ''; - html += '
    '; + html += '
    '; - return html; + return html; +} + +function ensureOsdElement() { + + var elem = osdElement; + if (!elem) { + + enableAnimation = browser.supportsCssAnimation(); + + elem = document.createElement('div'); + elem.classList.add('hide'); + elem.classList.add('iconOsd'); + elem.classList.add('iconOsd-hidden'); + elem.classList.add('brightnessOsd'); + elem.innerHTML = getOsdElementHtml(); + + iconElement = elem.querySelector('.material-icons'); + progressElement = elem.querySelector('.iconOsdProgressInner'); + + document.body.appendChild(elem); + osdElement = elem; } +} - function ensureOsdElement() { +function onHideComplete() { + this.classList.add('hide'); +} - var elem = osdElement; - if (!elem) { +var hideTimeout; +function showOsd() { - enableAnimation = browser.supportsCssAnimation(); + clearHideTimeout(); - elem = document.createElement('div'); - elem.classList.add('hide'); - elem.classList.add('iconOsd'); - elem.classList.add('iconOsd-hidden'); - elem.classList.add('brightnessOsd'); - elem.innerHTML = getOsdElementHtml(); + var elem = osdElement; - iconElement = elem.querySelector('.material-icons'); - progressElement = elem.querySelector('.iconOsdProgressInner'); - - document.body.appendChild(elem); - osdElement = elem; - } - } - - function onHideComplete() { - this.classList.add('hide'); - } - - var hideTimeout; - function showOsd() { - - clearHideTimeout(); - - var elem = osdElement; - - dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: true - }); - - elem.classList.remove('hide'); - - // trigger reflow - void elem.offsetWidth; - - requestAnimationFrame(function () { - elem.classList.remove('iconOsd-hidden'); - - hideTimeout = setTimeout(hideOsd, 3000); - }); - } - - function clearHideTimeout() { - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - } - - function hideOsd() { - - clearHideTimeout(); - - var elem = osdElement; - if (elem) { - - if (enableAnimation) { - // trigger reflow - void elem.offsetWidth; - - requestAnimationFrame(function () { - elem.classList.add('iconOsd-hidden'); - - dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: true - }); - }); - } else { - onHideComplete.call(elem); - } - } - } - - function setIcon(iconElement, icon) { - iconElement.classList.remove('brightness_high'); - iconElement.classList.remove('brightness_medium'); - iconElement.classList.remove('brightness_low'); - iconElement.classList.add(icon); - } - - function updateElementsFromPlayer(brightness) { - - if (iconElement) { - if (brightness >= 80) { - setIcon(iconElement, 'brightness_high'); - } else if (brightness >= 20) { - setIcon(iconElement, 'brightness_medium'); - } else { - setIcon(iconElement, 'brightness_low'); - } - } - if (progressElement) { - progressElement.style.width = (brightness || 0) + '%'; - } - } - - function releaseCurrentPlayer() { - - var player = currentPlayer; - - if (player) { - events.off(player, 'brightnesschange', onBrightnessChanged); - events.off(player, 'playbackstop', hideOsd); - currentPlayer = null; - } - } - - function onBrightnessChanged(e) { - - var player = this; - - ensureOsdElement(); - - updateElementsFromPlayer(playbackManager.getBrightness(player)); - - showOsd(); - } - - function bindToPlayer(player) { - - if (player === currentPlayer) { - return; - } - - releaseCurrentPlayer(); - - currentPlayer = player; - - if (!player) { - return; - } - - hideOsd(); - events.on(player, 'brightnesschange', onBrightnessChanged); - events.on(player, 'playbackstop', hideOsd); - } - - events.on(playbackManager, 'playerchange', function () { - bindToPlayer(playbackManager.getCurrentPlayer()); + dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true }); - bindToPlayer(playbackManager.getCurrentPlayer()); + elem.classList.remove('hide'); + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.remove('iconOsd-hidden'); + + hideTimeout = setTimeout(hideOsd, 3000); + }); +} + +function clearHideTimeout() { + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; + } +} + +function hideOsd() { + + clearHideTimeout(); + + var elem = osdElement; + if (elem) { + + if (enableAnimation) { + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.add('iconOsd-hidden'); + + dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true + }); + }); + } else { + onHideComplete.call(elem); + } + } +} + +function setIcon(iconElement, icon) { + iconElement.classList.remove('brightness_high'); + iconElement.classList.remove('brightness_medium'); + iconElement.classList.remove('brightness_low'); + iconElement.classList.add(icon); +} + +function updateElementsFromPlayer(brightness) { + + if (iconElement) { + if (brightness >= 80) { + setIcon(iconElement, 'brightness_high'); + } else if (brightness >= 20) { + setIcon(iconElement, 'brightness_medium'); + } else { + setIcon(iconElement, 'brightness_low'); + } + } + if (progressElement) { + progressElement.style.width = (brightness || 0) + '%'; + } +} + +function releaseCurrentPlayer() { + + var player = currentPlayer; + + if (player) { + events.off(player, 'brightnesschange', onBrightnessChanged); + events.off(player, 'playbackstop', hideOsd); + currentPlayer = null; + } +} + +function onBrightnessChanged(e) { + + var player = this; + + ensureOsdElement(); + + updateElementsFromPlayer(playbackManager.getBrightness(player)); + + showOsd(); +} + +function bindToPlayer(player) { + + if (player === currentPlayer) { + return; + } + + releaseCurrentPlayer(); + + currentPlayer = player; + + if (!player) { + return; + } + + hideOsd(); + events.on(player, 'brightnesschange', onBrightnessChanged); + events.on(player, 'playbackstop', hideOsd); +} + +events.on(playbackManager, 'playerchange', function () { + bindToPlayer(playbackManager.getCurrentPlayer()); }); + +bindToPlayer(playbackManager.getCurrentPlayer()); diff --git a/src/components/playback/nowplayinghelper.js b/src/components/playback/nowplayinghelper.js index 9bba23c294..310edc03c3 100644 --- a/src/components/playback/nowplayinghelper.js +++ b/src/components/playback/nowplayinghelper.js @@ -1,86 +1,82 @@ -define([], function () { - 'use strict'; +export function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) { - function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) { + var topItem = nowPlayingItem; + var bottomItem = null; + var topText = nowPlayingItem.Name; - var topItem = nowPlayingItem; - var bottomItem = null; - var topText = nowPlayingItem.Name; - - if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType === 'Audio') { - topItem = { - Id: nowPlayingItem.AlbumId, - Name: nowPlayingItem.Album, - Type: 'MusicAlbum', - IsFolder: true - }; - } - - if (nowPlayingItem.MediaType === 'Video') { - if (nowPlayingItem.IndexNumber != null) { - topText = nowPlayingItem.IndexNumber + ' - ' + topText; - } - if (nowPlayingItem.ParentIndexNumber != null) { - topText = nowPlayingItem.ParentIndexNumber + '.' + topText; - } - } - - var bottomText = ''; - - if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) { - - bottomItem = { - Id: nowPlayingItem.ArtistItems[0].Id, - Name: nowPlayingItem.ArtistItems[0].Name, - Type: 'MusicArtist', - IsFolder: true - }; - - bottomText = nowPlayingItem.ArtistItems.map(function (a) { - return a.Name; - }).join(', '); - - } else if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) { - - bottomText = nowPlayingItem.Artists.join(', '); - } else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) { - bottomText = topText; - topText = nowPlayingItem.SeriesName || nowPlayingItem.Album; - - bottomItem = topItem; - - if (nowPlayingItem.SeriesId) { - topItem = { - Id: nowPlayingItem.SeriesId, - Name: nowPlayingItem.SeriesName, - Type: 'Series', - IsFolder: true - }; - } else { - topItem = null; - } - } else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) { - bottomText = nowPlayingItem.ProductionYear; - } - - var list = []; - - list.push({ - text: topText, - item: topItem - }); - - if (bottomText) { - list.push({ - text: bottomText, - item: bottomItem - }); - } - - return list; + if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType === 'Audio') { + topItem = { + Id: nowPlayingItem.AlbumId, + Name: nowPlayingItem.Album, + Type: 'MusicAlbum', + IsFolder: true + }; } - return { - getNowPlayingNames: getNowPlayingNames - }; -}); + if (nowPlayingItem.MediaType === 'Video') { + if (nowPlayingItem.IndexNumber != null) { + topText = nowPlayingItem.IndexNumber + ' - ' + topText; + } + if (nowPlayingItem.ParentIndexNumber != null) { + topText = nowPlayingItem.ParentIndexNumber + '.' + topText; + } + } + + var bottomText = ''; + + if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) { + + bottomItem = { + Id: nowPlayingItem.ArtistItems[0].Id, + Name: nowPlayingItem.ArtistItems[0].Name, + Type: 'MusicArtist', + IsFolder: true + }; + + bottomText = nowPlayingItem.ArtistItems.map(function (a) { + return a.Name; + }).join(', '); + + } else if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) { + + bottomText = nowPlayingItem.Artists.join(', '); + } else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) { + bottomText = topText; + topText = nowPlayingItem.SeriesName || nowPlayingItem.Album; + + bottomItem = topItem; + + if (nowPlayingItem.SeriesId) { + topItem = { + Id: nowPlayingItem.SeriesId, + Name: nowPlayingItem.SeriesName, + Type: 'Series', + IsFolder: true + }; + } else { + topItem = null; + } + } else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) { + bottomText = nowPlayingItem.ProductionYear; + } + + var list = []; + + list.push({ + text: topText, + item: topItem + }); + + if (bottomText) { + list.push({ + text: bottomText, + item: bottomItem + }); + } + + return list; +} + +export default { + getNowPlayingNames: getNowPlayingNames +}; diff --git a/src/components/playback/playbackorientation.js b/src/components/playback/playbackorientation.js index 654ac29848..2078c6f6a8 100644 --- a/src/components/playback/playbackorientation.js +++ b/src/components/playback/playbackorientation.js @@ -1,57 +1,57 @@ -define(['playbackManager', 'layoutManager', 'events'], function (playbackManager, layoutManager, events) { - 'use strict'; +import playbackManager from 'playbackManager'; +import layoutManager from 'layoutManager'; +import events from 'events'; - var orientationLocked; +var orientationLocked; - function onOrientationChangeSuccess() { - orientationLocked = true; - } +function onOrientationChangeSuccess() { + orientationLocked = true; +} - function onOrientationChangeError(err) { - orientationLocked = false; - console.error('error locking orientation: ' + err); - } +function onOrientationChangeError(err) { + orientationLocked = false; + console.error('error locking orientation: ' + err); +} - events.on(playbackManager, 'playbackstart', function (e, player, state) { +events.on(playbackManager, 'playbackstart', function (e, player, state) { - var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player); + var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player); - if (isLocalVideo && layoutManager.mobile) { - /* eslint-disable-next-line compat/compat */ - var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || (screen.orientation && screen.orientation.lock); + if (isLocalVideo && layoutManager.mobile) { + /* eslint-disable-next-line compat/compat */ + var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || (screen.orientation && screen.orientation.lock); - if (lockOrientation) { + if (lockOrientation) { - try { - var promise = lockOrientation('landscape'); - if (promise.then) { - promise.then(onOrientationChangeSuccess, onOrientationChangeError); - } else { - // returns a boolean - orientationLocked = promise; - } - } catch (err) { - onOrientationChangeError(err); + try { + var promise = lockOrientation('landscape'); + if (promise.then) { + promise.then(onOrientationChangeSuccess, onOrientationChangeError); + } else { + // returns a boolean + orientationLocked = promise; } + } catch (err) { + onOrientationChangeError(err); } } - }); - - events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) { - - if (orientationLocked && !playbackStopInfo.nextMediaType) { - - /* eslint-disable-next-line compat/compat */ - var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || (screen.orientation && screen.orientation.unlock); - - if (unlockOrientation) { - try { - unlockOrientation(); - } catch (err) { - console.error('error unlocking orientation: ' + err); - } - orientationLocked = false; - } - } - }); + } +}); + +events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) { + + if (orientationLocked && !playbackStopInfo.nextMediaType) { + + /* eslint-disable-next-line compat/compat */ + var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || (screen.orientation && screen.orientation.unlock); + + if (unlockOrientation) { + try { + unlockOrientation(); + } catch (err) { + console.error('error unlocking orientation: ' + err); + } + orientationLocked = false; + } + } }); diff --git a/src/components/playback/playerSelectionMenu.js b/src/components/playback/playerSelectionMenu.js index 329cc11f92..eb0ae08497 100644 --- a/src/components/playback/playerSelectionMenu.js +++ b/src/components/playback/playerSelectionMenu.js @@ -1,320 +1,325 @@ -define(['appSettings', 'events', 'browser', 'loading', 'playbackManager', 'appRouter', 'globalize', 'apphost'], function (appSettings, events, browser, loading, playbackManager, appRouter, globalize, appHost) { - 'use strict'; +import appSettings from 'appSettings'; +import events from 'events'; +import browser from 'browser'; +import loading from 'loading'; +import playbackManager from 'playbackManager'; +import appRouter from 'appRouter'; +import globalize from 'globalize'; +import appHost from 'apphost'; - function mirrorItem(info, player) { +function mirrorItem(info, player) { - var item = info.item; + var item = info.item; - playbackManager.displayContent({ + playbackManager.displayContent({ - ItemName: item.Name, - ItemId: item.Id, - ItemType: item.Type, - Context: info.context - }, player); - } + ItemName: item.Name, + ItemId: item.Id, + ItemType: item.Type, + Context: info.context + }, player); +} - function mirrorIfEnabled(info) { +function mirrorIfEnabled(info) { - if (info && playbackManager.enableDisplayMirroring()) { + if (info && playbackManager.enableDisplayMirroring()) { - var getPlayerInfo = playbackManager.getPlayerInfo(); + var getPlayerInfo = playbackManager.getPlayerInfo(); - if (getPlayerInfo) { - if (!getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { - mirrorItem(info, playbackManager.getCurrentPlayer()); - } + if (getPlayerInfo) { + if (!getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { + mirrorItem(info, playbackManager.getCurrentPlayer()); } } } +} - function emptyCallback() { - // avoid console logs about uncaught promises +function emptyCallback() { + // avoid console logs about uncaught promises +} + +function getTargetSecondaryText(target) { + + if (target.user) { + + return target.user.Name; } - function getTargetSecondaryText(target) { + return null; +} - if (target.user) { +function getIcon(target) { - return target.user.Name; - } + var deviceType = target.deviceType; - return null; - } - - function getIcon(target) { - - var deviceType = target.deviceType; - - if (!deviceType && target.isLocalPlayer) { - if (browser.tv) { - deviceType = 'tv'; - } else if (browser.mobile) { - deviceType = 'smartphone'; - } else { - deviceType = 'desktop'; - } - } - - if (!deviceType) { + if (!deviceType && target.isLocalPlayer) { + if (browser.tv) { deviceType = 'tv'; - } - - switch (deviceType) { - - case 'smartphone': - return 'smartphone'; - case 'tablet': - return 'tablet'; - case 'tv': - return 'tv'; - case 'cast': - return 'cast'; - case 'desktop': - return 'computer'; - default: - return 'tv'; - } - } - - function showPlayerSelection(button) { - - var currentPlayerInfo = playbackManager.getPlayerInfo(); - - if (currentPlayerInfo) { - if (!currentPlayerInfo.isLocalPlayer) { - showActivePlayerMenu(currentPlayerInfo); - return; - } - } - - var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null; - - loading.show(); - - playbackManager.getTargets().then(function (targets) { - - var menuItems = targets.map(function (t) { - - var name = t.name; - - if (t.appName && t.appName !== t.name) { - name += ' - ' + t.appName; - } - - return { - name: name, - id: t.id, - selected: currentPlayerId === t.id, - secondaryText: getTargetSecondaryText(t), - icon: getIcon(t) - }; - - }); - - require(['actionsheet'], function (actionsheet) { - - loading.hide(); - - var menuOptions = { - title: globalize.translate('HeaderPlayOn'), - items: menuItems, - positionTo: button, - - resolveOnClick: true, - border: true - }; - - // Unfortunately we can't allow the url to change or chromecast will throw a security error - // Might be able to solve this in the future by moving the dialogs to hashbangs - if (!(!browser.chrome || appHost.supports('castmenuhashchange'))) { - menuOptions.enableHistory = false; - } - - actionsheet.show(menuOptions).then(function (id) { - - var target = targets.filter(function (t) { - return t.id === id; - })[0]; - - playbackManager.trySetActivePlayer(target.playerName, target); - - mirrorIfEnabled(); - - }, emptyCallback); - }); - }); - } - - function showActivePlayerMenu(playerInfo) { - - require(['dialogHelper', 'dialog', 'emby-checkbox', 'emby-button'], function (dialogHelper) { - showActivePlayerMenuInternal(dialogHelper, playerInfo); - }); - } - - function disconnectFromPlayer(currentDeviceName) { - - if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) { - - require(['dialog'], function (dialog) { - - var menuItems = []; - - menuItems.push({ - name: globalize.translate('Yes'), - id: 'yes' - }); - menuItems.push({ - name: globalize.translate('No'), - id: 'no' - }); - - dialog({ - buttons: menuItems, - //positionTo: positionTo, - text: globalize.translate('ConfirmEndPlayerSession', currentDeviceName) - - }).then(function (id) { - switch (id) { - - case 'yes': - playbackManager.getCurrentPlayer().endSession(); - playbackManager.setDefaultPlayerActive(); - break; - case 'no': - playbackManager.setDefaultPlayerActive(); - break; - default: - break; - } - }); - - }); - + } else if (browser.mobile) { + deviceType = 'smartphone'; } else { - - playbackManager.setDefaultPlayerActive(); + deviceType = 'desktop'; } } - function showActivePlayerMenuInternal(dialogHelper, playerInfo) { - - var html = ''; - - var dialogOptions = { - removeOnClose: true - }; - - dialogOptions.modal = false; - dialogOptions.entryAnimationDuration = 160; - dialogOptions.exitAnimationDuration = 160; - dialogOptions.autoFocus = false; - - var dlg = dialogHelper.createDialog(dialogOptions); - - dlg.classList.add('promptDialog'); - - var currentDeviceName = (playerInfo.deviceName || playerInfo.name); - - html += '
    '; - html += '

    '; - html += currentDeviceName; - html += '

    '; - - html += '
    '; - - if (playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { - - html += ''; - } - - html += '
    '; - - html += '
    '; - - html += ''; - html += ''; - html += ''; - html += '
    '; - - html += '
    '; - dlg.innerHTML = html; - - var chkMirror = dlg.querySelector('.chkMirror'); - - if (chkMirror) { - chkMirror.addEventListener('change', onMirrorChange); - } - - var destination = ''; - - var btnRemoteControl = dlg.querySelector('.btnRemoteControl'); - if (btnRemoteControl) { - btnRemoteControl.addEventListener('click', function () { - destination = 'nowplaying'; - dialogHelper.close(dlg); - }); - } - - dlg.querySelector('.btnDisconnect').addEventListener('click', function () { - destination = 'disconnectFromPlayer'; - dialogHelper.close(dlg); - }); - - dlg.querySelector('.btnCancel').addEventListener('click', function () { - dialogHelper.close(dlg); - }); - - dialogHelper.open(dlg).then(function () { - if (destination === 'nowplaying') { - appRouter.showNowPlaying(); - } else if (destination === 'disconnectFromPlayer') { - disconnectFromPlayer(currentDeviceName); - } - }, emptyCallback); + if (!deviceType) { + deviceType = 'tv'; } - function onMirrorChange() { - playbackManager.enableDisplayMirroring(this.checked); + switch (deviceType) { + + case 'smartphone': + return 'smartphone'; + case 'tablet': + return 'tablet'; + case 'tv': + return 'tv'; + case 'cast': + return 'cast'; + case 'desktop': + return 'computer'; + default: + return 'tv'; } +} - document.addEventListener('viewshow', function (e) { +export function showPlayerSelection(button) { - var state = e.detail.state || {}; - var item = state.item; + var currentPlayerInfo = playbackManager.getPlayerInfo(); - if (item && item.ServerId) { - mirrorIfEnabled({ - item: item - }); + if (currentPlayerInfo) { + if (!currentPlayerInfo.isLocalPlayer) { + showActivePlayerMenu(currentPlayerInfo); return; } - }); + } - events.on(appSettings, 'change', function (e, name) { - if (name === 'displaymirror') { - mirrorIfEnabled(); - } - }); + var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null; - events.on(playbackManager, 'pairing', function (e) { - loading.show(); - }); + loading.show(); - events.on(playbackManager, 'paired', function (e) { - loading.hide(); - }); + playbackManager.getTargets().then(function (targets) { - events.on(playbackManager, 'pairerror', function (e) { - loading.hide(); - }); + var menuItems = targets.map(function (t) { - return { - show: showPlayerSelection + var name = t.name; + + if (t.appName && t.appName !== t.name) { + name += ' - ' + t.appName; + } + + return { + name: name, + id: t.id, + selected: currentPlayerId === t.id, + secondaryText: getTargetSecondaryText(t), + icon: getIcon(t) + }; + + }); + + require(['actionsheet'], function (actionsheet) { + + loading.hide(); + + var menuOptions = { + title: globalize.translate('HeaderPlayOn'), + items: menuItems, + positionTo: button, + + resolveOnClick: true, + border: true + }; + + // Unfortunately we can't allow the url to change or chromecast will throw a security error + // Might be able to solve this in the future by moving the dialogs to hashbangs + if (!(!browser.chrome || appHost.supports('castmenuhashchange'))) { + menuOptions.enableHistory = false; + } + + actionsheet.show(menuOptions).then(function (id) { + + var target = targets.filter(function (t) { + return t.id === id; + })[0]; + + playbackManager.trySetActivePlayer(target.playerName, target); + + mirrorIfEnabled(); + + }, emptyCallback); + }); + }); +} + +function showActivePlayerMenu(playerInfo) { + + require(['dialogHelper', 'dialog', 'emby-checkbox', 'emby-button'], function (dialogHelper) { + showActivePlayerMenuInternal(dialogHelper, playerInfo); + }); +} + +function disconnectFromPlayer(currentDeviceName) { + + if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) { + + require(['dialog'], function (dialog) { + + var menuItems = []; + + menuItems.push({ + name: globalize.translate('Yes'), + id: 'yes' + }); + menuItems.push({ + name: globalize.translate('No'), + id: 'no' + }); + + dialog({ + buttons: menuItems, + //positionTo: positionTo, + text: globalize.translate('ConfirmEndPlayerSession', currentDeviceName) + + }).then(function (id) { + switch (id) { + + case 'yes': + playbackManager.getCurrentPlayer().endSession(); + playbackManager.setDefaultPlayerActive(); + break; + case 'no': + playbackManager.setDefaultPlayerActive(); + break; + default: + break; + } + }); + + }); + + } else { + + playbackManager.setDefaultPlayerActive(); + } +} + +function showActivePlayerMenuInternal(dialogHelper, playerInfo) { + + var html = ''; + + var dialogOptions = { + removeOnClose: true }; + + dialogOptions.modal = false; + dialogOptions.entryAnimationDuration = 160; + dialogOptions.exitAnimationDuration = 160; + dialogOptions.autoFocus = false; + + var dlg = dialogHelper.createDialog(dialogOptions); + + dlg.classList.add('promptDialog'); + + var currentDeviceName = (playerInfo.deviceName || playerInfo.name); + + html += '
    '; + html += '

    '; + html += currentDeviceName; + html += '

    '; + + html += '
    '; + + if (playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { + + html += ''; + } + + html += '
    '; + + html += '
    '; + + html += ''; + html += ''; + html += ''; + html += '
    '; + + html += '
    '; + dlg.innerHTML = html; + + var chkMirror = dlg.querySelector('.chkMirror'); + + if (chkMirror) { + chkMirror.addEventListener('change', onMirrorChange); + } + + var destination = ''; + + var btnRemoteControl = dlg.querySelector('.btnRemoteControl'); + if (btnRemoteControl) { + btnRemoteControl.addEventListener('click', function () { + destination = 'nowplaying'; + dialogHelper.close(dlg); + }); + } + + dlg.querySelector('.btnDisconnect').addEventListener('click', function () { + destination = 'disconnectFromPlayer'; + dialogHelper.close(dlg); + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + dialogHelper.close(dlg); + }); + + dialogHelper.open(dlg).then(function () { + if (destination === 'nowplaying') { + appRouter.showNowPlaying(); + } else if (destination === 'disconnectFromPlayer') { + disconnectFromPlayer(currentDeviceName); + } + }, emptyCallback); +} + +function onMirrorChange() { + playbackManager.enableDisplayMirroring(this.checked); +} + +document.addEventListener('viewshow', function (e) { + + var state = e.detail.state || {}; + var item = state.item; + + if (item && item.ServerId) { + mirrorIfEnabled({ + item: item + }); + return; + } }); + +events.on(appSettings, 'change', function (e, name) { + if (name === 'displaymirror') { + mirrorIfEnabled(); + } +}); + +events.on(playbackManager, 'pairing', function (e) { + loading.show(); +}); + +events.on(playbackManager, 'paired', function (e) { + loading.hide(); +}); + +events.on(playbackManager, 'pairerror', function (e) { + loading.hide(); +}); + +export default { + show: showPlayerSelection +}; diff --git a/src/components/playback/playersettingsmenu.js b/src/components/playback/playersettingsmenu.js index dd67b667e6..2544590b34 100644 --- a/src/components/playback/playersettingsmenu.js +++ b/src/components/playback/playersettingsmenu.js @@ -1,270 +1,274 @@ -define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'globalize', 'appSettings', 'qualityoptions'], function (connectionManager, actionsheet, datetime, playbackManager, globalize, appSettings, qualityoptions) { - 'use strict'; +import connectionManager from 'connectionManager'; +import actionsheet from 'actionsheet'; +import datetime from 'datetime'; +import playbackManager from 'playbackManager'; +import globalize from 'globalize'; +import appSettings from 'appSettings'; +import qualityoptions from 'qualityoptions'; - function showQualityMenu(player, btn) { +function showQualityMenu(player, btn) { - var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { - return stream.Type === 'Video'; - })[0]; - var videoWidth = videoStream ? videoStream.Width : null; - var videoHeight = videoStream ? videoStream.Height : null; + var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { + return stream.Type === 'Video'; + })[0]; + var videoWidth = videoStream ? videoStream.Width : null; + var videoHeight = videoStream ? videoStream.Height : null; - var options = qualityoptions.getVideoQualityOptions({ - currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), - isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), - videoWidth: videoWidth, - videoHeight: videoHeight, - enableAuto: true - }); + var options = qualityoptions.getVideoQualityOptions({ + currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), + isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), + videoWidth: videoWidth, + videoHeight: videoHeight, + enableAuto: true + }); - var menuItems = options.map(function (o) { - var opt = { - name: o.name, - id: o.bitrate, - asideText: o.secondaryText - }; + var menuItems = options.map(function (o) { + var opt = { + name: o.name, + id: o.bitrate, + asideText: o.secondaryText + }; - if (o.selected) { - opt.selected = true; - } + if (o.selected) { + opt.selected = true; + } - return opt; - }); + return opt; + }); - var selectedId = options.filter(function (o) { - return o.selected; - }); + var selectedId = options.filter(function (o) { + return o.selected; + }); - selectedId = selectedId.length ? selectedId[0].bitrate : null; + selectedId = selectedId.length ? selectedId[0].bitrate : null; - return actionsheet.show({ - items: menuItems, - positionTo: btn - }).then(function (id) { - var bitrate = parseInt(id); - if (bitrate !== selectedId) { - playbackManager.setMaxStreamingBitrate({ - enableAutomaticBitrateDetection: bitrate ? false : true, - maxBitrate: bitrate - }, player); - } - }); + return actionsheet.show({ + items: menuItems, + positionTo: btn + }).then(function (id) { + var bitrate = parseInt(id); + if (bitrate !== selectedId) { + playbackManager.setMaxStreamingBitrate({ + enableAutomaticBitrateDetection: bitrate ? false : true, + maxBitrate: bitrate + }, player); + } + }); +} + +function showRepeatModeMenu(player, btn) { + var menuItems = []; + var currentValue = playbackManager.getRepeatMode(player); + + menuItems.push({ + name: globalize.translate('RepeatAll'), + id: 'RepeatAll', + selected: currentValue === 'RepeatAll' + }); + + menuItems.push({ + name: globalize.translate('RepeatOne'), + id: 'RepeatOne', + selected: currentValue === 'RepeatOne' + }); + + menuItems.push({ + name: globalize.translate('None'), + id: 'RepeatNone', + selected: currentValue === 'RepeatNone' + }); + + return actionsheet.show({ + items: menuItems, + positionTo: btn + }).then(function (mode) { + if (mode) { + playbackManager.setRepeatMode(mode, player); + } + }); +} + +function getQualitySecondaryText(player) { + var state = playbackManager.getPlayerState(player); + var isAutoEnabled = playbackManager.enableAutomaticBitrateDetection(player); + var currentMaxBitrate = playbackManager.getMaxStreamingBitrate(player); + + var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { + return stream.Type === 'Video'; + })[0]; + + var videoWidth = videoStream ? videoStream.Width : null; + var videoHeight = videoStream ? videoStream.Height : null; + + var options = qualityoptions.getVideoQualityOptions({ + currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), + isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), + videoWidth: videoWidth, + videoHeight: videoHeight, + enableAuto: true + }); + + var menuItems = options.map(function (o) { + var opt = { + name: o.name, + id: o.bitrate, + asideText: o.secondaryText + }; + + if (o.selected) { + opt.selected = true; + } + + return opt; + }); + + var selectedOption = options.filter(function (o) { + return o.selected; + }); + + if (!selectedOption.length) { + return null; } - function showRepeatModeMenu(player, btn) { - var menuItems = []; - var currentValue = playbackManager.getRepeatMode(player); + selectedOption = selectedOption[0]; + var text = selectedOption.name; - menuItems.push({ - name: globalize.translate('RepeatAll'), - id: 'RepeatAll', - selected: currentValue === 'RepeatAll' - }); - - menuItems.push({ - name: globalize.translate('RepeatOne'), - id: 'RepeatOne', - selected: currentValue === 'RepeatOne' - }); - - menuItems.push({ - name: globalize.translate('None'), - id: 'RepeatNone', - selected: currentValue === 'RepeatNone' - }); - - return actionsheet.show({ - items: menuItems, - positionTo: btn - }).then(function (mode) { - if (mode) { - playbackManager.setRepeatMode(mode, player); - } - }); + if (selectedOption.autoText) { + if (state.PlayState && state.PlayState.PlayMethod !== 'Transcode') { + text += ' - Direct'; + } else { + text += ' ' + selectedOption.autoText; + } } - function getQualitySecondaryText(player) { - var state = playbackManager.getPlayerState(player); - var isAutoEnabled = playbackManager.enableAutomaticBitrateDetection(player); - var currentMaxBitrate = playbackManager.getMaxStreamingBitrate(player); + return text; +} - var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { - return stream.Type === 'Video'; - })[0]; +function showAspectRatioMenu(player, btn) { + // each has a name and id + var currentId = playbackManager.getAspectRatio(player); + var menuItems = playbackManager.getSupportedAspectRatios(player).map(function (i) { + return { + id: i.id, + name: i.name, + selected: i.id === currentId + }; + }); - var videoWidth = videoStream ? videoStream.Width : null; - var videoHeight = videoStream ? videoStream.Height : null; - - var options = qualityoptions.getVideoQualityOptions({ - currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), - isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), - videoWidth: videoWidth, - videoHeight: videoHeight, - enableAuto: true - }); - - var menuItems = options.map(function (o) { - var opt = { - name: o.name, - id: o.bitrate, - asideText: o.secondaryText - }; - - if (o.selected) { - opt.selected = true; - } - - return opt; - }); - - var selectedOption = options.filter(function (o) { - return o.selected; - }); - - if (!selectedOption.length) { - return null; - } - - selectedOption = selectedOption[0]; - var text = selectedOption.name; - - if (selectedOption.autoText) { - if (state.PlayState && state.PlayState.PlayMethod !== 'Transcode') { - text += ' - Direct'; - } else { - text += ' ' + selectedOption.autoText; - } - } - - return text; - } - - function showAspectRatioMenu(player, btn) { - // each has a name and id - var currentId = playbackManager.getAspectRatio(player); - var menuItems = playbackManager.getSupportedAspectRatios(player).map(function (i) { - return { - id: i.id, - name: i.name, - selected: i.id === currentId - }; - }); - - return actionsheet.show({ - items: menuItems, - positionTo: btn - }).then(function (id) { - if (id) { - playbackManager.setAspectRatio(id, player); - return Promise.resolve(); - } - - return Promise.reject(); - }); - } - - function showWithUser(options, player, user) { - var supportedCommands = playbackManager.getSupportedCommands(player); - var mediaType = options.mediaType; - - var menuItems = []; - if (supportedCommands.indexOf('SetAspectRatio') !== -1) { - var currentAspectRatioId = playbackManager.getAspectRatio(player); - var currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function (i) { - return i.id === currentAspectRatioId; - })[0]; - - menuItems.push({ - name: globalize.translate('AspectRatio'), - id: 'aspectratio', - asideText: currentAspectRatio ? currentAspectRatio.name : null - }); - } - - if (user && user.Policy.EnableVideoPlaybackTranscoding) { - var secondaryQualityText = getQualitySecondaryText(player); - - menuItems.push({ - name: globalize.translate('Quality'), - id: 'quality', - asideText: secondaryQualityText - }); - } - - var repeatMode = playbackManager.getRepeatMode(player); - - if (supportedCommands.indexOf('SetRepeatMode') !== -1 && playbackManager.currentMediaSource(player).RunTimeTicks) { - menuItems.push({ - name: globalize.translate('RepeatMode'), - id: 'repeatmode', - asideText: repeatMode === 'RepeatNone' ? globalize.translate('None') : globalize.translate('' + repeatMode) - }); - } - - if (options.suboffset) { - menuItems.push({ - name: globalize.translate('SubtitleOffset'), - id: 'suboffset', - asideText: null - }); - } - - if (options.stats) { - menuItems.push({ - name: globalize.translate('PlaybackData'), - id: 'stats', - asideText: null - }); - } - - return actionsheet.show({ - items: menuItems, - positionTo: options.positionTo - }).then(function (id) { - return handleSelectedOption(id, options, player); - }); - } - - function show(options) { - var player = options.player; - var currentItem = playbackManager.currentItem(player); - - if (!currentItem || !currentItem.ServerId) { - return showWithUser(options, player, null); - } - - var apiClient = connectionManager.getApiClient(currentItem.ServerId); - return apiClient.getCurrentUser().then(function (user) { - return showWithUser(options, player, user); - }); - } - - function handleSelectedOption(id, options, player) { - switch (id) { - case 'quality': - return showQualityMenu(player, options.positionTo); - case 'aspectratio': - return showAspectRatioMenu(player, options.positionTo); - case 'repeatmode': - return showRepeatModeMenu(player, options.positionTo); - case 'stats': - if (options.onOption) { - options.onOption('stats'); - } - return Promise.resolve(); - case 'suboffset': - if (options.onOption) { - options.onOption('suboffset'); - } - return Promise.resolve(); - default: - break; + return actionsheet.show({ + items: menuItems, + positionTo: btn + }).then(function (id) { + if (id) { + playbackManager.setAspectRatio(id, player); + return Promise.resolve(); } return Promise.reject(); + }); +} + +function showWithUser(options, player, user) { + var supportedCommands = playbackManager.getSupportedCommands(player); + var mediaType = options.mediaType; + + var menuItems = []; + if (supportedCommands.indexOf('SetAspectRatio') !== -1) { + var currentAspectRatioId = playbackManager.getAspectRatio(player); + var currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function (i) { + return i.id === currentAspectRatioId; + })[0]; + + menuItems.push({ + name: globalize.translate('AspectRatio'), + id: 'aspectratio', + asideText: currentAspectRatio ? currentAspectRatio.name : null + }); } - return { - show: show - }; -}); + if (user && user.Policy.EnableVideoPlaybackTranscoding) { + var secondaryQualityText = getQualitySecondaryText(player); + + menuItems.push({ + name: globalize.translate('Quality'), + id: 'quality', + asideText: secondaryQualityText + }); + } + + var repeatMode = playbackManager.getRepeatMode(player); + + if (supportedCommands.indexOf('SetRepeatMode') !== -1 && playbackManager.currentMediaSource(player).RunTimeTicks) { + menuItems.push({ + name: globalize.translate('RepeatMode'), + id: 'repeatmode', + asideText: repeatMode === 'RepeatNone' ? globalize.translate('None') : globalize.translate('' + repeatMode) + }); + } + + if (options.suboffset) { + menuItems.push({ + name: globalize.translate('SubtitleOffset'), + id: 'suboffset', + asideText: null + }); + } + + if (options.stats) { + menuItems.push({ + name: globalize.translate('PlaybackData'), + id: 'stats', + asideText: null + }); + } + + return actionsheet.show({ + items: menuItems, + positionTo: options.positionTo + }).then(function (id) { + return handleSelectedOption(id, options, player); + }); +} + +export function show(options) { + var player = options.player; + var currentItem = playbackManager.currentItem(player); + + if (!currentItem || !currentItem.ServerId) { + return showWithUser(options, player, null); + } + + var apiClient = connectionManager.getApiClient(currentItem.ServerId); + return apiClient.getCurrentUser().then(function (user) { + return showWithUser(options, player, user); + }); +} + +function handleSelectedOption(id, options, player) { + switch (id) { + case 'quality': + return showQualityMenu(player, options.positionTo); + case 'aspectratio': + return showAspectRatioMenu(player, options.positionTo); + case 'repeatmode': + return showRepeatModeMenu(player, options.positionTo); + case 'stats': + if (options.onOption) { + options.onOption('stats'); + } + return Promise.resolve(); + case 'suboffset': + if (options.onOption) { + options.onOption('suboffset'); + } + return Promise.resolve(); + default: + break; + } + + return Promise.reject(); +} + +export default { + show: show +}; diff --git a/src/components/playback/playmethodhelper.js b/src/components/playback/playmethodhelper.js index 75af04035c..62793b9a33 100644 --- a/src/components/playback/playmethodhelper.js +++ b/src/components/playback/playmethodhelper.js @@ -1,24 +1,20 @@ -define([], function () { - 'use strict'; +export function getDisplayPlayMethod(session) { - function getDisplayPlayMethod(session) { - - if (!session.NowPlayingItem) { - return null; - } - - if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) { - return 'DirectStream'; - } else if (session.PlayState.PlayMethod === 'Transcode') { - return 'Transcode'; - } else if (session.PlayState.PlayMethod === 'DirectStream') { - return 'DirectPlay'; - } else if (session.PlayState.PlayMethod === 'DirectPlay') { - return 'DirectPlay'; - } + if (!session.NowPlayingItem) { + return null; } - return { - getDisplayPlayMethod: getDisplayPlayMethod - }; -}); + if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) { + return 'DirectStream'; + } else if (session.PlayState.PlayMethod === 'Transcode') { + return 'Transcode'; + } else if (session.PlayState.PlayMethod === 'DirectStream') { + return 'DirectPlay'; + } else if (session.PlayState.PlayMethod === 'DirectPlay') { + return 'DirectPlay'; + } +} + +export default { + getDisplayPlayMethod: getDisplayPlayMethod +}; diff --git a/src/components/playback/remotecontrolautoplay.js b/src/components/playback/remotecontrolautoplay.js index 90a872cc6e..b7da635cb7 100644 --- a/src/components/playback/remotecontrolautoplay.js +++ b/src/components/playback/remotecontrolautoplay.js @@ -1,47 +1,45 @@ -define(['events', 'playbackManager'], function (events, playbackManager) { - 'use strict'; +import events from 'events'; +import playbackManager from 'playbackManager'; - function transferPlayback(oldPlayer, newPlayer) { +function transferPlayback(oldPlayer, newPlayer) { - var state = playbackManager.getPlayerState(oldPlayer); + var state = playbackManager.getPlayerState(oldPlayer); - var item = state.NowPlayingItem; + var item = state.NowPlayingItem; - if (!item) { - return; - } - - var playState = state.PlayState || {}; - var resumePositionTicks = playState.PositionTicks || 0; - - playbackManager.stop(oldPlayer).then(function () { - - playbackManager.play({ - ids: [item.Id], - serverId: item.ServerId, - startPositionTicks: resumePositionTicks - - }, newPlayer); - }); + if (!item) { + return; } - events.on(playbackManager, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) { + var playState = state.PlayState || {}; + var resumePositionTicks = playState.PositionTicks || 0; - if (!oldPlayer || !newPlayer) { - return; - } + playbackManager.stop(oldPlayer).then(function () { - if (!oldPlayer.isLocalPlayer) { - console.debug('Skipping remote control autoplay because oldPlayer is not a local player'); - return; - } + playbackManager.play({ + ids: [item.Id], + serverId: item.ServerId, + startPositionTicks: resumePositionTicks - if (newPlayer.isLocalPlayer) { - console.debug('Skipping remote control autoplay because newPlayer is a local player'); - return; - } - - transferPlayback(oldPlayer, newPlayer); + }, newPlayer); }); +} +events.on(playbackManager, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) { + + if (!oldPlayer || !newPlayer) { + return; + } + + if (!oldPlayer.isLocalPlayer) { + console.debug('Skipping remote control autoplay because oldPlayer is not a local player'); + return; + } + + if (newPlayer.isLocalPlayer) { + console.debug('Skipping remote control autoplay because newPlayer is a local player'); + return; + } + + transferPlayback(oldPlayer, newPlayer); }); diff --git a/src/components/playback/volumeosd.js b/src/components/playback/volumeosd.js index 95a13d769d..c2f6761f53 100644 --- a/src/components/playback/volumeosd.js +++ b/src/components/playback/volumeosd.js @@ -1,159 +1,161 @@ -define(['events', 'playbackManager', 'dom', 'browser', 'css!./iconosd', 'material-icons'], function (events, playbackManager, dom, browser) { - 'use strict'; +import events from 'events'; +import playbackManager from 'playbackManager'; +import dom from 'dom'; +import browser from 'browser'; +import 'css!./iconosd'; +import 'material-icons'; - var currentPlayer; - var osdElement; - var iconElement; - var progressElement; +var currentPlayer; +var osdElement; +var iconElement; +var progressElement; - var enableAnimation; +var enableAnimation; - function getOsdElementHtml() { - var html = ''; +function getOsdElementHtml() { + var html = ''; - html += ''; + html += ''; - html += '
    '; + html += '
    '; - return html; + return html; +} + +function ensureOsdElement() { + + var elem = osdElement; + if (!elem) { + + enableAnimation = browser.supportsCssAnimation(); + + elem = document.createElement('div'); + elem.classList.add('hide'); + elem.classList.add('iconOsd'); + elem.classList.add('iconOsd-hidden'); + elem.classList.add('volumeOsd'); + elem.innerHTML = getOsdElementHtml(); + + iconElement = elem.querySelector('.material-icons'); + progressElement = elem.querySelector('.iconOsdProgressInner'); + + document.body.appendChild(elem); + osdElement = elem; } +} - function ensureOsdElement() { +function onHideComplete() { + this.classList.add('hide'); +} - var elem = osdElement; - if (!elem) { +var hideTimeout; +function showOsd() { - enableAnimation = browser.supportsCssAnimation(); + clearHideTimeout(); - elem = document.createElement('div'); - elem.classList.add('hide'); - elem.classList.add('iconOsd'); - elem.classList.add('iconOsd-hidden'); - elem.classList.add('volumeOsd'); - elem.innerHTML = getOsdElementHtml(); + var elem = osdElement; - iconElement = elem.querySelector('.material-icons'); - progressElement = elem.querySelector('.iconOsdProgressInner'); - - document.body.appendChild(elem); - osdElement = elem; - } - } - - function onHideComplete() { - this.classList.add('hide'); - } - - var hideTimeout; - function showOsd() { - - clearHideTimeout(); - - var elem = osdElement; - - dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: true - }); - - elem.classList.remove('hide'); - - // trigger reflow - void elem.offsetWidth; - - requestAnimationFrame(function () { - elem.classList.remove('iconOsd-hidden'); - - hideTimeout = setTimeout(hideOsd, 3000); - }); - } - - function clearHideTimeout() { - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - } - - function hideOsd() { - - clearHideTimeout(); - - var elem = osdElement; - if (elem) { - - if (enableAnimation) { - // trigger reflow - void elem.offsetWidth; - - requestAnimationFrame(function () { - elem.classList.add('iconOsd-hidden'); - - dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: true - }); - }); - } else { - onHideComplete.call(elem); - } - } - } - - function updatePlayerVolumeState(isMuted, volume) { - - if (iconElement) { - iconElement.classList.remove('volume_off', 'volume_up'); - iconElement.classList.add(isMuted ? 'volume_off' : 'volume_up'); - } - if (progressElement) { - progressElement.style.width = (volume || 0) + '%'; - } - } - - function releaseCurrentPlayer() { - - var player = currentPlayer; - - if (player) { - events.off(player, 'volumechange', onVolumeChanged); - events.off(player, 'playbackstop', hideOsd); - currentPlayer = null; - } - } - - function onVolumeChanged(e) { - - var player = this; - - ensureOsdElement(); - - updatePlayerVolumeState(player.isMuted(), player.getVolume()); - - showOsd(); - } - - function bindToPlayer(player) { - - if (player === currentPlayer) { - return; - } - - releaseCurrentPlayer(); - - currentPlayer = player; - - if (!player) { - return; - } - - hideOsd(); - events.on(player, 'volumechange', onVolumeChanged); - events.on(player, 'playbackstop', hideOsd); - } - - events.on(playbackManager, 'playerchange', function () { - bindToPlayer(playbackManager.getCurrentPlayer()); + dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true }); - bindToPlayer(playbackManager.getCurrentPlayer()); + elem.classList.remove('hide'); + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.remove('iconOsd-hidden'); + + hideTimeout = setTimeout(hideOsd, 3000); + }); +} + +function clearHideTimeout() { + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; + } +} + +function hideOsd() { + + clearHideTimeout(); + + var elem = osdElement; + if (elem) { + + if (enableAnimation) { + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.add('iconOsd-hidden'); + + dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true + }); + }); + } else { + onHideComplete.call(elem); + } + } +} + +function updatePlayerVolumeState(isMuted, volume) { + + if (iconElement) { + iconElement.classList.remove('volume_off', 'volume_up'); + iconElement.classList.add(isMuted ? 'volume_off' : 'volume_up'); + } + if (progressElement) { + progressElement.style.width = (volume || 0) + '%'; + } +} + +function releaseCurrentPlayer() { + + var player = currentPlayer; + + if (player) { + events.off(player, 'volumechange', onVolumeChanged); + events.off(player, 'playbackstop', hideOsd); + currentPlayer = null; + } +} + +function onVolumeChanged(e) { + + var player = this; + + ensureOsdElement(); + + updatePlayerVolumeState(player.isMuted(), player.getVolume()); + + showOsd(); +} + +function bindToPlayer(player) { + + if (player === currentPlayer) { + return; + } + + releaseCurrentPlayer(); + + currentPlayer = player; + + if (!player) { + return; + } + + hideOsd(); + events.on(player, 'volumechange', onVolumeChanged); + events.on(player, 'playbackstop', hideOsd); +} + +events.on(playbackManager, 'playerchange', function () { + bindToPlayer(playbackManager.getCurrentPlayer()); }); + +bindToPlayer(playbackManager.getCurrentPlayer()); From 70d01c92d45c15436d70e08cadfa56209ddde9c3 Mon Sep 17 00:00:00 2001 From: Delgan Date: Thu, 21 May 2020 19:14:25 +0200 Subject: [PATCH 0036/2132] Remove unsed imports and merge classList calls Co-authored-by: Sarab Singh --- src/components/playback/brightnessosd.js | 4 +--- src/components/playback/playersettingsmenu.js | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/playback/brightnessosd.js b/src/components/playback/brightnessosd.js index 3a0e270257..5f3becd64b 100644 --- a/src/components/playback/brightnessosd.js +++ b/src/components/playback/brightnessosd.js @@ -103,9 +103,7 @@ function hideOsd() { } function setIcon(iconElement, icon) { - iconElement.classList.remove('brightness_high'); - iconElement.classList.remove('brightness_medium'); - iconElement.classList.remove('brightness_low'); + iconElement.classList.remove('brightness_high', 'brightness_medium', 'brightness_low'); iconElement.classList.add(icon); } diff --git a/src/components/playback/playersettingsmenu.js b/src/components/playback/playersettingsmenu.js index 2544590b34..33d252c52a 100644 --- a/src/components/playback/playersettingsmenu.js +++ b/src/components/playback/playersettingsmenu.js @@ -1,9 +1,7 @@ import connectionManager from 'connectionManager'; import actionsheet from 'actionsheet'; -import datetime from 'datetime'; import playbackManager from 'playbackManager'; import globalize from 'globalize'; -import appSettings from 'appSettings'; import qualityoptions from 'qualityoptions'; function showQualityMenu(player, btn) { From 76ad33449f3210508a3b9efdf33d31bd43c8faf5 Mon Sep 17 00:00:00 2001 From: Delgan Date: Thu, 21 May 2020 19:16:19 +0200 Subject: [PATCH 0037/2132] Fix export of "showPlayerSelection()" by renaming it to "show()" --- src/components/playback/playerSelectionMenu.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/playback/playerSelectionMenu.js b/src/components/playback/playerSelectionMenu.js index eb0ae08497..91b1ddc20b 100644 --- a/src/components/playback/playerSelectionMenu.js +++ b/src/components/playback/playerSelectionMenu.js @@ -83,7 +83,7 @@ function getIcon(target) { } } -export function showPlayerSelection(button) { +export function show(button) { var currentPlayerInfo = playbackManager.getPlayerInfo(); @@ -321,5 +321,5 @@ events.on(playbackManager, 'pairerror', function (e) { }); export default { - show: showPlayerSelection + show: show }; From 5b2837f1377a5a0a53b1ee1c9f6f8b4f5f494c1e Mon Sep 17 00:00:00 2001 From: dkanada Date: Fri, 22 May 2020 10:39:21 +0900 Subject: [PATCH 0038/2132] use a class and imports for the book player --- package.json | 1 + src/components/bookPlayer/plugin.js | 92 ++++++++++++++++------------- src/components/pluginManager.js | 2 +- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index a24030de0d..de95105298 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "src/components/playback/mediasession.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", + "src/components/bookPlayer/plugin.js", "src/scripts/dfnshelper.js", "src/scripts/dom.js", "src/scripts/filesystem.js", diff --git a/src/components/bookPlayer/plugin.js b/src/components/bookPlayer/plugin.js index 1abbf778c8..62fbd1a593 100644 --- a/src/components/bookPlayer/plugin.js +++ b/src/components/bookPlayer/plugin.js @@ -1,35 +1,44 @@ -define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavigation', 'dialogHelper', 'apphost', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (connectionManager, dom, loading, playbackManager, keyboardnavigation, dialogHelper, appHost) { - 'use strict'; +import connectionManager from 'connectionManager'; +import dom from 'dom'; +import loading from 'loading'; +import playbackManager from 'playbackManager'; +import keyboardnavigation from 'keyboardnavigation'; +import dialogHelper from 'dialogHelper'; +import appHost from 'apphost'; +import 'css!./style'; +import 'material-icons'; +import 'paper-icon-button-light'; - function BookPlayer() { - let self = this; +/* eslint-disable indent */ +export class BookPlayer { + constructor() { + this.name = 'Book Player'; + this.type = 'mediaplayer'; + this.id = 'bookplayer'; + this.priority = 1; + } - self.name = 'Book Player'; - self.type = 'mediaplayer'; - self.id = 'bookplayer'; - self.priority = 1; - - self.play = function (options) { + play(options) { loading.show(); - let elem = createMediaElement(); - return setCurrentSrc(elem, options); - }; + let elem = this.createMediaElement(); + return this.setCurrentSrc(elem, options); + } - self.stop = function () { - let elem = self._mediaElement; - let rendition = self._rendition; + stop() { + let elem = this._mediaElement; + let rendition = this._rendition; if (elem && rendition) { rendition.destroy(); elem.remove(); - self._mediaElement = null; + this._mediaElement = null; } - }; + } - function onWindowKeyUp(e) { + onWindowKeyUp(e) { let key = keyboardnavigation.getKeyName(e); - let rendition = self._rendition; + let rendition = this._rendition; let book = rendition.book; switch (key) { @@ -44,19 +53,19 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev(); break; case 'Escape': - dialogHelper.close(self._mediaElement); - if (self._tocElement) { - dialogHelper.close(self._tocElement); + dialogHelper.close(this._mediaElement); + if (this._tocElement) { + dialogHelper.close(this._tocElement); } break; } } - function onDialogClosed() { - self.stop(); + onDialogClosed() { + this.stop(); } - function replaceLinks(contents, f) { + replaceLinks(contents, f) { let links = contents.querySelectorAll('a[href]'); links.forEach((link) => { @@ -69,8 +78,8 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig }); } - function createMediaElement() { - let elem = self._mediaElement; + createMediaElement() { + let elem = this._mediaElement; if (elem) { return elem; @@ -104,7 +113,7 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig }); elem.querySelector('.btnBookplayerToc').addEventListener('click', function () { - let rendition = self._rendition; + let rendition = this._rendition; if (rendition) { let tocElement = dialogHelper.createDialog({ size: 'small', @@ -131,13 +140,13 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig dialogHelper.close(tocElement); }); - replaceLinks(tocElement, (href) => { + this.replaceLinks(tocElement, (href) => { let relative = rendition.book.path.relative(href); rendition.display(relative); dialogHelper.close(tocElement); }); - self._tocElement = tocElement; + this._tocElement = tocElement; dialogHelper.open(tocElement); } @@ -145,18 +154,19 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig dialogHelper.open(elem); - elem.addEventListener('close', onDialogClosed); + elem.addEventListener('close', this.onDialogClosed.bind(this)); } - self._mediaElement = elem; + this._mediaElement = elem; return elem; } - function setCurrentSrc(elem, options) { + setCurrentSrc(elem, options) { let serverId = options.items[0].ServerId; let apiClient = connectionManager.getApiClient(serverId); + const self = this; return new Promise(function (resolve, reject) { require(['epubjs'], function (epubjs) { let downloadHref = apiClient.getItemDownloadUrl(options.items[0].Id); @@ -168,9 +178,9 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig self._rendition = rendition; return rendition.display().then(function () { - document.addEventListener('keyup', onWindowKeyUp); + document.addEventListener('keyup', self.onWindowKeyUp.bind(self)); // FIXME: I don't really get why document keyup event is not triggered when epub is in focus - self._rendition.on('keyup', onWindowKeyUp); + self._rendition.on('keyup', self.onWindowKeyUp.bind(self)); loading.hide(); @@ -182,11 +192,11 @@ define(['connectionManager', 'dom', 'loading', 'playbackManager', 'keyboardnavig }); }); } - } - BookPlayer.prototype.canPlayMediaType = function (mediaType) { + canPlayMediaType(mediaType) { return (mediaType || '').toLowerCase() === 'book'; - }; + } +} - return BookPlayer; -}); +/* eslint-enable indent */ +export default BookPlayer; diff --git a/src/components/pluginManager.js b/src/components/pluginManager.js index 6cb56d767b..fd35d344bf 100644 --- a/src/components/pluginManager.js +++ b/src/components/pluginManager.js @@ -58,7 +58,7 @@ define(['events', 'globalize'], function (events, globalize) { return new Promise(function (resolve, reject) { require([pluginSpec], (pluginFactory) => { - var plugin = new pluginFactory(); + var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory(); // See if it's already installed var existing = instance.pluginsList.filter(function (p) { From 512f33a7fb30b2f36eb214cc78d2724788d9405f Mon Sep 17 00:00:00 2001 From: dkanada Date: Fri, 22 May 2020 10:42:58 +0900 Subject: [PATCH 0039/2132] fix all indentation issues in book player --- src/components/bookPlayer/plugin.js | 324 ++++++++++++++-------------- 1 file changed, 161 insertions(+), 163 deletions(-) diff --git a/src/components/bookPlayer/plugin.js b/src/components/bookPlayer/plugin.js index 62fbd1a593..557e3aa427 100644 --- a/src/components/bookPlayer/plugin.js +++ b/src/components/bookPlayer/plugin.js @@ -9,194 +9,192 @@ import 'css!./style'; import 'material-icons'; import 'paper-icon-button-light'; -/* eslint-disable indent */ export class BookPlayer { - constructor() { - this.name = 'Book Player'; - this.type = 'mediaplayer'; - this.id = 'bookplayer'; - this.priority = 1; + constructor() { + this.name = 'Book Player'; + this.type = 'mediaplayer'; + this.id = 'bookplayer'; + this.priority = 1; + } + + play(options) { + loading.show(); + let elem = this.createMediaElement(); + return this.setCurrentSrc(elem, options); + } + + stop() { + let elem = this._mediaElement; + let rendition = this._rendition; + + if (elem && rendition) { + rendition.destroy(); + + elem.remove(); + this._mediaElement = null; } + } - play(options) { - loading.show(); - let elem = this.createMediaElement(); - return this.setCurrentSrc(elem, options); + onWindowKeyUp(e) { + let key = keyboardnavigation.getKeyName(e); + let rendition = this._rendition; + let book = rendition.book; + + switch (key) { + case 'l': + case 'ArrowRight': + case 'Right': + book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next(); + break; + case 'j': + case 'ArrowLeft': + case 'Left': + book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev(); + break; + case 'Escape': + dialogHelper.close(this._mediaElement); + if (this._tocElement) { + dialogHelper.close(this._tocElement); + } + break; } + } - stop() { - let elem = this._mediaElement; - let rendition = this._rendition; + onDialogClosed() { + this.stop(); + } - if (elem && rendition) { - rendition.destroy(); + replaceLinks(contents, f) { + let links = contents.querySelectorAll('a[href]'); - elem.remove(); - this._mediaElement = null; - } - } + links.forEach((link) => { + let href = link.getAttribute('href'); - onWindowKeyUp(e) { - let key = keyboardnavigation.getKeyName(e); - let rendition = this._rendition; - let book = rendition.book; + link.onclick = function () { + f(href); + return false; + }; + }); + } - switch (key) { - case 'l': - case 'ArrowRight': - case 'Right': - book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next(); - break; - case 'j': - case 'ArrowLeft': - case 'Left': - book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev(); - break; - case 'Escape': - dialogHelper.close(this._mediaElement); - if (this._tocElement) { - dialogHelper.close(this._tocElement); - } - break; - } - } - - onDialogClosed() { - this.stop(); - } - - replaceLinks(contents, f) { - let links = contents.querySelectorAll('a[href]'); - - links.forEach((link) => { - let href = link.getAttribute('href'); - - link.onclick = function () { - f(href); - return false; - }; - }); - } - - createMediaElement() { - let elem = this._mediaElement; - - if (elem) { - return elem; - } - - elem = document.getElementById('bookPlayer'); - - if (!elem) { - elem = dialogHelper.createDialog({ - exitAnimationDuration: 400, - size: 'fullscreen', - autoFocus: false, - scrollY: false, - exitAnimation: 'fadeout', - removeOnClose: true - }); - elem.id = 'bookPlayer'; - - let html = ''; - html += '
    '; - html += ''; - html += '
    '; - html += '
    '; - html += ''; - html += '
    '; - - elem.innerHTML = html; - - elem.querySelector('.btnBookplayerExit').addEventListener('click', function () { - dialogHelper.close(elem); - }); - - elem.querySelector('.btnBookplayerToc').addEventListener('click', function () { - let rendition = this._rendition; - if (rendition) { - let tocElement = dialogHelper.createDialog({ - size: 'small', - autoFocus: false, - removeOnClose: true - }); - tocElement.id = 'dialogToc'; - - let tocHtml = '
    '; - tocHtml += ''; - tocHtml += '
    '; - tocHtml += '
      '; - rendition.book.navigation.forEach((chapter) => { - tocHtml += '
    • '; - // Remove '../' from href - let link = chapter.href.startsWith('../') ? chapter.href.substr(3) : chapter.href; - tocHtml += `${chapter.label}`; - tocHtml += '
    • '; - }); - tocHtml += '
    '; - tocElement.innerHTML = tocHtml; - - tocElement.querySelector('.btnBookplayerTocClose').addEventListener('click', function () { - dialogHelper.close(tocElement); - }); - - this.replaceLinks(tocElement, (href) => { - let relative = rendition.book.path.relative(href); - rendition.display(relative); - dialogHelper.close(tocElement); - }); - - this._tocElement = tocElement; - - dialogHelper.open(tocElement); - } - }); - - dialogHelper.open(elem); - - elem.addEventListener('close', this.onDialogClosed.bind(this)); - } - - this._mediaElement = elem; + createMediaElement() { + let elem = this._mediaElement; + if (elem) { return elem; } - setCurrentSrc(elem, options) { - let serverId = options.items[0].ServerId; - let apiClient = connectionManager.getApiClient(serverId); + elem = document.getElementById('bookPlayer'); - const self = this; - return new Promise(function (resolve, reject) { - require(['epubjs'], function (epubjs) { - let downloadHref = apiClient.getItemDownloadUrl(options.items[0].Id); - self._currentSrc = downloadHref; + if (!elem) { + elem = dialogHelper.createDialog({ + exitAnimationDuration: 400, + size: 'fullscreen', + autoFocus: false, + scrollY: false, + exitAnimation: 'fadeout', + removeOnClose: true + }); + elem.id = 'bookPlayer'; - let book = epubjs.default(downloadHref, {openAs: 'epub'}); + let html = ''; + html += '
    '; + html += ''; + html += '
    '; + html += '
    '; + html += ''; + html += '
    '; - let rendition = book.renderTo(elem, {width: '100%', height: '97%'}); - self._rendition = rendition; + elem.innerHTML = html; - return rendition.display().then(function () { - document.addEventListener('keyup', self.onWindowKeyUp.bind(self)); - // FIXME: I don't really get why document keyup event is not triggered when epub is in focus - self._rendition.on('keyup', self.onWindowKeyUp.bind(self)); + elem.querySelector('.btnBookplayerExit').addEventListener('click', function () { + dialogHelper.close(elem); + }); - loading.hide(); - - return resolve(); - }, function () { - console.error('Failed to display epub'); - return reject(); + elem.querySelector('.btnBookplayerToc').addEventListener('click', function () { + let rendition = this._rendition; + if (rendition) { + let tocElement = dialogHelper.createDialog({ + size: 'small', + autoFocus: false, + removeOnClose: true }); + tocElement.id = 'dialogToc'; + + let tocHtml = '
    '; + tocHtml += ''; + tocHtml += '
    '; + tocHtml += '
      '; + rendition.book.navigation.forEach((chapter) => { + tocHtml += '
    • '; + // Remove '../' from href + let link = chapter.href.startsWith('../') ? chapter.href.substr(3) : chapter.href; + tocHtml += `${chapter.label}`; + tocHtml += '
    • '; + }); + tocHtml += '
    '; + tocElement.innerHTML = tocHtml; + + tocElement.querySelector('.btnBookplayerTocClose').addEventListener('click', function () { + dialogHelper.close(tocElement); + }); + + this.replaceLinks(tocElement, (href) => { + let relative = rendition.book.path.relative(href); + rendition.display(relative); + dialogHelper.close(tocElement); + }); + + this._tocElement = tocElement; + + dialogHelper.open(tocElement); + } + }); + + dialogHelper.open(elem); + + elem.addEventListener('close', this.onDialogClosed.bind(this)); + } + + this._mediaElement = elem; + + return elem; + } + + setCurrentSrc(elem, options) { + let serverId = options.items[0].ServerId; + let apiClient = connectionManager.getApiClient(serverId); + + const self = this; + return new Promise(function (resolve, reject) { + require(['epubjs'], function (epubjs) { + let downloadHref = apiClient.getItemDownloadUrl(options.items[0].Id); + self._currentSrc = downloadHref; + + let book = epubjs.default(downloadHref, {openAs: 'epub'}); + + let rendition = book.renderTo(elem, {width: '100%', height: '97%'}); + self._rendition = rendition; + + return rendition.display().then(function () { + document.addEventListener('keyup', self.onWindowKeyUp.bind(self)); + // FIXME: I don't really get why document keyup event is not triggered when epub is in focus + self._rendition.on('keyup', self.onWindowKeyUp.bind(self)); + + loading.hide(); + + return resolve(); + }, function () { + console.error('Failed to display epub'); + return reject(); }); }); - } + }); + } canPlayMediaType(mediaType) { return (mediaType || '').toLowerCase() === 'book'; } } -/* eslint-enable indent */ export default BookPlayer; From f7f778b3532bd288e68f8f2e17bb8241a98030c8 Mon Sep 17 00:00:00 2001 From: dkanada Date: Fri, 22 May 2020 10:55:34 +0900 Subject: [PATCH 0040/2132] fix toc button for new book player class --- src/components/bookPlayer/plugin.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/bookPlayer/plugin.js b/src/components/bookPlayer/plugin.js index 557e3aa427..9b33ee5ab6 100644 --- a/src/components/bookPlayer/plugin.js +++ b/src/components/bookPlayer/plugin.js @@ -149,7 +149,7 @@ export class BookPlayer { dialogHelper.open(tocElement); } - }); + }.bind(this)); dialogHelper.open(elem); @@ -169,20 +169,18 @@ export class BookPlayer { return new Promise(function (resolve, reject) { require(['epubjs'], function (epubjs) { let downloadHref = apiClient.getItemDownloadUrl(options.items[0].Id); - self._currentSrc = downloadHref; - let book = epubjs.default(downloadHref, {openAs: 'epub'}); - let rendition = book.renderTo(elem, {width: '100%', height: '97%'}); - self._rendition = rendition; + self._currentSrc = downloadHref; + self._rendition = rendition; return rendition.display().then(function () { document.addEventListener('keyup', self.onWindowKeyUp.bind(self)); + // FIXME: I don't really get why document keyup event is not triggered when epub is in focus self._rendition.on('keyup', self.onWindowKeyUp.bind(self)); loading.hide(); - return resolve(); }, function () { console.error('Failed to display epub'); From 2efdc9414651dea8cac711073c2e06237310ec14 Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 21 May 2020 13:52:43 +0200 Subject: [PATCH 0041/2132] Add blurhash package --- package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index 0dad2dc99d..8d0c53c342 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ }, "dependencies": { "alameda": "^1.4.0", + "blurhash": "^1.1.3", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", "core-js": "^3.6.5", "date-fns": "^2.13.0", diff --git a/yarn.lock b/yarn.lock index e68063e688..bb5a8fb7f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1782,6 +1782,11 @@ bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +blurhash@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-1.1.3.tgz#dc325af7da836d07a0861d830bdd63694382483e" + integrity sha512-yUhPJvXexbqbyijCIE/T2NCXcj9iNPhWmOKbPTuR/cm7Q5snXYIfnVnz6m7MWOXxODMz/Cr3UcVkRdHiuDVRDw== + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" From 8ef7a7a0549ad3893611e2abfb324cc42c65015b Mon Sep 17 00:00:00 2001 From: ferferga Date: Sat, 23 May 2020 18:35:34 +0200 Subject: [PATCH 0042/2132] Blurhash implementation (from scratch) --- src/bundle.js | 6 ++ src/components/cardbuilder/cardBuilder.js | 36 ++++++++- src/components/images/imageLoader.js | 96 ++++++++++++++++++++--- src/components/images/style.css | 19 +++-- 4 files changed, 139 insertions(+), 18 deletions(-) diff --git a/src/bundle.js b/src/bundle.js index d7ba6c6a51..0cd7021878 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -16,6 +16,12 @@ _define('fetch', function() { return fetch; }); +// Blurhash +var blurhash = require('blurhash'); +_define('blurhash', function() { + return blurhash; +}); + // query-string var query = require('query-string'); _define('queryString', function() { diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index d4d4d7f73b..73f90cf4fd 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -505,6 +505,9 @@ import 'programStyles'; let imgUrl = null; let coverImage = false; let uiAspect = null; + let blurhash; + let blurhashimg = item.ImageBlurHashes; + let imgtag; if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) { @@ -513,6 +516,7 @@ import 'programStyles'; maxWidth: width, tag: item.ImageTags.Thumb }); + imgtag = item.ImageTags.Thumb; } else if ((options.preferBanner || shape === 'banner') && item.ImageTags && item.ImageTags.Banner) { @@ -521,6 +525,7 @@ import 'programStyles'; maxWidth: width, tag: item.ImageTags.Banner }); + imgtag = item.ImageTags.Banner; } else if (options.preferDisc && item.ImageTags && item.ImageTags.Disc) { @@ -529,6 +534,7 @@ import 'programStyles'; maxWidth: width, tag: item.ImageTags.Disc }); + imgtag = item.ImageTags.Disc; } else if (options.preferLogo && item.ImageTags && item.ImageTags.Logo) { @@ -537,6 +543,7 @@ import 'programStyles'; maxWidth: width, tag: item.ImageTags.Logo }); + imgtag = item.ImageTags.Logo; } else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) { @@ -545,6 +552,7 @@ import 'programStyles'; maxWidth: width, tag: item.ParentLogoImageTag }); + imgtag = item.ParentLogoImageTag; } else if (options.preferThumb && item.SeriesThumbImageTag && options.inheritThumb !== false) { @@ -553,6 +561,7 @@ import 'programStyles'; maxWidth: width, tag: item.SeriesThumbImageTag }); + imgtag = item.SeriesThumbImageTag; } else if (options.preferThumb && item.ParentThumbItemId && options.inheritThumb !== false && item.MediaType !== 'Photo') { @@ -561,6 +570,7 @@ import 'programStyles'; maxWidth: width, tag: item.ParentThumbImageTag }); + imgtag = item.ParentThumbImageTag; } else if (options.preferThumb && item.BackdropImageTags && item.BackdropImageTags.length) { @@ -569,6 +579,7 @@ import 'programStyles'; maxWidth: width, tag: item.BackdropImageTags[0] }); + imgtag = item.BackdropImageTags[0]; forceName = true; @@ -579,6 +590,7 @@ import 'programStyles'; maxWidth: width, tag: item.ParentBackdropImageTags[0] }); + imgtag = item.ParentBackdropImageTags[0]; } else if (item.ImageTags && item.ImageTags.Primary) { @@ -590,6 +602,7 @@ import 'programStyles'; maxWidth: width, tag: item.ImageTags.Primary }); + imgtag = item.ImageTags.Primary; if (options.preferThumb && options.showTitle !== false) { forceName = true; @@ -612,6 +625,7 @@ import 'programStyles'; maxWidth: width, tag: item.PrimaryImageTag }); + imgtag = item.PrimaryImageTag; if (options.preferThumb && options.showTitle !== false) { forceName = true; @@ -630,6 +644,7 @@ import 'programStyles'; maxWidth: width, tag: item.ParentPrimaryImageTag }); + imgtag = item.ParentPrimaryImageTag; } else if (item.SeriesPrimaryImageTag) { imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { @@ -637,6 +652,7 @@ import 'programStyles'; maxWidth: width, tag: item.SeriesPrimaryImageTag }); + imgtag = item.SeriesPrimaryImageTag; } else if (item.AlbumId && item.AlbumPrimaryImageTag) { height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; @@ -647,6 +663,7 @@ import 'programStyles'; maxWidth: width, tag: item.AlbumPrimaryImageTag }); + imgtag = item.AlbumPrimaryImageTag; if (primaryImageAspectRatio) { uiAspect = getDesiredAspect(shape); @@ -661,6 +678,7 @@ import 'programStyles'; maxWidth: width, tag: item.ImageTags.Thumb }); + imgtag = item.ImageTags.Thumb; } else if (item.BackdropImageTags && item.BackdropImageTags.length) { @@ -669,6 +687,7 @@ import 'programStyles'; maxWidth: width, tag: item.BackdropImageTags[0] }); + imgtag = item.BackdropImageTags[0]; } else if (item.ImageTags && item.ImageTags.Thumb) { @@ -677,6 +696,7 @@ import 'programStyles'; maxWidth: width, tag: item.ImageTags.Thumb }); + imgtag = item.ImageTags.Thumb; } else if (item.SeriesThumbImageTag && options.inheritThumb !== false) { @@ -685,6 +705,7 @@ import 'programStyles'; maxWidth: width, tag: item.SeriesThumbImageTag }); + imgtag = item.SeriesThumbImageTag; } else if (item.ParentThumbItemId && options.inheritThumb !== false) { @@ -693,6 +714,7 @@ import 'programStyles'; maxWidth: width, tag: item.ParentThumbImageTag }); + imgtag = item.ParentThumbImageTag; } else if (item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false) { @@ -701,11 +723,15 @@ import 'programStyles'; maxWidth: width, tag: item.ParentBackdropImageTags[0] }); + imgtag = item.ParentBackdropImageTags[0]; } + blurhash = imageLoader.getImageBlurhashStr(blurhashimg, imgtag); + return { imgUrl: imgUrl, + blurhash: blurhash, forceName: forceName, coverImage: coverImage }; @@ -1321,6 +1347,7 @@ import 'programStyles'; const imgInfo = getCardImageUrl(item, apiClient, options, shape); const imgUrl = imgInfo.imgUrl; + const blurhash = imgInfo.blurhash; const forceName = imgInfo.forceName; @@ -1445,15 +1472,20 @@ import 'programStyles'; cardContentClass += ' cardContent-shadow'; } + let blurhashAttrib = ''; + if (blurhash && blurhash.length > 0) { + blurhashAttrib = 'data-blurhash="' + blurhash + '"'; + } + if (layoutManager.tv) { // Don't use the IMG tag with safari because it puts a white border around it - cardImageContainerOpen = imgUrl ? ('
    ') : ('
    '); + cardImageContainerOpen = imgUrl ? ('
    ') : ('
    '); cardImageContainerClose = '
    '; } else { // Don't use the IMG tag with safari because it puts a white border around it - cardImageContainerOpen = imgUrl ? (''; } diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index f23b407def..fe450b2816 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -1,5 +1,6 @@ import * as lazyLoader from 'lazyLoader'; import * as userSettings from 'userSettings'; +import * as blurhash from 'blurhash'; import 'css!./style'; /* eslint-disable indent */ @@ -11,6 +12,82 @@ import 'css!./style'; fillImageElement(elem, source); } + export function getImageBlurhashStr(hashes, tags) { + if (hashes && tags) { + return hashes[tags]; + } + return null; + } + + // function destroyBlurhash(target) { + // let canvas = target.getElementsByClassName('blurhash-canvas')[0]; + // target.removeChild(canvas); + // target.classList.remove('blurhashed'); + // } + + function itemBlurhashing(entry) { + // This intersection ratio ensures that items that are near the borders are also blurhashed, alongside items that are outside the viewport + // if (entry.intersectionRation <= 0.025) + if (entry.target) { + let target = entry.target; + // We only keep max 80 items blurhashed in screen to save memory + // if (document.getElementsByClassName('blurhashed').length <= 80) { + + //} else { + // destroyBlurhash(target); + //} + let blurhashstr = target.getAttribute('data-blurhash'); + if (blurhash.isBlurhashValid(blurhashstr) && target.getElementsByClassName('blurhash-canvas').length === 0) { + console.log('Blurhashing item ' + target.parentElement.parentElement.parentElement.getAttribute('data-index') + ' with intersection ratio ' + entry.intersectionRatio); + let width = target.offsetWidth; + let height = target.offsetHeight; + if (width && height) { + let pixels; + try { + pixels = blurhash.decode(blurhashstr, width, height); + } catch (err) { + console.log('Blurhash decode error: ' + err.toString()); + return; + } + + let canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + let ctx = canvas.getContext('2d'); + let imgData = ctx.createImageData(width, height); + + imgData.data.set(pixels); + // Values taken from https://www.npmjs.com/package/blurhash + ctx.putImageData(imgData, 1, 1); + + let child = target.appendChild(canvas); + child.classList.add('blurhash-canvas'); + child.style.opacity = 1; + if (userSettings.enableFastFadein()) { + child.classList.add('lazy-blurhash-fadein-fast'); + } else { + child.classList.add('lazy-blurhash-fadein'); + } + + target.classList.add('blurhashed'); + } + } + } + return; + } + + function switchCanvas(elem) { + let child = elem.getElementsByClassName('blurhash-canvas')[0]; + if (child) { + if (elem.getAttribute('data-src')) { + child.style.opacity = 1; + } else { + child.style.opacity = 0; + } + } + return; + } + export function fillImage(entry) { if (!entry) { throw new Error('entry cannot be null'); @@ -23,6 +100,10 @@ import 'css!./style'; source = entry; } + if (!entry.target.classList.contains('blurhashed')) { + itemBlurhashing(entry); + } + if (entry.intersectionRatio > 0) { if (source) fillImageElement(entry.target, source); } else if (!source) { @@ -45,14 +126,12 @@ import 'css!./style'; elem.setAttribute('src', url); } - if (userSettings.enableFastFadein()) { - elem.classList.add('lazy-image-fadein-fast'); - } else { - elem.classList.add('lazy-image-fadein'); - } - elem.removeAttribute('data-src'); + switchCanvas(elem); }); + // preloaderImg.onload = function () { + + // }; } function emptyImageElement(elem) { @@ -67,9 +146,7 @@ import 'css!./style'; } elem.setAttribute('data-src', url); - - elem.classList.remove('lazy-image-fadein-fast'); - elem.classList.remove('lazy-image-fadein'); + switchCanvas(elem); } export function lazyChildren(elem) { @@ -148,6 +225,7 @@ import 'css!./style'; export default { fillImages: fillImages, fillImage: fillImage, + getImageBlurhashStr: getImageBlurhashStr, lazyImage: lazyImage, lazyChildren: lazyChildren, getPrimaryImageAspectRatio: getPrimaryImageAspectRatio diff --git a/src/components/images/style.css b/src/components/images/style.css index 2b9422d55b..2182452b1b 100644 --- a/src/components/images/style.css +++ b/src/components/images/style.css @@ -1,13 +1,18 @@ -.cardImageContainer.lazy { - opacity: 0; +.lazy-blurhash-fadein-fast { + transition: opacity 0.2s; } -.cardImageContainer.lazy.lazy-image-fadein { - opacity: 1; +.lazy-blurhash-fadein { transition: opacity 0.7s; } -.cardImageContainer.lazy.lazy-image-fadein-fast { - opacity: 1; - transition: opacity 0.2s; +/* We let the canvas overflow a little, so it gives a cooler zoom effect when transitioning */ +.blurhash-canvas { + align-items: stretch; + position: absolute; + top: -5px; + left: -5px; + width: 105%; + height: 105%; + z-index: -1000; } From 6840beaca9f0cc9a168c80de9aa5bc8e95ea2946 Mon Sep 17 00:00:00 2001 From: ferferga Date: Sat, 23 May 2020 19:43:22 +0200 Subject: [PATCH 0043/2132] Remove useless comment --- src/components/images/imageLoader.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index fe450b2816..ef13dcc8bc 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -129,9 +129,6 @@ import 'css!./style'; elem.removeAttribute('data-src'); switchCanvas(elem); }); - // preloaderImg.onload = function () { - - // }; } function emptyImageElement(elem) { From d7fda69e9ccd16c9e31a15b19542dac67d8fdb74 Mon Sep 17 00:00:00 2001 From: ferferga Date: Sat, 23 May 2020 19:58:03 +0200 Subject: [PATCH 0044/2132] Revert testing values --- src/components/images/imageLoader.js | 2 +- src/components/images/style.css | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index ef13dcc8bc..8c7eacc8b9 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -58,7 +58,7 @@ import 'css!./style'; imgData.data.set(pixels); // Values taken from https://www.npmjs.com/package/blurhash - ctx.putImageData(imgData, 1, 1); + ctx.putImageData(imgData, 0, 0); let child = target.appendChild(canvas); child.classList.add('blurhash-canvas'); diff --git a/src/components/images/style.css b/src/components/images/style.css index 2182452b1b..d186b10422 100644 --- a/src/components/images/style.css +++ b/src/components/images/style.css @@ -6,13 +6,12 @@ transition: opacity 0.7s; } -/* We let the canvas overflow a little, so it gives a cooler zoom effect when transitioning */ .blurhash-canvas { align-items: stretch; position: absolute; - top: -5px; - left: -5px; - width: 105%; - height: 105%; + top: 0; + left: 0; + width: 100%; + height: 100%; z-index: -1000; } From 3b6fa7e97201a5121975e6280013455e79f26edc Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 24 May 2020 18:30:35 +0900 Subject: [PATCH 0045/2132] add display preference for details banner and update some defaults --- src/components/displaySettings/displaySettings.js | 2 ++ .../displaySettings/displaySettings.template.html | 8 ++++++++ src/controllers/itemDetails.js | 2 +- src/scripts/settings/userSettings.js | 13 +++++++++++-- src/strings/en-gb.json | 2 +- src/strings/en-us.json | 8 +++++--- 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/components/displaySettings/displaySettings.js b/src/components/displaySettings/displaySettings.js index b6938bc95d..4e068960a3 100644 --- a/src/components/displaySettings/displaySettings.js +++ b/src/components/displaySettings/displaySettings.js @@ -182,6 +182,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', ' context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos(); context.querySelector('#chkFadein').checked = userSettings.enableFastFadein(); context.querySelector('#chkBackdrops').checked = userSettings.enableBackdrops(); + context.querySelector('#chkDetailsBanner').checked = userSettings.detailsBanner(); context.querySelector('#selectLanguage').value = userSettings.language() || ''; context.querySelector('.selectDateTimeLocale').value = userSettings.dateTimeLocale() || ''; @@ -223,6 +224,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', ' userSettingsInstance.enableFastFadein(context.querySelector('#chkFadein').checked); userSettingsInstance.enableBackdrops(context.querySelector('#chkBackdrops').checked); + userSettingsInstance.detailsBanner(context.querySelector('#chkDetailsBanner').checked); if (user.Id === apiClient.getCurrentUserId()) { skinManager.setTheme(userSettingsInstance.theme()); diff --git a/src/components/displaySettings/displaySettings.template.html b/src/components/displaySettings/displaySettings.template.html index b8ab1a9ba5..d37c24b49d 100644 --- a/src/components/displaySettings/displaySettings.template.html +++ b/src/components/displaySettings/displaySettings.template.html @@ -156,6 +156,14 @@
    ${EnableFastImageFadeInHelp}
    +
    + +
    ${EnableDetailsBannerHelp}
    +
    +
    '; $('.deviceAccess', page).show().html(html); - $('#chkEnableAllDevices', page).checked(user.Policy.EnableAllDevices).trigger('change'); + $('#chkEnableAllDevices', page).checked = user.Policy.EnableAllDevices; if (user.Policy.IsAdministrator) { page.querySelector('.deviceAccessContainer').classList.add('hide'); @@ -90,19 +90,19 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function } function saveUser(user, page) { - user.Policy.EnableAllFolders = $('#chkEnableAllFolders', page).checked(); + user.Policy.EnableAllFolders = $('#chkEnableAllFolders', page).checked; user.Policy.EnabledFolders = user.Policy.EnableAllFolders ? [] : $('.chkFolder', page).get().filter(function (c) { return c.checked; }).map(function (c) { return c.getAttribute('data-id'); }); - user.Policy.EnableAllChannels = $('#chkEnableAllChannels', page).checked(); + user.Policy.EnableAllChannels = $('#chkEnableAllChannels', page).checked; user.Policy.EnabledChannels = user.Policy.EnableAllChannels ? [] : $('.chkChannel', page).get().filter(function (c) { return c.checked; }).map(function (c) { return c.getAttribute('data-id'); }); - user.Policy.EnableAllDevices = $('#chkEnableAllDevices', page).checked(); + user.Policy.EnableAllDevices = $('#chkEnableAllDevices', page).checked; user.Policy.EnabledDevices = user.Policy.EnableAllDevices ? [] : $('.chkDevice', page).get().filter(function (c) { return c.checked; }).map(function (c) { diff --git a/src/controllers/dashboard/users/usernew.js b/src/controllers/dashboard/users/usernew.js index 5646b239d0..1e4e2bbee2 100644 --- a/src/controllers/dashboard/users/usernew.js +++ b/src/controllers/dashboard/users/usernew.js @@ -1,4 +1,4 @@ -define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-checkbox'], function ($, loading, globalize) { +define(['jQuery', 'loading', 'globalize', 'emby-checkbox'], function ($, loading, globalize) { 'use strict'; function loadMediaFolders(page, mediaFolders) { @@ -13,7 +13,7 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-checkbox'], functio html += '
    '; $('.folderAccess', page).html(html).trigger('create'); - $('#chkEnableAllFolders', page).checked(false).trigger('change'); + $('#chkEnableAllFolders', page).checked = false; } function loadChannels(page, channels) { @@ -35,7 +35,7 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-checkbox'], functio $('.channelAccessContainer', page).hide(); } - $('#chkEnableAllChannels', page).checked(false).trigger('change'); + $('#chkEnableAllChannels', page).checked = false; } function loadUser(page) { @@ -58,7 +58,7 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-checkbox'], functio user.Name = $('#txtUsername', page).val(); user.Password = $('#txtPassword', page).val(); ApiClient.createUser(user).then(function (user) { - user.Policy.EnableAllFolders = $('#chkEnableAllFolders', page).checked(); + user.Policy.EnableAllFolders = $('#chkEnableAllFolders', page).checked; user.Policy.EnabledFolders = []; if (!user.Policy.EnableAllFolders) { @@ -69,7 +69,7 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-checkbox'], functio }); } - user.Policy.EnableAllChannels = $('#chkEnableAllChannels', page).checked(); + user.Policy.EnableAllChannels = $('#chkEnableAllChannels', page).checked; user.Policy.EnabledChannels = []; if (!user.Policy.EnableAllChannels) { diff --git a/src/controllers/livetvsettings.js b/src/controllers/livetvsettings.js index 7a8552b9ac..4c5fdc60de 100644 --- a/src/controllers/livetvsettings.js +++ b/src/controllers/livetvsettings.js @@ -1,4 +1,4 @@ -define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-button'], function ($, loading, globalize) { +define(['jQuery', 'loading', 'globalize', 'emby-button'], function ($, loading, globalize) { 'use strict'; function loadPage(page, config) { diff --git a/src/dashboard.html b/src/dashboard.html index 9063f55965..b7af3c8c03 100644 --- a/src/dashboard.html +++ b/src/dashboard.html @@ -16,7 +16,7 @@
    -
    -
    - -
    ${LaunchWebAppOnStartupHelp}
    -
    From 8cef3817bd6d3ce3a8f552d09ddc6c438a18e108 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 25 May 2020 15:06:21 +0900 Subject: [PATCH 0047/2132] fix linting error from missing semicolon --- src/controllers/dashboard/dlna/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/dashboard/dlna/settings.js b/src/controllers/dashboard/dlna/settings.js index 6cca2fd348..da254c0bd9 100644 --- a/src/controllers/dashboard/dlna/settings.js +++ b/src/controllers/dashboard/dlna/settings.js @@ -22,7 +22,7 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, config.EnablePlayTo = form.querySelector('#chkEnablePlayTo').checked; config.EnableDebugLog = form.querySelector('#chkEnableDlnaDebugLogging').checked; config.ClientDiscoveryIntervalSeconds = $('#txtClientDiscoveryInterval', form).val(); - config.EnableServer = $('#chkEnableServer', form).checked + config.EnableServer = $('#chkEnableServer', form).checked; config.BlastAliveMessages = $('#chkBlastAliveMessages', form).checked; config.BlastAliveMessageIntervalSeconds = $('#txtBlastInterval', form).val(); config.DefaultUserId = $('#selectUser', form).val(); From 7587469480fd5d85c42545bd929b6db78c77844d Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 25 May 2020 18:31:51 +0200 Subject: [PATCH 0048/2132] Improve decoding speed --- src/components/images/imageLoader.js | 7 ++++--- src/components/images/style.css | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index 8c7eacc8b9..f9cbb33c6c 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -39,8 +39,10 @@ import 'css!./style'; let blurhashstr = target.getAttribute('data-blurhash'); if (blurhash.isBlurhashValid(blurhashstr) && target.getElementsByClassName('blurhash-canvas').length === 0) { console.log('Blurhashing item ' + target.parentElement.parentElement.parentElement.getAttribute('data-index') + ' with intersection ratio ' + entry.intersectionRatio); - let width = target.offsetWidth; - let height = target.offsetHeight; + // let width = target.offsetWidth; + // let height = target.offsetHeight; + let width = 18; + let height = 18; if (width && height) { let pixels; try { @@ -49,7 +51,6 @@ import 'css!./style'; console.log('Blurhash decode error: ' + err.toString()); return; } - let canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; diff --git a/src/components/images/style.css b/src/components/images/style.css index d186b10422..afd7e004ea 100644 --- a/src/components/images/style.css +++ b/src/components/images/style.css @@ -7,11 +7,12 @@ } .blurhash-canvas { - align-items: stretch; position: absolute; top: 0; + right: 0; + bottom: 0; left: 0; width: 100%; height: 100%; - z-index: -1000; + z-index: 100; } From 2bf7a53dc2bea40048566b0919f4f529b3935104 Mon Sep 17 00:00:00 2001 From: Vasily Date: Tue, 26 May 2020 01:29:29 +0300 Subject: [PATCH 0049/2132] Adapt to changes in server-side blurhash, reduce copypasta a bit --- src/components/cardbuilder/cardBuilder.js | 207 +++++----------------- src/components/images/imageLoader.js | 8 - 2 files changed, 42 insertions(+), 173 deletions(-) diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index 73f90cf4fd..a3c3aa3234 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -503,107 +503,40 @@ import 'programStyles'; const primaryImageAspectRatio = item.PrimaryImageAspectRatio; let forceName = false; let imgUrl = null; + let imgTag = null; let coverImage = false; let uiAspect = null; let blurhash; - let blurhashimg = item.ImageBlurHashes; - let imgtag; + let imgType; if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: 'Thumb', - maxWidth: width, - tag: item.ImageTags.Thumb - }); - imgtag = item.ImageTags.Thumb; - + imgType = 'Thumb'; } else if ((options.preferBanner || shape === 'banner') && item.ImageTags && item.ImageTags.Banner) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: 'Banner', - maxWidth: width, - tag: item.ImageTags.Banner - }); - imgtag = item.ImageTags.Banner; - + imgType = 'Banner'; } else if (options.preferDisc && item.ImageTags && item.ImageTags.Disc) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: 'Disc', - maxWidth: width, - tag: item.ImageTags.Disc - }); - imgtag = item.ImageTags.Disc; - + imgType = 'Disc'; } else if (options.preferLogo && item.ImageTags && item.ImageTags.Logo) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: 'Logo', - maxWidth: width, - tag: item.ImageTags.Logo - }); - imgtag = item.ImageTags.Logo; - + imgType = 'Logo'; } else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) { - - imgUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, { - type: 'Logo', - maxWidth: width, - tag: item.ParentLogoImageTag - }); - imgtag = item.ParentLogoImageTag; - + imgType = 'Logo'; + imgTag = item.ParentLogoImageTag; } else if (options.preferThumb && item.SeriesThumbImageTag && options.inheritThumb !== false) { - - imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { - type: 'Thumb', - maxWidth: width, - tag: item.SeriesThumbImageTag - }); - imgtag = item.SeriesThumbImageTag; - + imgType = 'Thumb'; + imgTag = item.SeriesThumbImageTag; } else if (options.preferThumb && item.ParentThumbItemId && options.inheritThumb !== false && item.MediaType !== 'Photo') { - - imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { - type: 'Thumb', - maxWidth: width, - tag: item.ParentThumbImageTag - }); - imgtag = item.ParentThumbImageTag; - + imgType = 'Thumb'; + imgTag = item.ParentThumbImageTag; } else if (options.preferThumb && item.BackdropImageTags && item.BackdropImageTags.length) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: 'Backdrop', - maxWidth: width, - tag: item.BackdropImageTags[0] - }); - imgtag = item.BackdropImageTags[0]; - + imgType = 'Backdrop'; + imgTag = item.BackdropImageTags[0]; forceName = true; - } else if (options.preferThumb && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false && item.Type === 'Episode') { - - imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { - type: 'Backdrop', - maxWidth: width, - tag: item.ParentBackdropImageTags[0] - }); - imgtag = item.ParentBackdropImageTags[0]; - + imgType = 'Backdrop'; + imgTag = item.ParentBackdropImageTags[0]; } else if (item.ImageTags && item.ImageTags.Primary) { - + imgType = 'Primary'; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: 'Primary', - maxHeight: height, - maxWidth: width, - tag: item.ImageTags.Primary - }); - imgtag = item.ImageTags.Primary; - if (options.preferThumb && options.showTitle !== false) { forceName = true; } @@ -616,17 +549,10 @@ import 'programStyles'; } } else if (item.PrimaryImageTag) { - + imgType = 'Primary'; + imgTag = item.PrimaryImageTag; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; - imgUrl = apiClient.getScaledImageUrl(item.PrimaryImageItemId || item.Id || item.ItemId, { - type: 'Primary', - maxHeight: height, - maxWidth: width, - tag: item.PrimaryImageTag - }); - imgtag = item.PrimaryImageTag; - if (options.preferThumb && options.showTitle !== false) { forceName = true; } @@ -638,33 +564,16 @@ import 'programStyles'; } } } else if (item.ParentPrimaryImageTag) { - - imgUrl = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, { - type: 'Primary', - maxWidth: width, - tag: item.ParentPrimaryImageTag - }); - imgtag = item.ParentPrimaryImageTag; + imgType = 'Primary'; + imgTag = item.ParentPrimaryImageTag; } else if (item.SeriesPrimaryImageTag) { - - imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { - type: 'Primary', - maxWidth: width, - tag: item.SeriesPrimaryImageTag - }); - imgtag = item.SeriesPrimaryImageTag; + imgType = 'Primary'; + imgTag = item.SeriesPrimaryImageTag; } else if (item.AlbumId && item.AlbumPrimaryImageTag) { - + imgType = 'Primary'; + imgTag = item.AlbumPrimaryImageTag; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; - imgUrl = apiClient.getScaledImageUrl(item.AlbumId, { - type: 'Primary', - maxHeight: height, - maxWidth: width, - tag: item.AlbumPrimaryImageTag - }); - imgtag = item.AlbumPrimaryImageTag; - if (primaryImageAspectRatio) { uiAspect = getDesiredAspect(shape); if (uiAspect) { @@ -672,62 +581,30 @@ import 'programStyles'; } } } else if (item.Type === 'Season' && item.ImageTags && item.ImageTags.Thumb) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: 'Thumb', - maxWidth: width, - tag: item.ImageTags.Thumb - }); - imgtag = item.ImageTags.Thumb; - + imgType = 'Thumb'; } else if (item.BackdropImageTags && item.BackdropImageTags.length) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: 'Backdrop', - maxWidth: width, - tag: item.BackdropImageTags[0] - }); - imgtag = item.BackdropImageTags[0]; - + imgType = 'Backdrop'; + imgTag = item.BackdropImageTags[0]; } else if (item.ImageTags && item.ImageTags.Thumb) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: 'Thumb', - maxWidth: width, - tag: item.ImageTags.Thumb - }); - imgtag = item.ImageTags.Thumb; - + imgType = 'Thumb'; } else if (item.SeriesThumbImageTag && options.inheritThumb !== false) { - - imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { - type: 'Thumb', - maxWidth: width, - tag: item.SeriesThumbImageTag - }); - imgtag = item.SeriesThumbImageTag; - + imgType = 'Thumb'; + imgTag = item.SeriesThumbImageTag; } else if (item.ParentThumbItemId && options.inheritThumb !== false) { - - imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { - type: 'Thumb', - maxWidth: width, - tag: item.ParentThumbImageTag - }); - imgtag = item.ParentThumbImageTag; - + imgType = 'Thumb'; + imgTag = item.ParentThumbImageTag; } else if (item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false) { - - imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { - type: 'Backdrop', - maxWidth: width, - tag: item.ParentBackdropImageTags[0] - }); - imgtag = item.ParentBackdropImageTags[0]; - + imgType = 'Backdrop'; + imgTag = item.ParentBackdropImageTags[0]; } - blurhash = imageLoader.getImageBlurhashStr(blurhashimg, imgtag); + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: imgType, + maxHeight: height, + maxWidth: width, + tag: tag || item.ImageTags[imgType] + }); + blurhash = (item.ImageBlurHashes || {})[imgType]; return { imgUrl: imgUrl, diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index f9cbb33c6c..0ab90c7b9a 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -12,13 +12,6 @@ import 'css!./style'; fillImageElement(elem, source); } - export function getImageBlurhashStr(hashes, tags) { - if (hashes && tags) { - return hashes[tags]; - } - return null; - } - // function destroyBlurhash(target) { // let canvas = target.getElementsByClassName('blurhash-canvas')[0]; // target.removeChild(canvas); @@ -223,7 +216,6 @@ import 'css!./style'; export default { fillImages: fillImages, fillImage: fillImage, - getImageBlurhashStr: getImageBlurhashStr, lazyImage: lazyImage, lazyChildren: lazyChildren, getPrimaryImageAspectRatio: getPrimaryImageAspectRatio From b83dc6b197fb47819f5836dbc4a3dd4da07e7be2 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 26 May 2020 01:05:08 +0200 Subject: [PATCH 0050/2132] Fix variable typo --- src/components/cardbuilder/cardBuilder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index a3c3aa3234..1e8fcf251a 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -602,7 +602,7 @@ import 'programStyles'; type: imgType, maxHeight: height, maxWidth: width, - tag: tag || item.ImageTags[imgType] + tag: imgTag || item.ImageTags[imgType] }); blurhash = (item.ImageBlurHashes || {})[imgType]; From b46c48d4dc5c54fe07564ac60923a26d14507957 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 26 May 2020 10:47:20 +0200 Subject: [PATCH 0051/2132] Apply some suggestions --- src/components/cardbuilder/cardBuilder.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index 1e8fcf251a..dc1d7071f8 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -506,7 +506,6 @@ import 'programStyles'; let imgTag = null; let coverImage = false; let uiAspect = null; - let blurhash; let imgType; if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) { @@ -604,11 +603,10 @@ import 'programStyles'; maxWidth: width, tag: imgTag || item.ImageTags[imgType] }); - blurhash = (item.ImageBlurHashes || {})[imgType]; return { imgUrl: imgUrl, - blurhash: blurhash, + blurhash: (item.ImageBlurHashes || {})[imgType], forceName: forceName, coverImage: coverImage }; From c3ce87ecb23d6e987eff0d757dfc97427b8eff0b Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 26 May 2020 13:00:26 +0200 Subject: [PATCH 0052/2132] Implement blurhash in listview --- src/components/listview/listview.js | 34 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/listview/listview.js b/src/components/listview/listview.js index 587355b351..b29e513627 100644 --- a/src/components/listview/listview.js +++ b/src/components/listview/listview.js @@ -70,6 +70,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan function getImageUrl(item, width) { var apiClient = connectionManager.getApiClient(item.ServerId); + let itemId; var options = { maxWidth: width * 2, @@ -77,45 +78,37 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan }; if (item.ImageTags && item.ImageTags.Primary) { - options.tag = item.ImageTags.Primary; - return apiClient.getScaledImageUrl(item.Id, options); + itemId = item.Id; } if (item.AlbumId && item.AlbumPrimaryImageTag) { - options.tag = item.AlbumPrimaryImageTag; - return apiClient.getScaledImageUrl(item.AlbumId, options); + itemId = item.AlbumId; } else if (item.SeriesId && item.SeriesPrimaryImageTag) { - options.tag = item.SeriesPrimaryImageTag; - return apiClient.getScaledImageUrl(item.SeriesId, options); - + itemId = item.SeriesId; } else if (item.ParentPrimaryImageTag) { - options.tag = item.ParentPrimaryImageTag; - return apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, options); + itemId = item.ParentPrimaryImageItemId; } - return null; + return { url: apiClient.getScaledImageUrl(itemId, options) || null, blurHash: (item.ImageBlurHashes || {})[options.tag] || null}; } function getChannelImageUrl(item, width) { var apiClient = connectionManager.getApiClient(item.ServerId); - var options = { maxWidth: width * 2, type: 'Primary' }; if (item.ChannelId && item.ChannelPrimaryImageTag) { - options.tag = item.ChannelPrimaryImageTag; - return apiClient.getScaledImageUrl(item.ChannelId, options); } - return null; + return { url: apiClient.getScaledImageUrl(item.ChannelId, options) || null, blurhash: (item.ImageBlurHashes || {})[options.tag] || null}; } function getTextLinesHtml(textlines, isLargeStyle) { @@ -268,8 +261,10 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan } if (options.image !== false) { - var imgUrl = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth); - var imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage'; + let imgData = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth); + let imgUrl = imgData.url; + let blurhash = imgData.blurhash; + let imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage'; if (isLargeStyle && layoutManager.tv) { imageClass += ' listItemImage-large-tv'; @@ -283,8 +278,13 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan var imageAction = playOnImageClick ? 'resume' : action; + let blurhashAttrib = ''; + if (blurhash && blurhash.length > 0) { + blurhashAttrib = 'data-blurhash="' + blurhash + '"'; + } + if (imgUrl) { - html += '
    '; + html += '
    '; } else { html += '
    '; } From 3637a11a3b899ea7df5864c7d9a62bf880ac94b7 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 26 May 2020 14:03:59 +0200 Subject: [PATCH 0053/2132] Fix changes in 2bf7a53 --- src/components/cardbuilder/cardBuilder.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index dc1d7071f8..b00d555936 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -510,12 +510,16 @@ import 'programStyles'; if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) { imgType = 'Thumb'; + imgTag = item.ImageTags.Thumb; } else if ((options.preferBanner || shape === 'banner') && item.ImageTags && item.ImageTags.Banner) { imgType = 'Banner'; + imgTag = item.ImageTags.Banner; } else if (options.preferDisc && item.ImageTags && item.ImageTags.Disc) { imgType = 'Disc'; + imgTag = item.ImageTags.Disc; } else if (options.preferLogo && item.ImageTags && item.ImageTags.Logo) { imgType = 'Logo'; + imgTag = item.ImageTags.Logo; } else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) { imgType = 'Logo'; imgTag = item.ParentLogoImageTag; @@ -534,6 +538,7 @@ import 'programStyles'; imgTag = item.ParentBackdropImageTags[0]; } else if (item.ImageTags && item.ImageTags.Primary) { imgType = 'Primary'; + imgTag = item.ImageTags.Primary; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; if (options.preferThumb && options.showTitle !== false) { @@ -581,11 +586,13 @@ import 'programStyles'; } } else if (item.Type === 'Season' && item.ImageTags && item.ImageTags.Thumb) { imgType = 'Thumb'; + imgTag = item.ImageTags.Thumb; } else if (item.BackdropImageTags && item.BackdropImageTags.length) { imgType = 'Backdrop'; imgTag = item.BackdropImageTags[0]; } else if (item.ImageTags && item.ImageTags.Thumb) { imgType = 'Thumb'; + imgTag = item.ImageTags.Thumb; } else if (item.SeriesThumbImageTag && options.inheritThumb !== false) { imgType = 'Thumb'; imgTag = item.SeriesThumbImageTag; @@ -601,12 +608,12 @@ import 'programStyles'; type: imgType, maxHeight: height, maxWidth: width, - tag: imgTag || item.ImageTags[imgType] + tag: imgTag }); return { imgUrl: imgUrl, - blurhash: (item.ImageBlurHashes || {})[imgType], + blurhash: item.ImageBlurHashes[imgTag] || null, forceName: forceName, coverImage: coverImage }; From 93148c87ad7a65b5d1455cbc94789bbd9cc5ce89 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 26 May 2020 14:05:34 +0200 Subject: [PATCH 0054/2132] With async the DOM seems to be less blocked while generating (at least for me), so I guess we should keep it (?) :D --- src/components/images/imageLoader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index 0ab90c7b9a..ea40a93376 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -18,7 +18,7 @@ import 'css!./style'; // target.classList.remove('blurhashed'); // } - function itemBlurhashing(entry) { + async function itemBlurhashing(entry) { // This intersection ratio ensures that items that are near the borders are also blurhashed, alongside items that are outside the viewport // if (entry.intersectionRation <= 0.025) if (entry.target) { From f3129f28efc0e5f2c0ab72e4f06eff66f3bd5a1c Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 26 May 2020 14:48:34 +0200 Subject: [PATCH 0055/2132] Check for images for items without images --- src/components/cardbuilder/cardBuilder.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index b00d555936..7f1a4dc2d7 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -604,12 +604,14 @@ import 'programStyles'; imgTag = item.ParentBackdropImageTags[0]; } - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: imgType, - maxHeight: height, - maxWidth: width, - tag: imgTag - }); + if (imgTag && imgType) { + imgUrl = apiClient.getScaledImageUrl(item.Id, { + type: imgType, + maxHeight: height, + maxWidth: width, + tag: imgTag + }); + } return { imgUrl: imgUrl, From 71e1c890ae0f4746b67007ea1aa3dc89690ae1bf Mon Sep 17 00:00:00 2001 From: Influence365 Date: Tue, 26 May 2020 14:30:53 +0100 Subject: [PATCH 0056/2132] update card.css for bug fix jellyfin#1207 --- src/components/cardbuilder/card.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/cardbuilder/card.css b/src/components/cardbuilder/card.css index c24fcf6ba6..059c1d5986 100644 --- a/src/components/cardbuilder/card.css +++ b/src/components/cardbuilder/card.css @@ -157,7 +157,7 @@ button::-moz-focus-inner { } .cardImageContainer { - background-size: cover; + background-size: contain; background-repeat: no-repeat; background-position: center center; display: -webkit-flex; From 722c8321b0c86d880380683abfc78b1694a5d422 Mon Sep 17 00:00:00 2001 From: Influence365 Date: Tue, 26 May 2020 14:31:52 +0100 Subject: [PATCH 0057/2132] Update to imageeditor.js for bug fix jellyfin#1207 --- src/components/imageeditor/imageeditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/imageeditor/imageeditor.js b/src/components/imageeditor/imageeditor.js index 228060b8bf..502539fae6 100644 --- a/src/components/imageeditor/imageeditor.js +++ b/src/components/imageeditor/imageeditor.js @@ -132,7 +132,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', var imageUrl = getImageUrl(currentItem, apiClient, image.ImageType, image.ImageIndex, { maxWidth: imageSize }); - html += '
    '; + html += '
    '; html += '
    '; html += '
    '; From 04e27d98d5c89582351f3de9efd955e120a47aae Mon Sep 17 00:00:00 2001 From: Influence365 Date: Tue, 26 May 2020 14:32:47 +0100 Subject: [PATCH 0058/2132] Update to imageDownloader.js for bug fix jellyfin#1207 --- src/components/imageDownloader/imageDownloader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/imageDownloader/imageDownloader.js b/src/components/imageDownloader/imageDownloader.js index 220e4148cc..c989011e8d 100644 --- a/src/components/imageDownloader/imageDownloader.js +++ b/src/components/imageDownloader/imageDownloader.js @@ -203,9 +203,9 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image html += '
    '; if (layoutManager.tv || !appHost.supports('externallinks')) { - html += '
    '; + html += '
    '; } else { - html += ''; + html += ''; } html += '
    '; From 6428bb1ad59214e86f5c144b3c074de84a6035f3 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 26 May 2020 21:53:49 +0200 Subject: [PATCH 0059/2132] Code cleanup --- src/components/cardbuilder/cardBuilder.js | 2 +- src/components/images/imageLoader.js | 109 +++++++++------------- 2 files changed, 43 insertions(+), 68 deletions(-) diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index 7f1a4dc2d7..0c167df55b 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -615,7 +615,7 @@ import 'programStyles'; return { imgUrl: imgUrl, - blurhash: item.ImageBlurHashes[imgTag] || null, + blurhash: (item.ImageBlurHashes || {})[imgType], forceName: forceName, coverImage: coverImage }; diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index ea40a93376..2ba8e19ff3 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -12,96 +12,71 @@ import 'css!./style'; fillImageElement(elem, source); } - // function destroyBlurhash(target) { - // let canvas = target.getElementsByClassName('blurhash-canvas')[0]; - // target.removeChild(canvas); - // target.classList.remove('blurhashed'); - // } - - async function itemBlurhashing(entry) { - // This intersection ratio ensures that items that are near the borders are also blurhashed, alongside items that are outside the viewport - // if (entry.intersectionRation <= 0.025) - if (entry.target) { - let target = entry.target; - // We only keep max 80 items blurhashed in screen to save memory - // if (document.getElementsByClassName('blurhashed').length <= 80) { - - //} else { - // destroyBlurhash(target); - //} - let blurhashstr = target.getAttribute('data-blurhash'); - if (blurhash.isBlurhashValid(blurhashstr) && target.getElementsByClassName('blurhash-canvas').length === 0) { - console.log('Blurhashing item ' + target.parentElement.parentElement.parentElement.getAttribute('data-index') + ' with intersection ratio ' + entry.intersectionRatio); - // let width = target.offsetWidth; - // let height = target.offsetHeight; - let width = 18; - let height = 18; - if (width && height) { - let pixels; - try { - pixels = blurhash.decode(blurhashstr, width, height); - } catch (err) { - console.log('Blurhash decode error: ' + err.toString()); - return; - } - let canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - let ctx = canvas.getContext('2d'); - let imgData = ctx.createImageData(width, height); - - imgData.data.set(pixels); - // Values taken from https://www.npmjs.com/package/blurhash - ctx.putImageData(imgData, 0, 0); - - let child = target.appendChild(canvas); - child.classList.add('blurhash-canvas'); - child.style.opacity = 1; - if (userSettings.enableFastFadein()) { - child.classList.add('lazy-blurhash-fadein-fast'); - } else { - child.classList.add('lazy-blurhash-fadein'); - } - - target.classList.add('blurhashed'); - } + async function itemBlurhashing(target) { + let blurhashstr = target.getAttribute('data-blurhash'); + if (blurhashstr && blurhash.isBlurhashValid(blurhashstr)) { + // Although the default values provided by blurhash devs is 32x32, 18x18 seems to be the sweetest spot possible, + // cramping up a lot the performance and reducing the memory usage, while retaining almost full blur quality. + // Lower values had more visible pixelation + let width = 18; + let height = 18; + let pixels; + try { + pixels = blurhash.decode(blurhashstr, width, height); + } catch (err) { + console.error('Blurhash decode error: ' + err.toString()); + return; } + let canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + let ctx = canvas.getContext('2d'); + let imgData = ctx.createImageData(width, height); + + imgData.data.set(pixels); + ctx.putImageData(imgData, 0, 0); + + let child = target.appendChild(canvas); + child.classList.add('blurhash-canvas'); + child.style.opacity = 1; + if (userSettings.enableFastFadein()) { + child.classList.add('lazy-blurhash-fadein-fast'); + } else { + child.classList.add('lazy-blurhash-fadein'); + } + + target.classList.add('blurhashed'); } - return; } function switchCanvas(elem) { let child = elem.getElementsByClassName('blurhash-canvas')[0]; if (child) { - if (elem.getAttribute('data-src')) { - child.style.opacity = 1; - } else { - child.style.opacity = 0; - } + child.style.opacity = elem.getAttribute('data-src') ? 1 : 0; } - return; } export function fillImage(entry) { if (!entry) { throw new Error('entry cannot be null'); } - + let target = entry.target; var source = undefined; - if (entry.target) { - source = entry.target.getAttribute('data-src'); + + if (target) { + source = target.getAttribute('data-src'); } else { source = entry; } - if (!entry.target.classList.contains('blurhashed')) { - itemBlurhashing(entry); + if (!target.classList.contains('blurhashed')) { + itemBlurhashing(target); } if (entry.intersectionRatio > 0) { - if (source) fillImageElement(entry.target, source); + if (source) fillImageElement(target, source); } else if (!source) { - emptyImageElement(entry.target); + emptyImageElement(target); } } From 67da5e49ab98be81d899cde645983b9a2963977f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Dardenne?= Date: Tue, 26 May 2020 23:07:19 +0200 Subject: [PATCH 0060/2132] Transfer whole playlist when transfer playback to another device --- .../playback/remotecontrolautoplay.js | 76 +++++++++---------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/src/components/playback/remotecontrolautoplay.js b/src/components/playback/remotecontrolautoplay.js index 90a872cc6e..39a0db8286 100644 --- a/src/components/playback/remotecontrolautoplay.js +++ b/src/components/playback/remotecontrolautoplay.js @@ -1,47 +1,45 @@ -define(['events', 'playbackManager'], function (events, playbackManager) { - 'use strict'; +import events from 'events'; +import playbackManager from 'playbackManager'; - function transferPlayback(oldPlayer, newPlayer) { +function transferPlayback(oldPlayer, newPlayer) { + const state = playbackManager.getPlayerState(oldPlayer); + const item = state.NowPlayingItem; - var state = playbackManager.getPlayerState(oldPlayer); - - var item = state.NowPlayingItem; - - if (!item) { - return; - } - - var playState = state.PlayState || {}; - var resumePositionTicks = playState.PositionTicks || 0; - - playbackManager.stop(oldPlayer).then(function () { - - playbackManager.play({ - ids: [item.Id], - serverId: item.ServerId, - startPositionTicks: resumePositionTicks - - }, newPlayer); - }); + if (!item) { + return; } - events.on(playbackManager, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) { + const playlist = playbackManager.getPlaylist(oldPlayer).then(playlist => { + const playlistIds = playlist.map(x => x.Id); + const playState = state.PlayState || {}; + const resumePositionTicks = playState.PositionTicks || 0; + const playlistIndex = playlistIds.indexOf(item.Id) || 0; - if (!oldPlayer || !newPlayer) { - return; - } - - if (!oldPlayer.isLocalPlayer) { - console.debug('Skipping remote control autoplay because oldPlayer is not a local player'); - return; - } - - if (newPlayer.isLocalPlayer) { - console.debug('Skipping remote control autoplay because newPlayer is a local player'); - return; - } - - transferPlayback(oldPlayer, newPlayer); + playbackManager.stop(oldPlayer).then(() => { + playbackManager.play({ + ids: playlistIds, + serverId: item.ServerId, + startPositionTicks: resumePositionTicks, + startIndex: playlistIndex + }, newPlayer); + }); }); +} +events.on(playbackManager, 'playerchange', (e, newPlayer, newTarget, oldPlayer) => { + if (!oldPlayer || !newPlayer) { + return; + } + + if (!oldPlayer.isLocalPlayer) { + console.debug('Skipping remote control autoplay because oldPlayer is not a local player'); + return; + } + + if (newPlayer.isLocalPlayer) { + console.debug('Skipping remote control autoplay because newPlayer is a local player'); + return; + } + + transferPlayback(oldPlayer, newPlayer); }); From 10470e24a5bb28b41937f1bf8fe089cda1e8614b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Dardenne?= Date: Tue, 26 May 2020 23:09:08 +0200 Subject: [PATCH 0061/2132] Add ES6 module to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 14ff38d351..8111552d28 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "src/components/images/imageLoader.js", "src/components/lazyLoader/lazyLoaderIntersectionObserver.js", "src/components/playback/mediasession.js", + "src/components/playback/remotecontrolautoplay.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", "src/scripts/dfnshelper.js", From ffa9f2093baeb09f6e719b79ab24934ec20b0664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Dardenne?= Date: Tue, 26 May 2020 23:16:33 +0200 Subject: [PATCH 0062/2132] Fixed code smell --- src/components/playback/remotecontrolautoplay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/playback/remotecontrolautoplay.js b/src/components/playback/remotecontrolautoplay.js index 39a0db8286..c0adb57a45 100644 --- a/src/components/playback/remotecontrolautoplay.js +++ b/src/components/playback/remotecontrolautoplay.js @@ -9,7 +9,7 @@ function transferPlayback(oldPlayer, newPlayer) { return; } - const playlist = playbackManager.getPlaylist(oldPlayer).then(playlist => { + playbackManager.getPlaylist(oldPlayer).then(playlist => { const playlistIds = playlist.map(x => x.Id); const playState = state.PlayState || {}; const resumePositionTicks = playState.PositionTicks || 0; From ef0b4b5f51c978861a4f4acd02f5fa8bc3afded7 Mon Sep 17 00:00:00 2001 From: Influence365 Date: Wed, 27 May 2020 07:15:26 +0100 Subject: [PATCH 0063/2132] Update to imageDownloader.js for bug fix #1207 --- src/components/imageDownloader/imageDownloader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/imageDownloader/imageDownloader.js b/src/components/imageDownloader/imageDownloader.js index c989011e8d..a3965279cf 100644 --- a/src/components/imageDownloader/imageDownloader.js +++ b/src/components/imageDownloader/imageDownloader.js @@ -203,9 +203,9 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image html += '
    '; if (layoutManager.tv || !appHost.supports('externallinks')) { - html += '
    '; + html += '
    '; } else { - html += ''; + html += ''; } html += '
    '; From 9cc10c32ab666aa0d687143e8310130e14ad922d Mon Sep 17 00:00:00 2001 From: Influence365 Date: Wed, 27 May 2020 07:16:24 +0100 Subject: [PATCH 0064/2132] Update to imageeditor.js for bug fix #1207 --- src/components/imageeditor/imageeditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/imageeditor/imageeditor.js b/src/components/imageeditor/imageeditor.js index 502539fae6..c13d207892 100644 --- a/src/components/imageeditor/imageeditor.js +++ b/src/components/imageeditor/imageeditor.js @@ -132,7 +132,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', var imageUrl = getImageUrl(currentItem, apiClient, image.ImageType, image.ImageIndex, { maxWidth: imageSize }); - html += '
    '; + html += '
    '; html += '
    '; html += '
    '; From c9a6753aba031a14bb1571f6da5747020abf47c3 Mon Sep 17 00:00:00 2001 From: Influence365 Date: Wed, 27 May 2020 07:17:06 +0100 Subject: [PATCH 0065/2132] Update imageeditor.js --- src/components/imageeditor/imageeditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/imageeditor/imageeditor.js b/src/components/imageeditor/imageeditor.js index c13d207892..2927a0b120 100644 --- a/src/components/imageeditor/imageeditor.js +++ b/src/components/imageeditor/imageeditor.js @@ -132,7 +132,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', var imageUrl = getImageUrl(currentItem, apiClient, image.ImageType, image.ImageIndex, { maxWidth: imageSize }); - html += '
    '; + html += '
    '; html += ''; html += ''; From 0d7d4c7d15f5083b06477d8cb16e901ab3596d01 Mon Sep 17 00:00:00 2001 From: Influence365 Date: Wed, 27 May 2020 07:17:45 +0100 Subject: [PATCH 0066/2132] Reverted previous commit --- src/components/cardbuilder/card.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/cardbuilder/card.css b/src/components/cardbuilder/card.css index 059c1d5986..c24fcf6ba6 100644 --- a/src/components/cardbuilder/card.css +++ b/src/components/cardbuilder/card.css @@ -157,7 +157,7 @@ button::-moz-focus-inner { } .cardImageContainer { - background-size: contain; + background-size: cover; background-repeat: no-repeat; background-position: center center; display: -webkit-flex; From c0280bf28d1e47cdadd0f4fa882ffb2e4b598e3a Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 27 May 2020 12:12:46 +0200 Subject: [PATCH 0067/2132] Create strings & config placeholder --- .../displaySettings/displaySettings.template.html | 8 ++++++++ src/strings/en-us.json | 8 +++++--- src/strings/es.json | 8 +++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/components/displaySettings/displaySettings.template.html b/src/components/displaySettings/displaySettings.template.html index b8ab1a9ba5..58be97b066 100644 --- a/src/components/displaySettings/displaySettings.template.html +++ b/src/components/displaySettings/displaySettings.template.html @@ -156,6 +156,14 @@
    ${EnableFastImageFadeInHelp}
    +
    + +
    ${EnableBlurhashHelp}
    +
    +
    -
    +
    ${LabelCustomCssHelp}
    From a032f6a35da5ad1430fbedaf246992e864d0e454 Mon Sep 17 00:00:00 2001 From: Influence365 Date: Sat, 30 May 2020 09:44:23 +0100 Subject: [PATCH 0122/2132] Update emby-textarea.js --- src/elements/emby-textarea/emby-textarea.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/elements/emby-textarea/emby-textarea.js b/src/elements/emby-textarea/emby-textarea.js index e0ce77aa51..8d5a272656 100644 --- a/src/elements/emby-textarea/emby-textarea.js +++ b/src/elements/emby-textarea/emby-textarea.js @@ -55,6 +55,7 @@ define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'e newHeight = textarea.scrollHeight/* - offset*/; hasGrown = true; } + $('.customCSS1').css("height",newHeight + 'px'); textarea.style.height = newHeight + 'px'; } From 6ce8c77d8ec3cc547589fe22685d68c50cab5853 Mon Sep 17 00:00:00 2001 From: Influence365 Date: Sat, 30 May 2020 09:51:49 +0100 Subject: [PATCH 0123/2132] Update emby-textarea.js --- src/elements/emby-textarea/emby-textarea.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elements/emby-textarea/emby-textarea.js b/src/elements/emby-textarea/emby-textarea.js index 8d5a272656..8d01ff950b 100644 --- a/src/elements/emby-textarea/emby-textarea.js +++ b/src/elements/emby-textarea/emby-textarea.js @@ -55,7 +55,7 @@ define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'e newHeight = textarea.scrollHeight/* - offset*/; hasGrown = true; } - $('.customCSS1').css("height",newHeight + 'px'); + $('.customCSS').css("height",newHeight + 'px'); textarea.style.height = newHeight + 'px'; } From f0d337ee37c8542a7cc297af4232a7e4ccd9af7d Mon Sep 17 00:00:00 2001 From: dkanada Date: Sat, 30 May 2020 19:51:03 +0900 Subject: [PATCH 0124/2132] fix default export for photo player --- src/components/photoPlayer/plugin.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/photoPlayer/plugin.js b/src/components/photoPlayer/plugin.js index f658e4c702..d8d4ee70ef 100644 --- a/src/components/photoPlayer/plugin.js +++ b/src/components/photoPlayer/plugin.js @@ -1,6 +1,6 @@ import connectionManager from 'connectionManager'; -export class PhotoPlayer { +export default class PhotoPlayer { constructor() { this.name = 'Photo Player'; this.type = 'mediaplayer'; @@ -43,5 +43,3 @@ export class PhotoPlayer { return (mediaType || '').toLowerCase() === 'photo'; } } - -export default PhotoPlayer; From 0c302adbb5970a94d498599ad434b407a3cd4ecc Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 30 May 2020 13:00:51 +0200 Subject: [PATCH 0125/2132] Add format checking to EPUB player --- src/components/bookPlayer/plugin.js | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/components/bookPlayer/plugin.js b/src/components/bookPlayer/plugin.js index 66bcb46973..b655b038a8 100644 --- a/src/components/bookPlayer/plugin.js +++ b/src/components/bookPlayer/plugin.js @@ -215,24 +215,7 @@ export class BookPlayer { Id: item.Id } }; - if (!item.Path.endsWith('.epub')) { - return new Promise((resolve, reject) => { - let errorDialog = dialogHelper.createDialog({ - size: 'small', - autoFocus: false, - removeOnClose: true - }); - errorDialog.innerHTML = '

    This book type is not supported yet

    '; - - this.stop(); - - dialogHelper.open(errorDialog); - loading.hide(); - - return resolve(); - }); - } let serverId = item.ServerId; let apiClient = connectionManager.getApiClient(serverId); @@ -283,6 +266,13 @@ export class BookPlayer { canPlayMediaType(mediaType) { return (mediaType || '').toLowerCase() === 'book'; } + + canPlayItem(item) { + if (item.Path && (item.Path.endsWith('epub'))) { + return true; + } + return false; + } } export default BookPlayer; From 9535d94ab770089d7122d8702102340b6d85e2cf Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 30 May 2020 13:15:49 +0200 Subject: [PATCH 0126/2132] Add Path field to List query Fixes the lack of play button on books --- src/components/playback/playbackmanager.js | 2 -- src/controllers/list.js | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 053088ef2f..88d4e0d599 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -1129,7 +1129,6 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } self.canPlay = function (item) { - var itemType = item.Type; if (itemType === 'PhotoAlbum' || itemType === 'MusicGenre' || itemType === 'Season' || itemType === 'Series' || itemType === 'BoxSet' || itemType === 'MusicAlbum' || itemType === 'MusicArtist' || itemType === 'Playlist') { @@ -1143,7 +1142,6 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } if (itemType === 'Program') { - if (!item.EndDate || !item.StartDate) { return false; } diff --git a/src/controllers/list.js b/src/controllers/list.js index edd4469007..d05616ec9d 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -308,7 +308,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' return apiClient.getItems(apiClient.getCurrentUserId(), modifyQueryWithFilters(instance, { StartIndex: startIndex, Limit: limit, - Fields: 'PrimaryImageAspectRatio,SortName', + Fields: 'PrimaryImageAspectRatio,SortName,Path', ImageTypeLimit: 1, ParentId: item.Id, SortBy: sortBy From d8375efc6918017a5fa4d3bb6a009c5b40c3641d Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 30 May 2020 13:33:38 +0200 Subject: [PATCH 0127/2132] Add Path to home sections Fixes play button on home sections for books --- src/components/homesections/homesections.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/homesections/homesections.js b/src/components/homesections/homesections.js index 3cd30893b9..535f1cd687 100644 --- a/src/components/homesections/homesections.js +++ b/src/components/homesections/homesections.js @@ -229,7 +229,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la var options = { Limit: limit, - Fields: 'PrimaryImageAspectRatio,BasicSyncInfo', + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo,Path', ImageTypeLimit: 1, EnableImageTypes: 'Primary,Backdrop,Thumb', ParentId: parentId @@ -667,7 +667,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la var apiClient = connectionManager.getApiClient(serverId); return apiClient.getNextUpEpisodes({ Limit: enableScrollX() ? 24 : 15, - Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo', + Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo,Path', UserId: apiClient.getCurrentUserId(), ImageTypeLimit: 1, EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', From 8a1ca94760db89e7f848a3e782b6505199fd2e75 Mon Sep 17 00:00:00 2001 From: Influence365 Date: Sat, 30 May 2020 13:26:22 +0100 Subject: [PATCH 0128/2132] Update src/dashboardgeneral.html Co-authored-by: Julien Machiels --- src/dashboardgeneral.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashboardgeneral.html b/src/dashboardgeneral.html index 9ac0c0b0ca..6c0f43eb34 100644 --- a/src/dashboardgeneral.html +++ b/src/dashboardgeneral.html @@ -62,7 +62,7 @@
    ${LabelLoginDisclaimerHelp}
    -
    +
    ${LabelCustomCssHelp}
    From e6b7c8a6915540c7ef57ba833655b60174f46c63 Mon Sep 17 00:00:00 2001 From: Influence365 Date: Sat, 30 May 2020 13:27:06 +0100 Subject: [PATCH 0129/2132] Update emby-textarea.js --- src/elements/emby-textarea/emby-textarea.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elements/emby-textarea/emby-textarea.js b/src/elements/emby-textarea/emby-textarea.js index 8d01ff950b..58402843e9 100644 --- a/src/elements/emby-textarea/emby-textarea.js +++ b/src/elements/emby-textarea/emby-textarea.js @@ -55,7 +55,7 @@ define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'e newHeight = textarea.scrollHeight/* - offset*/; hasGrown = true; } - $('.customCSS').css("height",newHeight + 'px'); + $('.customCssContainer').css("height",newHeight + 'px'); textarea.style.height = newHeight + 'px'; } From 7169d8494d6794fb4a821488807d9e960e7fc45c Mon Sep 17 00:00:00 2001 From: ferferga Date: Sat, 30 May 2020 16:44:33 +0200 Subject: [PATCH 0130/2132] Add a toggle for blurhash --- .../displaySettings/displaySettings.js | 2 ++ .../displaySettings.template.html | 11 ++++--- src/components/images/imageLoader.js | 32 ++++++++++++++++--- src/components/images/style.css | 14 ++++++++ src/scripts/settings/userSettings.js | 10 ++++++ src/strings/es.json | 5 --- 6 files changed, 59 insertions(+), 15 deletions(-) diff --git a/src/components/displaySettings/displaySettings.js b/src/components/displaySettings/displaySettings.js index 4e068960a3..c4eb35f49f 100644 --- a/src/components/displaySettings/displaySettings.js +++ b/src/components/displaySettings/displaySettings.js @@ -181,6 +181,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', ' context.querySelector('#chkThemeSong').checked = userSettings.enableThemeSongs(); context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos(); context.querySelector('#chkFadein').checked = userSettings.enableFastFadein(); + context.querySelector('#chkBlurhash').checked = userSettings.enableBlurhash(); context.querySelector('#chkBackdrops').checked = userSettings.enableBackdrops(); context.querySelector('#chkDetailsBanner').checked = userSettings.detailsBanner(); @@ -223,6 +224,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', ' userSettingsInstance.skin(context.querySelector('.selectSkin').value); userSettingsInstance.enableFastFadein(context.querySelector('#chkFadein').checked); + userSettingsInstance.enableBlurhash(context.querySelector('#chkBlurhash').checked); userSettingsInstance.enableBackdrops(context.querySelector('#chkBackdrops').checked); userSettingsInstance.detailsBanner(context.querySelector('#chkDetailsBanner').checked); diff --git a/src/components/displaySettings/displaySettings.template.html b/src/components/displaySettings/displaySettings.template.html index 698d60af81..a0c5cba611 100644 --- a/src/components/displaySettings/displaySettings.template.html +++ b/src/components/displaySettings/displaySettings.template.html @@ -143,12 +143,12 @@
    -
    +
    ${LabelLibraryPageSizeHelp}
    -
    +
    -
    +
    ${EnableBlurhashHelp}
    -
    + +
    -
    - -
    ${EnableDecodingColorDepth10Help}
    -
    -
    From 212c093dab3561707beabba163bc4059161bce7e Mon Sep 17 00:00:00 2001 From: artiume Date: Fri, 29 May 2020 12:34:05 -0400 Subject: [PATCH 0228/2132] Update en-us.json --- src/strings/en-us.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 102d7d65b1..cbcd248bf9 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1550,8 +1550,6 @@ "OnApplicationStartup": "On application startup", "UnsupportedPlayback": "Jellyfin cannot decrypt content protected by DRM but all content will be attempted regardless, including protected titles. Some files may appear completely black due to encryption or other unsupported features, such as interactive titles.", "EnableBlurhash": "Enable blurred placeholders for images", - "EnableBlurhashHelp": "Images that are still being loaded will be displayed with a blurred placeholder", + "EnableBlurhashHelp": "Images that are still being loaded will be displayed with a blurred placeholder" - "EnableDecodingColorDepth10": "Enable 10-Bit hardware decoding", - "EnableDecodingColorDepth10Help" : "Enable 10-Bit hardware decoding on supported hardware. Only works for HEVC and VP9 formats. Turn this off if you experience playback issues." } From dbe4020ba5e06b91dc85a7a9b302ceb8f17315e1 Mon Sep 17 00:00:00 2001 From: artiume Date: Fri, 29 May 2020 14:06:02 -0400 Subject: [PATCH 0229/2132] Update encodingsettings.js --- src/controllers/dashboard/encodingsettings.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/controllers/dashboard/encodingsettings.js b/src/controllers/dashboard/encodingsettings.js index 65f4e4401d..0f54f9d70f 100644 --- a/src/controllers/dashboard/encodingsettings.js +++ b/src/controllers/dashboard/encodingsettings.js @@ -5,6 +5,8 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo Array.prototype.forEach.call(page.querySelectorAll('.chkDecodeCodec'), function (c) { c.checked = -1 !== (config.HardwareDecodingCodecs || []).indexOf(c.getAttribute('data-codec')); }); + page.querySelector('#chkDecodingColorDepth10Hevc').checked = config.EnableDecodingColorDepth10Hevc; + page.querySelector('#chkDecodingColorDepth10Vp9').checked = config.EnableDecodingColorDepth10Vp9; page.querySelector('#chkHardwareEncoding').checked = config.EnableHardwareEncoding; $('#selectVideoDecoder', page).val(config.HardwareAccelerationType); $('#selectThreadCount', page).val(config.EncodingThreadCount); @@ -67,6 +69,8 @@ define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, lo }), function (c) { return c.getAttribute('data-codec'); }); + config.EnableDecodingColorDepth10Hevc = form.querySelector('#chkDecodingColorDepth10Hevc').checked; + config.EnableDecodingColorDepth10Vp9 = form.querySelector('#chkDecodingColorDepth10Vp9').checked; config.EnableHardwareEncoding = form.querySelector('#chkHardwareEncoding').checked; ApiClient.updateNamedConfiguration('encoding', config).then(function () { updateEncoder(form); From d424ed669bf5397e1969f1da40e7a9968d5eed10 Mon Sep 17 00:00:00 2001 From: artiume Date: Fri, 29 May 2020 14:08:44 -0400 Subject: [PATCH 0230/2132] Update encodingsettings.html --- src/encodingsettings.html | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/encodingsettings.html b/src/encodingsettings.html index f63dd93e89..f5b7b0e1c0 100644 --- a/src/encodingsettings.html +++ b/src/encodingsettings.html @@ -43,10 +43,6 @@ HEVC - -
    +
    + + +
    ${EnableDecodingColorDepth10Help}
    +
    +
    -
    ${EnableDecodingColorDepth10Help}
    From f0af15707c449a9c5987147165714e9e3fc106de Mon Sep 17 00:00:00 2001 From: artiume Date: Fri, 29 May 2020 16:11:25 -0400 Subject: [PATCH 0233/2132] Update encodingsettings.html --- src/encodingsettings.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/encodingsettings.html b/src/encodingsettings.html index e3866a7e73..858375b145 100644 --- a/src/encodingsettings.html +++ b/src/encodingsettings.html @@ -66,11 +66,13 @@
    -
    +
    +
    +