From e48321a2c49d535752d67994ef7e127b43d151f4 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 24 Aug 2024 07:31:25 +0800 Subject: [PATCH 01/10] Add option to always burn in subtitles if transcoding is triggered --- src/components/playback/playbackmanager.js | 14 ++++++++++++-- .../subtitlesettings/subtitlesettings.js | 2 ++ .../subtitlesettings.template.html | 8 ++++++++ src/scripts/settings/appSettings.js | 13 +++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index e14aa062d1..086ab3c937 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -1,4 +1,5 @@ import { PlaybackErrorCode } from '@jellyfin/sdk/lib/generated-client/models/playback-error-code.js'; +import { getMediaInfoApi } from '@jellyfin/sdk/lib/utils/api/media-info-api'; import merge from 'lodash-es/merge'; import Screenfull from 'screenfull'; @@ -22,6 +23,7 @@ import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type' import { MediaError } from 'types/mediaError'; import { getMediaError } from 'utils/mediaError'; +import { toApi } from 'utils/jellyfin-apiclient/compat'; const UNLIMITED_ITEMS = -1; @@ -401,7 +403,7 @@ function setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositio }); } -function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, liveStreamId, options) { +async function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, liveStreamId, options) { if (!itemHelper.isLocalItem(item) && item.MediaType === 'Audio' && !player.useServerPlaybackInfoForAudio) { return Promise.resolve({ MediaSources: [ @@ -427,6 +429,9 @@ function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, StartTimeTicks: options.startPosition || 0 }; + const api = toApi(apiClient); + const mediaInfoApi = getMediaInfoApi(api); + if (options.isPlayback) { query.IsPlayback = true; query.AutoOpenLiveStream = true; @@ -480,7 +485,12 @@ function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, query.DirectPlayProtocols = player.getDirectPlayProtocols(); } - return apiClient.getPlaybackInfo(itemId, query, deviceProfile); + query.AlwaysBurnInSubtitleWhenTranscoding = appSettings.alwaysBurnInSubtitleWhenTranscoding(); + + query.DeviceProfile = deviceProfile; + + const res = await mediaInfoApi.getPostedPlaybackInfo({ itemId: itemId, playbackInfoDto: query }); + return res.data; } function getOptimalMediaSource(apiClient, item, versions) { diff --git a/src/components/subtitlesettings/subtitlesettings.js b/src/components/subtitlesettings/subtitlesettings.js index da548a747c..b7852a79fb 100644 --- a/src/components/subtitlesettings/subtitlesettings.js +++ b/src/components/subtitlesettings/subtitlesettings.js @@ -64,6 +64,7 @@ function loadForm(context, user, userSettings, appearanceSettings, apiClient) { context.querySelector('#chkSubtitleRenderPgs').checked = appSettings.get('subtitlerenderpgs') === 'true'; context.querySelector('#selectSubtitleBurnIn').dispatchEvent(new CustomEvent('change', {})); + context.querySelector('#chkAlwaysBurnInSubtitleWhenTranscoding').checked = appSettings.alwaysBurnInSubtitleWhenTranscoding(); onAppearanceFieldChange({ target: context.querySelector('#selectTextSize') @@ -90,6 +91,7 @@ function save(instance, context, userId, userSettings, apiClient, enableSaveConf appSettings.set('subtitleburnin', context.querySelector('#selectSubtitleBurnIn').value); appSettings.set('subtitlerenderpgs', context.querySelector('#chkSubtitleRenderPgs').checked); + appSettings.alwaysBurnInSubtitleWhenTranscoding(context.querySelector('#chkAlwaysBurnInSubtitleWhenTranscoding').checked); apiClient.getUser(userId).then(function (user) { saveUser(context, user, userSettings, instance.appearanceKey, apiClient).then(function () { diff --git a/src/components/subtitlesettings/subtitlesettings.template.html b/src/components/subtitlesettings/subtitlesettings.template.html index 003a2a4e22..32584fc6da 100644 --- a/src/components/subtitlesettings/subtitlesettings.template.html +++ b/src/components/subtitlesettings/subtitlesettings.template.html @@ -42,6 +42,14 @@ +
+ +
${AlwaysBurnInSubtitleWhenTranscodingHelp}
+
+

