From 137977893a5a245ee2a208eadff9ab933cdb2189 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Mon, 14 Sep 2026 16:31:25 +0200 Subject: [PATCH] Render subtitles client-side () instead of asking Emby to burn them in Six rounds of real-server debugging (see docs/architecture.md's player- track-selection section) found three different, unrelated reasons a server-side burn-in could fail - missing fonts, a bitmap format libass fundamentally can't burn, ffmpeg failing outright on an otherwise unremarkable file - none of which this app's own request ever caused or could have fixed, all downstream of the one thing every attempt had in common: asking Emby for a real-time video re-encode. Not needing that re-encode at all closes the whole class of failure instead of chasing each one. - server/src/services/CatalogService.ts: new proxySubtitle asks Emby to convert the track to WebVTT (GET /Videos/{id}/{mediaSourceId}/ Subtitles/{index}/Stream.vtt - a real Emby endpoint their own web client already uses for this) - entirely separate from the video transcode. applyPlaybackOverrides lost its SubtitleStreamIndex/ SubtitleMethod=Encode branch - the video request no longer needs to know a subtitle was picked at all. - server/src/types.ts, client/src/types.ts: PlaybackTrackOption's flag renamed supportsBurnIn -> isTextBased - same underlying fact (image vs text codec), but the name shouldn't keep describing a mechanism this app no longer uses. - server/src/index.ts: new GET /api/catalog/subtitle/:itemId/:subtitleIndex route, grouped with the other cookie-authed media-element routes (a can't send a bearer header either). - client/src/api/embyApi.ts: new subtitleUrl(); streamUrl no longer carries SubtitleStreamIndex at all. - client/src/views/player/Player.ts: the subtitle . */ +export function subtitleUrl(itemId: string, subtitleIndex: number): string { + return `/api/catalog/subtitle/${itemId}/${subtitleIndex}`; +} diff --git a/client/src/types.ts b/client/src/types.ts index 71d5e85..65903b1 100644 --- a/client/src/types.ts +++ b/client/src/types.ts @@ -38,9 +38,11 @@ export interface PlaybackTrackOption { label: string; // Subtitle tracks only (undefined for audio) - false for an image/ // bitmap subtitle (PGS/VobSub/DVB - a Blu-ray/DVD rip's own subtitles, - // typically), which this player's forced burn-in (SubtitleMethod=Encode) - // cannot actually render - see Player.ts's subtitle ` no longer reloads the + video at all (`reloadWithCurrentOptions`, still used for audio/quality, + never touches subtitles now) - `applySubtitleTrack` just removes + whatever `` exists and, for a real pick, adds a fresh one + pointing at `subtitleUrl(itemId, index)`, then sets + `video.textTracks[0].mode = 'showing'` **synchronously**, right after + appending. Deliberately not waiting on the `` element's own + `"load"` event for that: verified against a real browser that it + doesn't fire reliably at all (a known, general `` quirk, not + specific to this app or this fix) - which would have left a + subtitle silently never shown, the exact symptom the *first* round of + this whole saga was originally about. The TextTrack object itself is + registered in `video.textTracks` the moment the element is connected, + before its content has actually loaded, and a browser honors `.mode` + being set that early regardless. Re-applied on every video (re)load, + not just when the subtitle picker itself changes, so switching audio or + quality - which does still reload the video - doesn't drop whatever + subtitle was showing. + +Verified against the real compiled bundle: picking a text track adds +exactly one `` with the right URL and flips it to `showing`; +picking "Aucun" removes it and `video.textTracks` goes back to empty; an +image-coded track is present in the list but disabled, exactly as +`isTextBased: false` says it should be; changing the audio track reloads +the video but the previously-selected subtitle's `` survives it, +same element, same `src`, still `showing`. + ## Assumptions to verify **Confirmed against the real server** (not a guess — actually exercised, diff --git a/server/src/index.ts b/server/src/index.ts index b2ab473..c2fae8a 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -155,11 +155,11 @@ app.get('/api/catalog/libraries/:libraryId/items', requireSession, asyncRoute(as res.json({ items: await CatalogService.getLibraryItems(req.embySession!, param(req, 'libraryId')) }); })); -// Images/video/audio are requested by /