From c8c027d0fcc1bdb1b03a3f68e740658c4cf2724a Mon Sep 17 00:00:00 2001 From: npeter83 Date: Mon, 6 Jul 2026 08:45:30 +0200 Subject: [PATCH] fix(plex): play subtitles as WebVTT tracks (external sidecar subs no longer crash) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Selecting a subtitle restarted the HLS session with `-map 0:s:{ord}`, assuming an EMBEDDED stream. Films whose subs are external sidecar .srt files (Plex reports them, but they aren't in the mkv) matched no stream; the master-playlist's declared subtitle group then made ffmpeg fail → "Playback couldn't start". Subtitles now go through a new GET /api/plex/subtitle/{rk}/{ord} → text/vtt (external subs fetched from Plex via the stream key + SRT→VTT; embedded text subs extracted with ffmpeg; image subs → 415), served as native {!ready && (
@@ -621,7 +635,7 @@ export default function PlexPlayer({ itemId, onClose }: Props) {
- {detail && (detail.audio_streams.length > 1 || detail.subtitle_streams.length > 0) && ( + {detail && (detail.audio_streams.length > 1 || textSubs.length > 0) && (
- {detail.subtitle_streams.map((s) => ( - - ))} + {textSubs.length > 0 && ( + <> +
+ {t("plex.player.subtitles")} +
+ + {textSubs.map((s) => ( + + ))} + + )}
)}
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 44a99c8..487255a 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -706,7 +706,8 @@ export interface PlexItemDetail { tagline?: string | null; markers: PlexMarker[]; audio_streams: { ord: number; label: string; language?: string | null; default: boolean }[]; - subtitle_streams: { ord: number; label: string; language?: string | null }[]; + // `text`=true → offered as a WebVTT ; image subs (PGS/VobSub) are text=false (hidden). + subtitle_streams: { ord: number; label: string; language?: string | null; codec?: string; text: boolean }[]; status: string; position_seconds: number; show_title?: string | null; @@ -1152,17 +1153,14 @@ export const api = { req(`/api/plex/show/${encodeURIComponent(id)}`), plexItem: (id: string): Promise => req(`/api/plex/item/${encodeURIComponent(id)}`), - plexSession: ( - id: string, - start = 0, - audio?: number | null, - subtitle?: number | null, - ): Promise => { + plexSession: (id: string, start = 0, audio?: number | null): Promise => { const p = new URLSearchParams({ start: String(Math.max(0, Math.floor(start))) }); if (audio != null) p.set("audio", String(audio)); - if (subtitle != null) p.set("subtitle", String(subtitle)); return req(`/api/plex/stream/${encodeURIComponent(id)}/session?${p.toString()}`, { method: "POST" }); }, + // WebVTT URL for a text subtitle track (used directly as a ). Same-origin → cookie-authed. + plexSubtitleUrl: (id: string, ord: number): string => + `/api/plex/subtitle/${encodeURIComponent(id)}/${ord}`, plexProgress: (id: string, position_seconds: number, duration_seconds: number): Promise => req(`/api/plex/item/${encodeURIComponent(id)}/progress`, { method: "POST", diff --git a/frontend/src/lib/releaseNotes.ts b/frontend/src/lib/releaseNotes.ts index 4131761..1df6e07 100644 --- a/frontend/src/lib/releaseNotes.ts +++ b/frontend/src/lib/releaseNotes.ts @@ -14,6 +14,14 @@ export interface ReleaseEntry { } export const RELEASE_NOTES: ReleaseEntry[] = [ + { + version: "0.28.3", + date: "2026-07-06", + summary: "Fix: Plex subtitles that wouldn't play — now instant, no restart.", + features: [ + "Plex subtitles: fixed playback failing (\"Playback couldn't start\") when picking a subtitle on films whose subtitles are external sidecar files (very common) — those aren't inside the video, so the old approach couldn't mux them. Subtitles are now served as standalone tracks the browser overlays, so turning one on (or switching) is instant and no longer restarts the video. Image-based subtitles (Blu-ray/DVD) are hidden for now (they can't be shown as text yet).", + ], + }, { version: "0.28.2", date: "2026-07-06",