${HeaderSubtitleAppearance} diff --git a/src/scripts/settings/appSettings.js b/src/scripts/settings/appSettings.js index cf6cbea284..e10d6053d3 100644 --- a/src/scripts/settings/appSettings.js +++ b/src/scripts/settings/appSettings.js @@ -156,6 +156,19 @@ class AppSettings { return this.get('preferredTranscodeVideoAudioCodec') || ''; } + /** + * Get or set 'Always burn in subtitle when transcoding' state. + * @param {boolean|undefined} val - Flag to enable 'Always burn in subtitle when transcoding' or undefined. + * @return {boolean} 'Always burn in subtitle when transcoding' state. + */ + alwaysBurnInSubtitleWhenTranscoding(val) { + if (val !== undefined) { + return this.set('alwaysBurnInSubtitleWhenTranscoding', val.toString()); + } + + return toBoolean(this.get('alwaysBurnInSubtitleWhenTranscoding'), false); + } + /** * Get or set 'Enable DTS' state. * @param {boolean|undefined} val - Flag to enable 'Enable DTS' or undefined. From 0dbd659bc48f3b846844d1098d8a2c017b89a6ff Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 24 Aug 2024 14:40:07 +0800 Subject: [PATCH 02/10] Fix localization --- src/strings/en-us.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/strings/en-us.json b/src/strings/en-us.json index a43a7bd8cc..0cf522a0c2 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -34,6 +34,8 @@ "AllowStreamSharingHelp": "Allow Jellyfin to duplicate the mpegts stream from tuner and share this duplicated stream to its clients. This is useful when the tuner has total stream count limit but may also cause playback issues.", "Alternate" : "Alternate", "AlternateDVD" : "Alternate DVD", + "AlwaysBurnInSubtitleWhenTranscoding": "Always burn in subtitle when transcoding", + "AlwaysBurnInSubtitleWhenTranscodingHelp": "Burn in all subtitles when transcoding is triggered. This ensures subtitle synchronization after transcoding at the cost of reduced transcoding speed.", "LabelThrottleDelaySeconds": "Throttle after", "LabelThrottleDelaySecondsHelp": "Time in seconds after which the transcoder will be throttled. Must be large enough for the client to maintain a healthy buffer. Only works if throttling is enabled.", "LabelSegmentKeepSeconds": "Time to keep segments", @@ -84,7 +86,7 @@ "BoxRear": "Box (rear)", "BoxSet": "Box Set", "Browse": "Browse", - "BurnSubtitlesHelp": "Determine if the server should burn in subtitles while transcoding videos. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VobSub, PGS, SUB, IDX, etc.) and certain ASS or SSA subtitles.", + "BurnSubtitlesHelp": "Determine if the server should burn in subtitles. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VobSub, PGS, SUB, IDX, etc.) and certain ASS or SSA subtitles.", "ButtonActivate": "Activate", "ButtonAddImage": "Add Image", "ButtonAddMediaLibrary": "Add Media Library", From 2d12aec9b425da4d768af30a92fbc14dfa4f87c7 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 00:36:29 +0800 Subject: [PATCH 03/10] Only load subtitle when direct play/remux --- src/plugins/htmlVideoPlayer/plugin.js | 52 ++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index 46a3062a11..908248e6a4 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1,6 +1,7 @@ import DOMPurify from 'dompurify'; import browser from '../../scripts/browser'; +import appSettings from '../../scripts/settings/appSettings'; import { appHost } from '../../components/apphost'; import loading from '../../components/loading/loading'; import dom from '../../scripts/dom'; @@ -1566,15 +1567,50 @@ export class HtmlVideoPlayer { return t.Index === streamIndex; })[0]; - this.setTrackForDisplay(this.#mediaElement, track, targetTextTrackIndex); - if (enableNativeTrackSupport(this._currentPlayOptions?.mediaSource, track)) { - if (streamIndex !== -1) { - this.setCueAppearance(); - } + // This play method can only check if it is real direct play, and will mark Remux as Transcode as well + const isDirectPlay = this._currentPlayOptions.playMethod === "DirectPlay"; + const burnInWhenTranscoding = appSettings.alwaysBurnInSubtitleWhenTranscoding(); + + if (!isDirectPlay && burnInWhenTranscoding) { + const apiClient = ServerConnections.getApiClient(this._currentPlayOptions.item.ServerId); + const sessionPromise = apiClient.getSessions({ + deviceId: apiClient.deviceId() + }).then(function (sessions) { + return sessions[0] || {};; + }, function () { + return Promise.resolve({}); + }); + + const player = this; + + sessionPromise.then((s) => { + if (!s.TranscodingInfo || s.TranscodingInfo?.IsVideoDirect) { + player.setTrackForDisplay(player.#mediaElement, track, targetTextTrackIndex); + if (enableNativeTrackSupport(player._currentPlayOptions?.mediaSource, track)) { + if (streamIndex !== -1) { + player.setCueAppearance(); + } + } else { + // null these out to disable the player's native display (handled below) + streamIndex = -1; + track = null; + } + } else { + // unset stream when switching to transcode + player.setTrackForDisplay(player.#mediaElement, null, -1); + } + }) } else { - // null these out to disable the player's native display (handled below) - streamIndex = -1; - track = null; + this.setTrackForDisplay(this.#mediaElement, track, targetTextTrackIndex); + if (enableNativeTrackSupport(this._currentPlayOptions?.mediaSource, track)) { + if (streamIndex !== -1) { + this.setCueAppearance(); + } + } else { + // null these out to disable the player's native display (handled below) + streamIndex = -1; + track = null; + } } } From 040b2d490198271edbdf9b118987b2b296219c9d Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 00:39:24 +0800 Subject: [PATCH 04/10] Fix Lint Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/plugins/htmlVideoPlayer/plugin.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index 908248e6a4..202c197d79 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1568,7 +1568,7 @@ export class HtmlVideoPlayer { })[0]; // This play method can only check if it is real direct play, and will mark Remux as Transcode as well - const isDirectPlay = this._currentPlayOptions.playMethod === "DirectPlay"; + const isDirectPlay = this._currentPlayOptions.playMethod === 'DirectPlay'; const burnInWhenTranscoding = appSettings.alwaysBurnInSubtitleWhenTranscoding(); if (!isDirectPlay && burnInWhenTranscoding) { @@ -1576,7 +1576,7 @@ export class HtmlVideoPlayer { const sessionPromise = apiClient.getSessions({ deviceId: apiClient.deviceId() }).then(function (sessions) { - return sessions[0] || {};; + return sessions[0] || {}; }, function () { return Promise.resolve({}); }); @@ -1599,7 +1599,7 @@ export class HtmlVideoPlayer { // unset stream when switching to transcode player.setTrackForDisplay(player.#mediaElement, null, -1); } - }) + }); } else { this.setTrackForDisplay(this.#mediaElement, track, targetTextTrackIndex); if (enableNativeTrackSupport(this._currentPlayOptions?.mediaSource, track)) { From e4c20df5aebd4b5564577274d7b9f9396356f8fd Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 00:41:33 +0800 Subject: [PATCH 05/10] Remove redundant Promise.resolve --- src/components/playback/playbackmanager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 086ab3c937..5ea1d3440d 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -405,7 +405,7 @@ function setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositio async function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, liveStreamId, options) { if (!itemHelper.isLocalItem(item) && item.MediaType === 'Audio' && !player.useServerPlaybackInfoForAudio) { - return Promise.resolve({ + return { MediaSources: [ { StreamUrl: getAudioStreamUrlFromDeviceProfile(item, deviceProfile, options.maxBitrate, apiClient, options.startPosition), @@ -413,7 +413,7 @@ async function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSour MediaStreams: [], RunTimeTicks: item.RunTimeTicks }] - }); + }; } if (item.PresetMediaSource) { From fc9485c49d66a6dd3f434cdf629fcce210724c6d Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 05:04:19 +0800 Subject: [PATCH 06/10] Remove more Promise.Resolve --- src/components/playback/playbackmanager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 5ea1d3440d..30842b1f77 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -417,9 +417,9 @@ async function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSour } if (item.PresetMediaSource) { - return Promise.resolve({ + return { MediaSources: [item.PresetMediaSource] - }); + }; } const itemId = item.Id; From 1216305992d3b770b2a2448d684736f246227ddd Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 05:46:54 +0800 Subject: [PATCH 07/10] Refactor sessionPromise --- src/plugins/htmlVideoPlayer/plugin.js | 53 ++++++++++++++------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index 202c197d79..a30c53d65e 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1571,47 +1571,48 @@ export class HtmlVideoPlayer { const isDirectPlay = this._currentPlayOptions.playMethod === 'DirectPlay'; const burnInWhenTranscoding = appSettings.alwaysBurnInSubtitleWhenTranscoding(); + let sessionPromise; if (!isDirectPlay && burnInWhenTranscoding) { const apiClient = ServerConnections.getApiClient(this._currentPlayOptions.item.ServerId); - const sessionPromise = apiClient.getSessions({ + sessionPromise = apiClient.getSessions({ deviceId: apiClient.deviceId() }).then(function (sessions) { return sessions[0] || {}; }, function () { return Promise.resolve({}); }); + } else { + sessionPromise = Promise.resolve({}); + } - const player = this; + const player = this; - sessionPromise.then((s) => { - if (!s.TranscodingInfo || s.TranscodingInfo?.IsVideoDirect) { - player.setTrackForDisplay(player.#mediaElement, track, targetTextTrackIndex); - if (enableNativeTrackSupport(player._currentPlayOptions?.mediaSource, track)) { - if (streamIndex !== -1) { - player.setCueAppearance(); - } - } else { - // null these out to disable the player's native display (handled below) - streamIndex = -1; - track = null; + sessionPromise.then((s) => { + if (!s.TranscodingInfo || s.TranscodingInfo?.IsVideoDirect) { + // restore recorded delivery method if any + mediaStreamTextTracks.forEach((t) => { + t.DeliveryMethod = t.realDeliveryMethod ?? t.DeliveryMethod; + }); + player.setTrackForDisplay(player.#mediaElement, track, targetTextTrackIndex); + if (enableNativeTrackSupport(player._currentPlayOptions?.mediaSource, track)) { + if (streamIndex !== -1) { + player.setCueAppearance(); } } else { - // unset stream when switching to transcode - player.setTrackForDisplay(player.#mediaElement, null, -1); - } - }); - } else { - this.setTrackForDisplay(this.#mediaElement, track, targetTextTrackIndex); - if (enableNativeTrackSupport(this._currentPlayOptions?.mediaSource, track)) { - if (streamIndex !== -1) { - this.setCueAppearance(); + // null these out to disable the player's native display (handled below) + streamIndex = -1; + track = null; } } else { - // null these out to disable the player's native display (handled below) - streamIndex = -1; - track = null; + // record the original delivery method and set all delivery method to encode + mediaStreamTextTracks.forEach((t) => { + t.realDeliveryMethod = t.DeliveryMethod; + t.DeliveryMethod = 'Encode'; + }); + // unset stream when switching to transcode + player.setTrackForDisplay(player.#mediaElement, null, -1); } - } + }) } /** From 43e4c2a5176f0726a88652c22ff8315517ff5ccc Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 05:47:50 +0800 Subject: [PATCH 08/10] Better comment --- src/plugins/htmlVideoPlayer/plugin.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index a30c53d65e..f82ee12703 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1605,6 +1605,7 @@ export class HtmlVideoPlayer { } } else { // record the original delivery method and set all delivery method to encode + // this is needed for subtitle track switching to properly reload the video stream mediaStreamTextTracks.forEach((t) => { t.realDeliveryMethod = t.DeliveryMethod; t.DeliveryMethod = 'Encode'; From fb1b9b15bb8165fca9fbcb5461650f955867e487 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 05:50:10 +0800 Subject: [PATCH 09/10] fix lint Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/plugins/htmlVideoPlayer/plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index f82ee12703..8452074907 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1613,7 +1613,7 @@ export class HtmlVideoPlayer { // unset stream when switching to transcode player.setTrackForDisplay(player.#mediaElement, null, -1); } - }) + }); } /** From f05b90ce1a677aba8019adc19989ee42c974f581 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 13:16:52 +0800 Subject: [PATCH 10/10] Fix unnecessary optional chaining Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/plugins/htmlVideoPlayer/plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index 8452074907..0697b65e56 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1588,7 +1588,7 @@ export class HtmlVideoPlayer { const player = this; sessionPromise.then((s) => { - if (!s.TranscodingInfo || s.TranscodingInfo?.IsVideoDirect) { + if (!s.TranscodingInfo || s.TranscodingInfo.IsVideoDirect) { // restore recorded delivery method if any mediaStreamTextTracks.forEach((t) => { t.DeliveryMethod = t.realDeliveryMethod ?? t.DeliveryMethod;