fix(plex): subtitles survive hls.js and a remounted track

hls.js emptied the cues of every text track on the element when a manifest
loaded or the media attached, our own <track> included, so a VTT that loaded
first showed nothing for the session; its subtitle and caption controllers are
no longer built. A remounted default <track> was switched to showing by the
browser after the hide had run, drawing the cues natively under ours; the hide
now follows every added track and mode change.
This commit is contained in:
2026-10-08 19:35:47 +02:00
parent dae0b80f6a
commit 57a81c5643
4 changed files with 115 additions and 13 deletions
+18 -7
View File
@@ -852,14 +852,25 @@ export function usePlexTracks(opts: {
// sessionK is already lag-compensated (see HlsEngine), so it is the true content-zero the VTT
// must be shifted by; the user offset nudges on top.
const subTrackOffset = sessionK - subOffApplied;
// Force the single mounted track to "hidden": loaded and its cues tracked (its VTT loads on mount;
// `default` alone can be flaky after a remount), but drawn by PlexSubtitles, not by the browser.
// Re-runs when the selection or the shifted-track identity changes (fresh <track>).
// Keep every track "hidden": loaded and its cues tracked, but drawn by PlexSubtitles, not by the
// browser. Not once per selection: the <track> is remounted on its own key (a grown window, a
// retry), and the browser switches a fresh `default` track to "showing" a task later — which drew
// the cues a second time, natively, under ours. So it is re-applied on every track the list gains
// and on every mode change the browser makes.
useEffect(() => {
const video = videoRef.current;
if (!video) return;
for (const tr of Array.from(video.textTracks)) tr.mode = "hidden";
}, [subOrd, id, subTrackOffset, videoRef]);
const tracks = videoRef.current?.textTracks;
if (!tracks) return;
const hide = () => {
for (const tr of Array.from(tracks)) if (tr.mode !== "hidden") tr.mode = "hidden";
};
hide();
tracks.addEventListener("addtrack", hide);
tracks.addEventListener("change", hide);
return () => {
tracks.removeEventListener("addtrack", hide);
tracks.removeEventListener("change", hide);
};
}, [subOrd, id, videoRef]);
return {
audioOrd,