From 3dd1de3d9c9088448c5960eec9feb360516e533c Mon Sep 17 00:00:00 2001 From: mani Date: Wed, 25 Feb 2026 22:23:24 +0100 Subject: [PATCH] Fix CRT shader: use plain &crtShader=true query param in TranscodingUrl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous approach used streamOptions[crtShader]=true (wrong format). ParseStreamOptions on the server reads IQueryCollection directly and stores keys verbatim — so streamOptions[crtShader] becomes the key, not crtShader, and TryGetValue("crtShader") always returns false. Plain &crtShader=true works because ParseStreamOptions adds any lowercase-starting query param directly to the StreamOptions dict. Also remove the dead PlaybackInfoDto.StreamOptions code — that DTO has no StreamOptions field, so it was silently ignored. Co-Authored-By: Claude Sonnet 4.6 --- src/components/playback/playbackmanager.js | 26 ++++++++++------------ 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 7d923516d4..eb008efaf3 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -470,9 +470,6 @@ async function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSour if (options.allowAudioStreamCopy != null) { query.AllowAudioStreamCopy = options.allowAudioStreamCopy; } - if (options.streamOptions) { - query.StreamOptions = options.streamOptions; - } if (mediaSourceId) { query.MediaSourceId = mediaSourceId; } @@ -1750,14 +1747,6 @@ export class PlaybackManager { const currentPlayOptions = currentItem.playOptions || getDefaultPlayOptions(); - const crtStreamOptions = (function () { - const pd = getPlayerData(player); - if (!pd.enableCrtShader) return undefined; - const opts = { crtShader: 'true' }; - if (pd.crtShadowMask != null) opts.crtShadowMask = String(pd.crtShadowMask); - return opts; - })(); - const options = { maxBitrate, startPosition: ticks, @@ -1767,8 +1756,7 @@ export class PlaybackManager { enableDirectPlay: params.EnableDirectPlay, enableDirectStream: params.EnableDirectStream, allowVideoStreamCopy: params.AllowVideoStreamCopy, - allowAudioStreamCopy: params.AllowAudioStreamCopy, - streamOptions: crtStreamOptions + allowAudioStreamCopy: params.AllowAudioStreamCopy }; getPlaybackInfo(player, apiClient, currentItem, deviceProfile, currentMediaSource.Id, liveStreamId, options).then(function (result) { @@ -2890,7 +2878,17 @@ export class PlaybackManager { playMethod = mediaSource.SupportsDirectPlay ? 'DirectPlay' : 'DirectStream'; } else if (mediaSource.SupportsTranscoding) { - const transcodingUrl = mediaSource.TranscodingUrl; + let transcodingUrl = mediaSource.TranscodingUrl; + + // Append CRT shader options as plain query params so ParseStreamOptions + // on the server picks them up (key must start lowercase, no streamOptions[] wrapper). + if (getPlayerData(player).enableCrtShader) { + transcodingUrl += '&crtShader=true'; + const maskVal = getPlayerData(player).crtShadowMask; + if (maskVal !== undefined && maskVal !== null) { + transcodingUrl += '&crtShadowMask=' + maskVal; + } + } mediaUrl = apiClient.getUrl(transcodingUrl);