From f06cd961d5ecfbf5ca3e007814b170c867fdeecd Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Fri, 17 Jun 2022 00:17:10 +0300 Subject: [PATCH] Add maximum allowed video resolution selector --- src/components/apphost.js | 3 +- .../playbackSettings/playbackSettings.js | 50 +++++++++++++++++-- .../playbackSettings.template.html | 13 +++++ src/components/qualityOptions.js | 5 +- src/scripts/settings/appSettings.js | 13 +++++ src/strings/en-us.json | 2 + 6 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/components/apphost.js b/src/components/apphost.js index eeef967eb7..4afb5c2f0b 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -39,7 +39,8 @@ function getDeviceProfile(item) { profile = profileBuilder(builderOpts); } - const maxTranscodingVideoWidth = appHost.screen()?.maxAllowedWidth; + const maxVideoWidth = appSettings.maxVideoWidth(); + const maxTranscodingVideoWidth = maxVideoWidth < 0 ? appHost.screen()?.maxAllowedWidth : maxVideoWidth; if (maxTranscodingVideoWidth) { profile.TranscodingProfiles.forEach((transcodingProfile) => { diff --git a/src/components/playbackSettings/playbackSettings.js b/src/components/playbackSettings/playbackSettings.js index 0de42c6c18..6eea27a57d 100644 --- a/src/components/playbackSettings/playbackSettings.js +++ b/src/components/playbackSettings/playbackSettings.js @@ -41,7 +41,7 @@ import template from './playbackSettings.template.html'; select.innerHTML = html; } - function setMaxBitrateIntoField(select, isInNetwork, mediatype) { + function fillQuality(select, isInNetwork, mediatype, maxVideoWidth) { const options = mediatype === 'Audio' ? qualityoptions.getAudioQualityOptions({ currentMaxBitrate: appSettings.maxStreamingBitrate(isInNetwork, mediatype), @@ -52,7 +52,8 @@ import template from './playbackSettings.template.html'; currentMaxBitrate: appSettings.maxStreamingBitrate(isInNetwork, mediatype), isAutomaticBitrateEnabled: appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype), - enableAuto: true + enableAuto: true, + maxVideoWidth }); @@ -60,6 +61,10 @@ import template from './playbackSettings.template.html'; // render empty string instead of 0 for the auto option return ``; }).join(''); + } + + function setMaxBitrateIntoField(select, isInNetwork, mediatype) { + fillQuality(select, isInNetwork, mediatype); if (appSettings.enableAutomaticBitrateDetection(isInNetwork, mediatype)) { select.value = ''; @@ -68,12 +73,13 @@ import template from './playbackSettings.template.html'; } } - function fillChromecastQuality(select) { + function fillChromecastQuality(select, maxVideoWidth) { const options = qualityoptions.getVideoQualityOptions({ currentMaxBitrate: appSettings.maxChromecastBitrate(), isAutomaticBitrateEnabled: !appSettings.maxChromecastBitrate(), - enableAuto: true + enableAuto: true, + maxVideoWidth }); select.innerHTML = options.map(i => { @@ -192,6 +198,9 @@ import template from './playbackSettings.template.html'; const selectChromecastVersion = context.querySelector('.selectChromecastVersion'); selectChromecastVersion.value = userSettings.chromecastVersion(); + const selectLabelMaxVideoWidth = context.querySelector('.selectLabelMaxVideoWidth'); + selectLabelMaxVideoWidth.value = appSettings.maxVideoWidth(); + const selectSkipForwardLength = context.querySelector('.selectSkipForwardLength'); fillSkipLengths(selectSkipForwardLength); selectSkipForwardLength.value = userSettings.skipForwardLength(); @@ -209,6 +218,7 @@ import template from './playbackSettings.template.html'; appSettings.enableSystemExternalPlayers(context.querySelector('.chkExternalVideoPlayer').checked); appSettings.maxChromecastBitrate(context.querySelector('.selectChromecastVideoQuality').value); + appSettings.maxVideoWidth(context.querySelector('.selectLabelMaxVideoWidth').value); setMaxBitrateFromField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video'); setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video'); @@ -247,6 +257,36 @@ import template from './playbackSettings.template.html'; }); } + function setSelectValue(select, value, defaultValue) { + select.value = value; + + if (select.selectedIndex < 0) { + select.value = defaultValue; + } + } + + function onMaxVideoWidthChange(e) { + const context = this.options.element; + + const selectVideoInNetworkQuality = context.querySelector('.selectVideoInNetworkQuality'); + const selectVideoInternetQuality = context.querySelector('.selectVideoInternetQuality'); + const selectChromecastVideoQuality = context.querySelector('.selectChromecastVideoQuality'); + + const selectVideoInNetworkQualityValue = selectVideoInNetworkQuality.value; + const selectVideoInternetQualityValue = selectVideoInternetQuality.value; + const selectChromecastVideoQualityValue = selectChromecastVideoQuality.value; + + const maxVideoWidth = parseInt(e.target.value || '0', 10) || 0; + + fillQuality(selectVideoInNetworkQuality, true, 'Video', maxVideoWidth); + fillQuality(selectVideoInternetQuality, false, 'Video', maxVideoWidth); + fillChromecastQuality(selectChromecastVideoQuality, maxVideoWidth); + + setSelectValue(selectVideoInNetworkQuality, selectVideoInNetworkQualityValue, ''); + setSelectValue(selectVideoInternetQuality, selectVideoInternetQualityValue, ''); + setSelectValue(selectChromecastVideoQuality, selectChromecastVideoQualityValue, ''); + } + function onSubmit(e) { const self = this; const apiClient = ServerConnections.getApiClient(self.options.serverId); @@ -274,6 +314,8 @@ import template from './playbackSettings.template.html'; options.element.querySelector('.btnSave').classList.remove('hide'); } + options.element.querySelector('.selectLabelMaxVideoWidth').addEventListener('change', onMaxVideoWidthChange.bind(self)); + self.loadData(); if (options.autoFocus) { diff --git a/src/components/playbackSettings/playbackSettings.template.html b/src/components/playbackSettings/playbackSettings.template.html index 19782334e1..c1a93e0e64 100644 --- a/src/components/playbackSettings/playbackSettings.template.html +++ b/src/components/playbackSettings/playbackSettings.template.html @@ -41,6 +41,19 @@
+ +
+ +
diff --git a/src/components/qualityOptions.js b/src/components/qualityOptions.js index a185a5f5fa..bf59ad1669 100644 --- a/src/components/qualityOptions.js +++ b/src/components/qualityOptions.js @@ -1,5 +1,6 @@ import { appHost } from '../components/apphost'; import globalize from '../scripts/globalize'; +import appSettings from '../scripts/settings/appSettings'; export function getVideoQualityOptions(options) { const maxStreamingBitrate = options.currentMaxBitrate; @@ -12,7 +13,9 @@ export function getVideoQualityOptions(options) { videoWidth = videoHeight * (16 / 9); } - const hostScreenWidth = appHost.screen()?.maxAllowedWidth || 4096; + const maxVideoWidth = options.maxVideoWidth == null ? appSettings.maxVideoWidth() : options.maxVideoWidth; + + const hostScreenWidth = (maxVideoWidth < 0 ? appHost.screen()?.maxAllowedWidth : maxVideoWidth) || 4096; const maxAllowedWidth = videoWidth || 4096; const qualityOptions = []; diff --git a/src/scripts/settings/appSettings.js b/src/scripts/settings/appSettings.js index 7b59c35f3c..ed909d3e09 100644 --- a/src/scripts/settings/appSettings.js +++ b/src/scripts/settings/appSettings.js @@ -92,6 +92,19 @@ class AppSettings { return val ? parseInt(val) : null; } + /** + * Get or set 'Maximum video width' + * @param {number|undefined} val - Maximum video width or undefined. + * @return {number} Maximum video width. + */ + maxVideoWidth(val) { + if (val !== undefined) { + return this.set('maxVideoWidth', val.toString()); + } + + return parseInt(this.get('maxVideoWidth') || '0', 10) || 0; + } + set(name, value, userId) { const currentValue = this.get(name, userId); AppStorage.setItem(this.#getKey(name, userId), value); diff --git a/src/strings/en-us.json b/src/strings/en-us.json index af17c594ea..d4ff4a942d 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -708,6 +708,7 @@ "LabelLibraryPageSizeHelp": "Set the amount of items to show on a library page. Set to 0 in order to disable paging.", "LabelMaxDaysForNextUp": "Max days in 'Next Up':", "LabelMaxDaysForNextUpHelp": "Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.", + "LabelMaxVideoResolution": "Maximum Allowed Video Transcoding Resolution", "LabelLineup": "Lineup:", "LabelLocalCustomCss": "Custom CSS code for styling which applies to this client only. You may want to disable server custom CSS code.", "LabelLocalHttpServerPortNumber": "Local HTTP port number:", @@ -1377,6 +1378,7 @@ "ScanForNewAndUpdatedFiles": "Scan for new and updated files", "ScanLibrary": "Scan library", "Schedule": "Schedule", + "ScreenResolution": "Screen Resolution", "Screenshot": "Screenshot", "Screenshots": "Screenshots", "Search": "Search",