From 92adb593c8312bde0375deedebadebd8e7cbd4fc Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sun, 30 Aug 2026 20:09:00 +0200 Subject: [PATCH] fix(plex): start hls.js at position 0 for the transcode/remux HLS session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our HLS is a growing EVENT playlist with no ENDLIST, which hls.js reads as LIVE. Its default startPosition (-1) then starts at the live edge and hugs it (no forward buffer → rebuffer spinners), and on a resume/seek session the segments' absolute PTS (-copyts, offset by the server's -ss) don't line up with the live-edge timeline → 0 fragments load → permanent stall. The seek-restart model makes fragment 0 the requested content position, so start VOD-style at 0 and buffer forward. Fixes the transcode playback stall/rebuffer found in 0.78.2 UAT. --- frontend/src/components/PlexPlayer.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/PlexPlayer.tsx b/frontend/src/components/PlexPlayer.tsx index 4fc7663b..61124c86 100644 --- a/frontend/src/components/PlexPlayer.tsx +++ b/frontend/src/components/PlexPlayer.tsx @@ -261,7 +261,15 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) { hlsRef.current = null; } if (sess.mode === "hls" && Hls.isSupported()) { - const hls = new Hls({ maxBufferLength: 30, enableWorker: true }); + // startPosition: 0 — our HLS is a growing EVENT playlist with no ENDLIST (see _HLS_TAIL in + // stream.py), which hls.js classifies as LIVE. Its default (startPosition: -1) then starts + // playback at the live edge and hugs it, so it never builds a forward buffer (rebuffer + // spinners), and on a resume/seek session the segments' absolute PTS (-copyts, offset by the + // server's -ss) don't line up with the live-edge timeline → 0 fragments load → permanent + // stall. But the seek-restart model means fragment 0 IS the requested content position + // (`?t=` positioned the server ffmpeg there), so we always want to start VOD-style at 0 and + // buffer forward up to maxBufferLength. Applies to remux + transcode alike (both use copyts). + const hls = new Hls({ maxBufferLength: 30, enableWorker: true, startPosition: 0 }); hlsRef.current = hls; hls.loadSource(`${sess.url}?t=${Math.floor(startAt)}`); hls.attachMedia(video);