diff --git a/k-tv-backend/infra/src/jellyfin.rs b/k-tv-backend/infra/src/jellyfin.rs index a3ab53a..46b756a 100644 --- a/k-tv-backend/infra/src/jellyfin.rs +++ b/k-tv-backend/infra/src/jellyfin.rs @@ -158,20 +158,18 @@ impl IMediaProvider for JellyfinMediaProvider { /// Build an HLS stream URL for a Jellyfin item. /// - /// Returns a `master.m3u8` playlist URL. HLS is preferred over a single - /// MP4 stream because hls.js `startPosition` can seek to the broadcast - /// offset before the first segment fetch, without byte-range seeking into - /// an in-progress transcode. - /// - /// `allowVideoStreamCopy=true` and `allowAudioStreamCopy=true` tell - /// Jellyfin to remux (not re-encode) content that is already H.264/AAC, - /// avoiding transcode cache file buildup. Content in other codecs will - /// still be transcoded to H.264/AAC for browser compatibility. + /// Returns a `master.m3u8` playlist URL. Jellyfin transcodes to H.264/AAC + /// segments on the fly. HLS is preferred over a single MP4 stream because + /// `StartTimeTicks` works reliably with HLS — each segment is independent, + /// so Jellyfin can begin the playlist at the correct broadcast offset + /// without needing to byte-range seek into an in-progress transcode. /// /// The API key is embedded so the player needs no separate auth header. + /// The caller (stream proxy route) appends `StartTimeTicks` when there is + /// a non-zero broadcast offset. async fn get_stream_url(&self, item_id: &MediaItemId) -> DomainResult { Ok(format!( - "{}/Videos/{}/master.m3u8?videoCodec=h264&audioCodec=aac&allowVideoStreamCopy=true&allowAudioStreamCopy=true&mediaSourceId={}&api_key={}", + "{}/Videos/{}/master.m3u8?videoCodec=h264&audioCodec=aac&VideoBitRate=40000000&mediaSourceId={}&api_key={}", self.config.base_url, item_id.as_ref(), item_id.as_ref(),