From 02baff1fe0c5a22d9b1563f7101d0a01824e34cd Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 9 Apr 2020 00:35:54 +0800 Subject: [PATCH 0001/1458] 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/1458] 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 257ce4974ebee7c0977ae95cba4269805f6f8b77 Mon Sep 17 00:00:00 2001 From: Christoph Potas Date: Sun, 3 May 2020 20:30:35 +0200 Subject: [PATCH 0003/1458] ~ 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 0004/1458] ~ 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 5b40232b7bf96f5468429f17f928d26eb169076f Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Thu, 7 May 2020 09:45:49 +0300 Subject: [PATCH 0005/1458] 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 0006/1458] 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 0007/1458] 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 0008/1458] + 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 0009/1458] + 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 0010/1458] ~ 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 0011/1458] 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 0012/1458] 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 0013/1458] 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 0014/1458] 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 0015/1458] 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 5dd6f108518622703422443b03ebc988a566bb00 Mon Sep 17 00:00:00 2001 From: ferferga Date: Sat, 16 May 2020 15:21:07 +0200 Subject: [PATCH 0016/1458] 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 0017/1458] 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 0018/1458] 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 0019/1458] 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 0020/1458] 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 0021/1458] 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 0022/1458] 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 0023/1458] 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 0024/1458] 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 0025/1458] 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 0026/1458] 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 0027/1458] 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 0028/1458] 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 0029/1458] 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 0030/1458] 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 0031/1458] 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 739473c0fb35e5dd79710342e16a718658ee0d9d Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 25 May 2020 14:59:57 +0900 Subject: [PATCH 0032/1458] remove legacy jquery function from source --- src/controllers/dashboard/dashboard.js | 6 -- src/controllers/dashboard/dlna/profile.js | 34 ++++----- src/controllers/dashboard/dlna/settings.js | 10 +-- src/controllers/dashboard/general.js | 16 +--- .../dashboard/notifications/notification.js | 6 +- src/controllers/dashboard/users/useredit.js | 74 +++++++++---------- .../dashboard/users/userlibraryaccess.js | 12 +-- src/controllers/dashboard/users/usernew.js | 10 +-- src/controllers/livetvsettings.js | 2 +- src/dashboard.html | 2 +- src/dashboardgeneral.html | 7 -- 11 files changed, 76 insertions(+), 103 deletions(-) diff --git a/src/controllers/dashboard/dashboard.js b/src/controllers/dashboard/dashboard.js index 1256f60074..f91f4e01ef 100644 --- a/src/controllers/dashboard/dashboard.js +++ b/src/controllers/dashboard/dashboard.js @@ -179,12 +179,6 @@ define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globa view.querySelector('#operatingSystem').innerHTML = globalize.translate('DashboardOperatingSystem', systemInfo.OperatingSystem); view.querySelector('#architecture').innerHTML = globalize.translate('DashboardArchitecture', systemInfo.SystemArchitecture); - if (systemInfo.CanSelfRestart) { - view.querySelector('#btnRestartServer').classList.remove('hide'); - } else { - view.querySelector('#btnRestartServer').classList.add('hide'); - } - view.querySelector('#cachePath').innerHTML = systemInfo.CachePath; view.querySelector('#logPath').innerHTML = systemInfo.LogPath; view.querySelector('#transcodePath').innerHTML = systemInfo.TranscodingTempPath; diff --git a/src/controllers/dashboard/dlna/profile.js b/src/controllers/dashboard/dlna/profile.js index 3fe238c8ad..81eb099f50 100644 --- a/src/controllers/dashboard/dlna/profile.js +++ b/src/controllers/dashboard/dlna/profile.js @@ -1,4 +1,4 @@ -define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-select', 'emby-button', 'emby-input', 'emby-checkbox', 'listViewStyle', 'emby-button'], function ($, loading, globalize) { +define(['jQuery', 'loading', 'globalize', 'emby-select', 'emby-button', 'emby-input', 'emby-checkbox', 'listViewStyle', 'emby-button'], function ($, loading, globalize) { 'use strict'; function loadProfile(page) { @@ -23,8 +23,8 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-select', 'emby-butt $('.chkMediaType', page).each(function () { this.checked = -1 != (profile.SupportedMediaTypes || '').split(',').indexOf(this.getAttribute('data-value')); }); - $('#chkEnableAlbumArtInDidl', page).checked(profile.EnableAlbumArtInDidl); - $('#chkEnableSingleImageLimit', page).checked(profile.EnableSingleAlbumArtLimit); + $('#chkEnableAlbumArtInDidl', page).checked = profile.EnableAlbumArtInDidl; + $('#chkEnableSingleImageLimit', page).checked = profile.EnableSingleAlbumArtLimit; renderXmlDocumentAttributes(page, profile.XmlRootAttributes || []); var idInfo = profile.Identification || {}; renderIdentificationHeaders(page, idInfo.Headers || []); @@ -51,11 +51,11 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-select', 'emby-butt $('#txtAlbumArtMaxHeight', page).val(profile.MaxAlbumArtHeight || ''); $('#txtIconMaxWidth', page).val(profile.MaxIconWidth || ''); $('#txtIconMaxHeight', page).val(profile.MaxIconHeight || ''); - $('#chkIgnoreTranscodeByteRangeRequests', page).checked(profile.IgnoreTranscodeByteRangeRequests); + $('#chkIgnoreTranscodeByteRangeRequests', page).checked = profile.IgnoreTranscodeByteRangeRequests; $('#txtMaxAllowedBitrate', page).val(profile.MaxStreamingBitrate || ''); $('#txtMusicStreamingTranscodingBitrate', page).val(profile.MusicStreamingTranscodingBitrate || ''); - $('#chkRequiresPlainFolders', page).checked(profile.RequiresPlainFolders); - $('#chkRequiresPlainVideoItems', page).checked(profile.RequiresPlainVideoItems); + $('#chkRequiresPlainFolders', page).checked = profile.RequiresPlainFolders; + $('#chkRequiresPlainVideoItems', page).checked = profile.RequiresPlainVideoItems; $('#txtProtocolInfo', page).val(profile.ProtocolInfo || ''); $('#txtXDlnaCap', page).val(profile.XDlnaCap || ''); $('#txtXDlnaDoc', page).val(profile.XDlnaDoc || ''); @@ -357,9 +357,9 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-select', 'emby-butt $('#txtTranscodingAudioCodec', popup).val(transcodingProfile.AudioCodec || ''); $('#txtTranscodingVideoCodec', popup).val(transcodingProfile.VideoCodec || ''); $('#selectTranscodingProtocol', popup).val(transcodingProfile.Protocol || 'Http'); - $('#chkEnableMpegtsM2TsMode', popup).checked(transcodingProfile.EnableMpegtsM2TsMode || false); - $('#chkEstimateContentLength', popup).checked(transcodingProfile.EstimateContentLength || false); - $('#chkReportByteRangeRequests', popup).checked('Bytes' == transcodingProfile.TranscodeSeekInfo); + $('#chkEnableMpegtsM2TsMode', popup).checked = transcodingProfile.EnableMpegtsM2TsMode || false; + $('#chkEstimateContentLength', popup).checked = transcodingProfile.EstimateContentLength || false; + $('#chkReportByteRangeRequests', popup).checked = 'Bytes' == transcodingProfile.TranscodeSeekInfo; $('.radioTabButton:first', popup).trigger('click'); openPopup(popup[0]); } @@ -376,9 +376,9 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-select', 'emby-butt currentSubProfile.VideoCodec = $('#txtTranscodingVideoCodec', page).val(); currentSubProfile.Protocol = $('#selectTranscodingProtocol', page).val(); currentSubProfile.Context = 'Streaming'; - currentSubProfile.EnableMpegtsM2TsMode = $('#chkEnableMpegtsM2TsMode', page).checked(); - currentSubProfile.EstimateContentLength = $('#chkEstimateContentLength', page).checked(); - currentSubProfile.TranscodeSeekInfo = $('#chkReportByteRangeRequests', page).checked() ? 'Bytes' : 'Auto'; + currentSubProfile.EnableMpegtsM2TsMode = $('#chkEnableMpegtsM2TsMode', page).checked; + currentSubProfile.EstimateContentLength = $('#chkEstimateContentLength', page).checked; + currentSubProfile.TranscodeSeekInfo = $('#chkReportByteRangeRequests', page).checked ? 'Bytes' : 'Auto'; if (isSubProfileNew) { currentProfile.TranscodingProfiles.push(currentSubProfile); @@ -647,8 +647,8 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-select', 'emby-butt function updateProfile(page, profile) { profile.Name = $('#txtName', page).val(); - profile.EnableAlbumArtInDidl = $('#chkEnableAlbumArtInDidl', page).checked(); - profile.EnableSingleAlbumArtLimit = $('#chkEnableSingleImageLimit', page).checked(); + profile.EnableAlbumArtInDidl = $('#chkEnableAlbumArtInDidl', page).checked; + profile.EnableSingleAlbumArtLimit = $('#chkEnableSingleImageLimit', page).checked; profile.SupportedMediaTypes = $('.chkMediaType:checked', page).get().map(function (c) { return c.getAttribute('data-value'); }).join(','); @@ -675,9 +675,9 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-select', 'emby-butt profile.MaxAlbumArtHeight = $('#txtAlbumArtMaxHeight', page).val(); profile.MaxIconWidth = $('#txtIconMaxWidth', page).val(); profile.MaxIconHeight = $('#txtIconMaxHeight', page).val(); - profile.RequiresPlainFolders = $('#chkRequiresPlainFolders', page).checked(); - profile.RequiresPlainVideoItems = $('#chkRequiresPlainVideoItems', page).checked(); - profile.IgnoreTranscodeByteRangeRequests = $('#chkIgnoreTranscodeByteRangeRequests', page).checked(); + profile.RequiresPlainFolders = $('#chkRequiresPlainFolders', page).checked; + profile.RequiresPlainVideoItems = $('#chkRequiresPlainVideoItems', page).checked; + profile.IgnoreTranscodeByteRangeRequests = $('#chkIgnoreTranscodeByteRangeRequests', page).checked; profile.MaxStreamingBitrate = $('#txtMaxAllowedBitrate', page).val(); profile.MusicStreamingTranscodingBitrate = $('#txtMusicStreamingTranscodingBitrate', page).val(); profile.ProtocolInfo = $('#txtProtocolInfo', page).val(); diff --git a/src/controllers/dashboard/dlna/settings.js b/src/controllers/dashboard/dlna/settings.js index a818002d01..6cca2fd348 100644 --- a/src/controllers/dashboard/dlna/settings.js +++ b/src/controllers/dashboard/dlna/settings.js @@ -1,12 +1,12 @@ -define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function ($, loading, libraryMenu, globalize) { +define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, libraryMenu, globalize) { 'use strict'; function loadPage(page, config, users) { page.querySelector('#chkEnablePlayTo').checked = config.EnablePlayTo; page.querySelector('#chkEnableDlnaDebugLogging').checked = config.EnableDebugLog; $('#txtClientDiscoveryInterval', page).val(config.ClientDiscoveryIntervalSeconds); - $('#chkEnableServer', page).checked(config.EnableServer); - $('#chkBlastAliveMessages', page).checked(config.BlastAliveMessages); + $('#chkEnableServer', page).checked = config.EnableServer; + $('#chkBlastAliveMessages', page).checked = config.BlastAliveMessages; $('#txtBlastInterval', page).val(config.BlastAliveMessageIntervalSeconds); var usersHtml = users.map(function (u) { return ''; @@ -22,8 +22,8 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function config.EnablePlayTo = form.querySelector('#chkEnablePlayTo').checked; config.EnableDebugLog = form.querySelector('#chkEnableDlnaDebugLogging').checked; config.ClientDiscoveryIntervalSeconds = $('#txtClientDiscoveryInterval', form).val(); - config.EnableServer = $('#chkEnableServer', form).checked(); - config.BlastAliveMessages = $('#chkBlastAliveMessages', form).checked(); + config.EnableServer = $('#chkEnableServer', form).checked + config.BlastAliveMessages = $('#chkBlastAliveMessages', form).checked; config.BlastAliveMessageIntervalSeconds = $('#txtBlastInterval', form).val(); config.DefaultUserId = $('#selectUser', form).val(); ApiClient.updateNamedConfiguration('dlna', config).then(Dashboard.processServerConfigurationUpdateResult); diff --git a/src/controllers/dashboard/general.js b/src/controllers/dashboard/general.js index 98ca260f21..f215ace28b 100644 --- a/src/controllers/dashboard/general.js +++ b/src/controllers/dashboard/general.js @@ -1,16 +1,8 @@ -define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-checkbox', 'emby-textarea', 'emby-input', 'emby-select', 'emby-button'], function ($, loading, globalize) { +define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-textarea', 'emby-input', 'emby-select', 'emby-button'], function ($, loading, globalize) { 'use strict'; function loadPage(page, config, languageOptions, systemInfo) { page.querySelector('#txtServerName').value = systemInfo.ServerName; - $('#chkAutoRunWebApp', page).checked(config.AutoRunWebApp); - - if (systemInfo.CanLaunchWebBrowser) { - page.querySelector('#fldAutoRunWebApp').classList.remove('hide'); - } else { - page.querySelector('#fldAutoRunWebApp').classList.add('hide'); - } - page.querySelector('#txtCachePath').value = systemInfo.CachePath || ''; $('#txtMetadataPath', page).val(systemInfo.InternalMetadataPath || ''); $('#txtMetadataNetworkPath', page).val(systemInfo.MetadataNetworkPath || ''); @@ -18,11 +10,6 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-checkbox', 'emby-te return ''; })).val(config.UICulture); currentLanguage = config.UICulture; - if (systemInfo.CanSelfRestart || systemInfo.CanSelfUpdate) { - $('.autoUpdatesContainer', page).removeClass('hide'); - } else { - $('.autoUpdatesContainer', page).addClass('hide'); - } loading.hide(); } @@ -38,7 +25,6 @@ define(['jQuery', 'loading', 'globalize', 'fnchecked', 'emby-checkbox', 'emby-te config.MetadataPath = $('#txtMetadataPath', form).val(); config.MetadataNetworkPath = $('#txtMetadataNetworkPath', form).val(); var requiresReload = config.UICulture !== currentLanguage; - config.AutoRunWebApp = $('#chkAutoRunWebApp', form).checked(); ApiClient.updateServerConfiguration(config).then(function() { ApiClient.getNamedConfiguration(brandingConfigKey).then(function(brandingConfig) { brandingConfig.LoginDisclaimer = form.querySelector('#txtLoginDisclaimer').value; diff --git a/src/controllers/dashboard/notifications/notification.js b/src/controllers/dashboard/notifications/notification.js index 90d453cdae..3d9abc7675 100644 --- a/src/controllers/dashboard/notifications/notification.js +++ b/src/controllers/dashboard/notifications/notification.js @@ -1,4 +1,4 @@ -define(['jQuery', 'emby-checkbox', 'fnchecked'], function ($) { +define(['jQuery', 'emby-checkbox'], function ($) { 'use strict'; function fillItems(elem, items, cssClass, idPrefix, currentList, isEnabledList) { @@ -50,7 +50,7 @@ define(['jQuery', 'emby-checkbox', 'fnchecked'], function ($) { fillItems($('.monitorUsersList', page), users, 'chkMonitor', 'chkMonitor', notificationConfig.DisabledMonitorUsers); fillItems($('.sendToUsersList', page), users, 'chkSendTo', 'chkSendTo', notificationConfig.SendToUsers, true); fillItems($('.servicesList', page), services, 'chkService', 'chkService', notificationConfig.DisabledServices); - $('#chkEnabled', page).checked(notificationConfig.Enabled || false); + $('#chkEnabled', page).checked = notificationConfig.Enabled || false; $('#selectUsers', page).val(notificationConfig.SendToUserMode).trigger('change'); }); } @@ -76,7 +76,7 @@ define(['jQuery', 'emby-checkbox', 'fnchecked'], function ($) { types.filter(function (n) { return n.Type == type; })[0]; - notificationConfig.Enabled = $('#chkEnabled', page).checked(); + notificationConfig.Enabled = $('#chkEnabled', page).checked; notificationConfig.SendToUserMode = $('#selectUsers', page).val(); notificationConfig.DisabledMonitorUsers = $('.chkMonitor', page).get().filter(function (c) { return !c.checked; diff --git a/src/controllers/dashboard/users/useredit.js b/src/controllers/dashboard/users/useredit.js index 21ed60cfa9..f60a1b3c84 100644 --- a/src/controllers/dashboard/users/useredit.js +++ b/src/controllers/dashboard/users/useredit.js @@ -1,4 +1,4 @@ -define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function ($, loading, libraryMenu, globalize) { +define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, libraryMenu, globalize) { 'use strict'; function loadDeleteFolders(page, user, mediaFolders) { @@ -27,7 +27,7 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function } $('.deleteAccess', page).html(html).trigger('create'); - $('#chkEnableDeleteAllFolders', page).checked(user.Policy.EnableContentDeletion).trigger('change'); + $('#chkEnableDeleteAllFolders', page).checked = user.Policy.EnableContentDeletion; }); } @@ -85,23 +85,23 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function libraryMenu.setTitle(user.Name); page.querySelector('.username').innerHTML = user.Name; $('#txtUserName', page).val(user.Name); - $('#chkIsAdmin', page).checked(user.Policy.IsAdministrator); - $('#chkDisabled', page).checked(user.Policy.IsDisabled); - $('#chkIsHidden', page).checked(user.Policy.IsHidden); - $('#chkRemoteControlSharedDevices', page).checked(user.Policy.EnableSharedDeviceControl); - $('#chkEnableRemoteControlOtherUsers', page).checked(user.Policy.EnableRemoteControlOfOtherUsers); - $('#chkEnableDownloading', page).checked(user.Policy.EnableContentDownloading); - $('#chkManageLiveTv', page).checked(user.Policy.EnableLiveTvManagement); - $('#chkEnableLiveTvAccess', page).checked(user.Policy.EnableLiveTvAccess); - $('#chkEnableMediaPlayback', page).checked(user.Policy.EnableMediaPlayback); - $('#chkEnableAudioPlaybackTranscoding', page).checked(user.Policy.EnableAudioPlaybackTranscoding); - $('#chkEnableVideoPlaybackTranscoding', page).checked(user.Policy.EnableVideoPlaybackTranscoding); - $('#chkEnableVideoPlaybackRemuxing', page).checked(user.Policy.EnablePlaybackRemuxing); - $('#chkForceRemoteSourceTranscoding', page).checked(user.Policy.ForceRemoteSourceTranscoding); - $('#chkRemoteAccess', page).checked(null == user.Policy.EnableRemoteAccess || user.Policy.EnableRemoteAccess); - $('#chkEnableSyncTranscoding', page).checked(user.Policy.EnableSyncTranscoding); - $('#chkEnableConversion', page).checked(user.Policy.EnableMediaConversion || false); - $('#chkEnableSharing', page).checked(user.Policy.EnablePublicSharing); + $('#chkIsAdmin', page).checked = user.Policy.IsAdministrator; + $('#chkDisabled', page).checked = user.Policy.IsDisabled; + $('#chkIsHidden', page).checked = user.Policy.IsHidden; + $('#chkRemoteControlSharedDevices', page).checked = user.Policy.EnableSharedDeviceControl; + $('#chkEnableRemoteControlOtherUsers', page).checked = user.Policy.EnableRemoteControlOfOtherUsers; + $('#chkEnableDownloading', page).checked = user.Policy.EnableContentDownloading; + $('#chkManageLiveTv', page).checked = user.Policy.EnableLiveTvManagement; + $('#chkEnableLiveTvAccess', page).checked = user.Policy.EnableLiveTvAccess; + $('#chkEnableMediaPlayback', page).checked = user.Policy.EnableMediaPlayback; + $('#chkEnableAudioPlaybackTranscoding', page).checked = user.Policy.EnableAudioPlaybackTranscoding; + $('#chkEnableVideoPlaybackTranscoding', page).checked = user.Policy.EnableVideoPlaybackTranscoding; + $('#chkEnableVideoPlaybackRemuxing', page).checked = user.Policy.EnablePlaybackRemuxing; + $('#chkForceRemoteSourceTranscoding', page).checked = user.Policy.ForceRemoteSourceTranscoding; + $('#chkRemoteAccess', page).checked = null == user.Policy.EnableRemoteAccess || user.Policy.EnableRemoteAccess; + $('#chkEnableSyncTranscoding', page).checked = user.Policy.EnableSyncTranscoding; + $('#chkEnableConversion', page).checked = user.Policy.EnableMediaConversion || false; + $('#chkEnableSharing', page).checked = user.Policy.EnablePublicSharing; $('#txtRemoteClientBitrateLimit', page).val(user.Policy.RemoteClientBitrateLimit / 1e6 || ''); $('#txtLoginAttemptsBeforeLockout', page).val(user.Policy.LoginAttemptsBeforeLockout || '0'); loading.hide(); @@ -118,28 +118,28 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function function saveUser(user, page) { user.Name = $('#txtUserName', page).val(); - user.Policy.IsAdministrator = $('#chkIsAdmin', page).checked(); - user.Policy.IsHidden = $('#chkIsHidden', page).checked(); - user.Policy.IsDisabled = $('#chkDisabled', page).checked(); - user.Policy.EnableRemoteControlOfOtherUsers = $('#chkEnableRemoteControlOtherUsers', page).checked(); - user.Policy.EnableLiveTvManagement = $('#chkManageLiveTv', page).checked(); - user.Policy.EnableLiveTvAccess = $('#chkEnableLiveTvAccess', page).checked(); - user.Policy.EnableSharedDeviceControl = $('#chkRemoteControlSharedDevices', page).checked(); - user.Policy.EnableMediaPlayback = $('#chkEnableMediaPlayback', page).checked(); - user.Policy.EnableAudioPlaybackTranscoding = $('#chkEnableAudioPlaybackTranscoding', page).checked(); - user.Policy.EnableVideoPlaybackTranscoding = $('#chkEnableVideoPlaybackTranscoding', page).checked(); - user.Policy.EnablePlaybackRemuxing = $('#chkEnableVideoPlaybackRemuxing', page).checked(); - user.Policy.ForceRemoteSourceTranscoding = $('#chkForceRemoteSourceTranscoding', page).checked(); - user.Policy.EnableContentDownloading = $('#chkEnableDownloading', page).checked(); - user.Policy.EnableSyncTranscoding = $('#chkEnableSyncTranscoding', page).checked(); - user.Policy.EnableMediaConversion = $('#chkEnableConversion', page).checked(); - user.Policy.EnablePublicSharing = $('#chkEnableSharing', page).checked(); - user.Policy.EnableRemoteAccess = $('#chkRemoteAccess', page).checked(); + user.Policy.IsAdministrator = $('#chkIsAdmin', page).checked; + user.Policy.IsHidden = $('#chkIsHidden', page).checked; + user.Policy.IsDisabled = $('#chkDisabled', page).checked; + user.Policy.EnableRemoteControlOfOtherUsers = $('#chkEnableRemoteControlOtherUsers', page).checked; + user.Policy.EnableLiveTvManagement = $('#chkManageLiveTv', page).checked; + user.Policy.EnableLiveTvAccess = $('#chkEnableLiveTvAccess', page).checked; + user.Policy.EnableSharedDeviceControl = $('#chkRemoteControlSharedDevices', page).checked; + user.Policy.EnableMediaPlayback = $('#chkEnableMediaPlayback', page).checked; + user.Policy.EnableAudioPlaybackTranscoding = $('#chkEnableAudioPlaybackTranscoding', page).checked; + user.Policy.EnableVideoPlaybackTranscoding = $('#chkEnableVideoPlaybackTranscoding', page).checked; + user.Policy.EnablePlaybackRemuxing = $('#chkEnableVideoPlaybackRemuxing', page).checked; + user.Policy.ForceRemoteSourceTranscoding = $('#chkForceRemoteSourceTranscoding', page).checked; + user.Policy.EnableContentDownloading = $('#chkEnableDownloading', page).checked; + user.Policy.EnableSyncTranscoding = $('#chkEnableSyncTranscoding', page).checked; + user.Policy.EnableMediaConversion = $('#chkEnableConversion', page).checked; + user.Policy.EnablePublicSharing = $('#chkEnableSharing', page).checked; + user.Policy.EnableRemoteAccess = $('#chkRemoteAccess', page).checked; user.Policy.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', page).val() || '0')); user.Policy.LoginAttemptsBeforeLockout = parseInt($('#txtLoginAttemptsBeforeLockout', page).val() || '0'); user.Policy.AuthenticationProviderId = page.querySelector('.selectLoginProvider').value; user.Policy.PasswordResetProviderId = page.querySelector('.selectPasswordResetProvider').value; - user.Policy.EnableContentDeletion = $('#chkEnableDeleteAllFolders', page).checked(); + user.Policy.EnableContentDeletion = $('#chkEnableDeleteAllFolders', page).checked; user.Policy.EnableContentDeletionFromFolders = user.Policy.EnableContentDeletion ? [] : $('.chkFolder', page).get().filter(function (c) { return c.checked; }).map(function (c) { diff --git a/src/controllers/dashboard/users/userlibraryaccess.js b/src/controllers/dashboard/users/userlibraryaccess.js index 95664c1eb3..1fdb6cb599 100644 --- a/src/controllers/dashboard/users/userlibraryaccess.js +++ b/src/controllers/dashboard/users/userlibraryaccess.js @@ -1,4 +1,4 @@ -define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function ($, loading, libraryMenu, globalize) { +define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, libraryMenu, globalize) { 'use strict'; function triggerChange(select) { @@ -47,7 +47,7 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function $('.channelAccessContainer', page).hide(); } - $('#chkEnableAllChannels', page).checked(user.Policy.EnableAllChannels).trigger('change'); + $('#chkEnableAllChannels', page).checked = user.Policy.EnableAllChannels; } function loadDevices(page, user, devices) { @@ -63,7 +63,7 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function html += '
'; $('.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 0033/1458] 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 0034/1458] 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 0035/1458] 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 0036/1458] 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 0037/1458] 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 0038/1458] 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 0039/1458] 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 0040/1458] 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 0041/1458] 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 6428bb1ad59214e86f5c144b3c074de84a6035f3 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 26 May 2020 21:53:49 +0200 Subject: [PATCH 0042/1458] 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 0043/1458] 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 0044/1458] 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 0045/1458] 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 c0280bf28d1e47cdadd0f4fa882ffb2e4b598e3a Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 27 May 2020 12:12:46 +0200 Subject: [PATCH 0046/1458] 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 0070/1458] 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 0071/1458] 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 0072/1458] 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 0073/1458] 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 0074/1458] 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 0075/1458] 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 0076/1458] 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 0077/1458] 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 0078/1458] 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 0176/1458] 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 0177/1458] 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 0178/1458] 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 0181/1458] 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 @@
-
+
+
+