From c843bc9fb668d723da01d6017c87c5b0afc4e3f3 Mon Sep 17 00:00:00 2001 From: Delgan Date: Thu, 21 May 2020 15:28:19 +0200 Subject: [PATCH 01/82] 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 0dad2dc99..25e3d91e3 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 5ed2b8f81..3a0e27025 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 9bba23c29..310edc03c 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 654ac2984..2078c6f6a 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 329cc11f9..eb0ae0849 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 dd67b667e..2544590b3 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 75af04035..62793b9a3 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 90a872cc6..b7da635cb 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 95a13d769..c2f6761f5 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 02/82] 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 3a0e27025..5f3becd64 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 2544590b3..33d252c52 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 03/82] 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 eb0ae0849..91b1ddc20 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 2efdc9414651dea8cac711073c2e06237310ec14 Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 21 May 2020 13:52:43 +0200 Subject: [PATCH 04/82] Add blurhash package --- package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index 0dad2dc99..8d0c53c34 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 e68063e68..bb5a8fb7f 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 05/82] 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 d7ba6c6a5..0cd702187 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 d4d4d7f73..73f90cf4f 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 f23b407de..fe450b281 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 2b9422d55..2182452b1 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 06/82] 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 fe450b281..ef13dcc8b 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 07/82] 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 ef13dcc8b..8c7eacc8b 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 2182452b1..d186b1042 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 7587469480fd5d85c42545bd929b6db78c77844d Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 25 May 2020 18:31:51 +0200 Subject: [PATCH 08/82] 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 8c7eacc8b..f9cbb33c6 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 d186b1042..afd7e004e 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 09/82] 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 73f90cf4f..a3c3aa323 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 f9cbb33c6..0ab90c7b9 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 10/82] 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 a3c3aa323..1e8fcf251 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 11/82] 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 1e8fcf251..dc1d7071f 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 12/82] 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 587355b35..b29e51362 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 13/82] 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 dc1d7071f..b00d55593 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 14/82] 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 0ab90c7b9..ea40a9337 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 15/82] 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 b00d55593..7f1a4dc2d 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 16/82] 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 7f1a4dc2d..0c167df55 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 ea40a9337..2ba8e19ff 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 c0280bf28d1e47cdadd0f4fa882ffb2e4b598e3a Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 27 May 2020 12:12:46 +0200 Subject: [PATCH 17/82] 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 b8ab1a9ba..58be97b06 100644 --- a/src/components/displaySettings/displaySettings.template.html +++ b/src/components/displaySettings/displaySettings.template.html @@ -156,6 +156,14 @@
${EnableFastImageFadeInHelp}
+
+ +
${EnableBlurhashHelp}
+
+
-
+
${LabelLibraryPageSizeHelp}
-
+
-
+
${EnableBlurhashHelp}
-
+ +