Commit Graph
17 Commits
Author SHA1 Message Date
edde746 f4ce60611b fix(subtitles): let the server deliver subtitles on a transcode
Two regressions since 2.9.1 broke subtitles on transcoded playback. Since
a1b6a8971 sidecars load with the media behind a 10s open guard, so a
subtitle URL the server is slow to serve — Jellyfin extracting an
embedded stream while its transcoder spins up — tripped the guard: stop,
reopen without subtitles, "Selected subtitles could not be loaded"
snackbar, and an emptied subtitle menu. Since 2b3853a88 every embedded
Plex subtitle was handed to the player as a sidecar whose URL is the
original container, so a transcode also range-read and demuxed the
source over HTTP — for a 40 GB remux, purely to find a subtitle track —
which is also why PGS never appeared: the client was handed a container
to demux rather than a rendition to play.

Delivery is the server's job again, backported from the AVPlayer branch
(42ba01440, the subtitle subset of 6852ac274, and a3da81e83) and adapted
to main's mpv backend:

Plex burns every embedded track (subtitles=burn); only a real external
file with a /library/streams key stays a client-fetched sidecar. A burn
is a re-encode, so directPlay is withdrawn — a real PMS answers HTTP 400
to directPlay=1 with burn — and the burn is aimed by selecting the
stream on the part first via the selectStreams PUT, because the decision
endpoint ignores subtitleStreamID alongside subtitles=burn. An
unaimable or undeliverable burn (dvb_teletext) refuses the transcode and
falls back to warned direct play rather than welding the wrong language
in or silently dropping the caption. Main's per-preset
directPlay/directStream pinning is kept; verified against a live PMS
that burn works under directStream=0.

Jellyfin never offers image formats as External, so bitmaps fall through
to Encode and are burned; text External is withheld per request when the
effective selection — including the server's DefaultSubtitleStreamIndex —
is embedded, and offered when it is a real file, so a file is delivered
as a file and never fetched twice. The burned row is excluded from the
sidecars; remaining text rows stay extractable, which is how a secondary
track still renders over a transcode. Sidecar URLs now use the format
extension the endpoint expects instead of the reported codec name.

The controls and selection layers learn what burning means: burn
eligibility is the codec's property, so burned rows stay selectable in
the menu; any change away from a burned selection renegotiates with the
server instead of pretending a local switch worked; the visibility
shortcut explains itself instead of doing nothing; and the track manager
is told when the primary is server-rendered so it stops waiting out a
thirty-second deadline for a native track that is already pixels.

Verified: analyzer parity, clean_translations --check --strict, full
flutter test (5749), and decision-level runs against live Plex and
Jellyfin servers — text and PGS burn decisions, the directPlay=1+burn
400, External file delivery, an unchanged no-burn baseline, and a real
burn session serving its playlist. The pre-commit aggregate was bypassed
for pre-existing main-state findings outside this diff: 21 format-drifted
files and three unused test seams in lib/main.dart.

close #1738

Refs #1815, #1622.
2026-08-09 07:30:47 +02:00
toluLikesToCode 8740a19f36 feat(player): start Plex transcodes at the resume position (#1817)
A Plex transcode session always starts producing at zero: the decision
request never sent offset=, so any non-zero open - resuming a
transcoded title, or switching from Direct Play to a transcoded
quality mid-playback - opened a session whose produced window begins
at the start of the file and seeked it. mpv immediately requests a
segment the transcoder has not produced, PMS answers 404 for it and
every subsequent segment, and playback buffers forever.

Send offset=<seconds> (6dp) with the decision and start request - the
view offset on initial open, the resolved resume position on every
in-place reload - so the session begins producing at the position the
player consumes first. The playlist timeline is unchanged: an offset
session's media playlist still covers the full title from segment
zero, so the player keeps opening with start: at the resume position
and in-stream seeks work as before.

Before a native player opens an offset playlist, waitForTranscodeReady
walks the master playlist, the media playlist, and the segment
containing the offset, because PMS can publish a manifest before that
segment is fetchable and mpv treats the 404 as an HLS error. The probe
is best-effort: it never fails an open, hands off immediately on HTTP
500 (on the response and exception paths alike) so the server-limit
dialog stays prompt, stops on cancellation, skips itself when the
playlist durations never reach the offset, and stays out of the
endpoint-failover cascade. In-place reloads resolve the replacement
source only after the old stop report has gone out, so Plex cannot use
that stop to terminate the replacement transcode.

close #1840
2026-08-09 06:28:59 +02:00
edde746 4872adcde3 feat(player): keep the session's explicit track choices across episodes
Episode advance carried live player state, so the viewer's choice only
survived while every episode could serve it: one episode without the
picked audio or subtitle fell back, and the fallback became the carry for
the rest of the session. The screen now keeps the last explicit audio,
subtitle, and secondary-subtitle choices for its lifetime; automatic
outcomes never overwrite them, so the choice retries on every following
episode and reattaches as soon as a catalog can serve it again.

Audio catches up with the subtitle carry from #1785. The old matcher
required raw language equality (a 'sv' pick never found a 'swe' row) and
otherwise took the first same-language track, flipping a commentary or
alternate-mix pick back to the main mix on every episode. Audio now uses
the same evidence bands as subtitles: bridged language parity is
authoritative, a unique title match vouches for untagged tracks, codec
and channel-count parity only break ties, and an ambiguous catalog
declines to the server's own choice instead of guessing. The synthesized
source descriptor also prefers the row's own title over the display title
that collapses to the bare language.

Episode advance previously sent no audio hint to negotiation at all, so a
transcode baked in the server's default audio no matter what was playing.
Both backends now resolve the carried semantics against the new episode's
streams: Jellyfin sends the resolved AudioStreamIndex, Plex feeds the
transcode decision, an explicit per-part stream id always wins, and a
failed match falls back to the server's pick.

close #1785
2026-08-04 16:10:02 +02:00
edde746 2b3853a882 fix(player): preserve transcoded subtitles at high speed
close #1622
2026-07-25 04:21:37 +02:00
edde746 e0bf66eea8 fix(runtime): harden application service boundaries 2026-07-24 03:46:46 +02:00
edde746 a1b6a89714 fix(player): load subtitle sidecars with media
close #1583
2026-07-17 23:09:07 +02:00
edde746 abf1027b77 feat(plex): migrate video transcoding to HLS 2026-07-14 18:11:04 +02:00
edde746 d1c4ae83eb fix(review): resolve post-consolidation regressions 2026-07-12 17:31:16 +02:00
edde746 7ecadffdb1 refactor(features): consolidate shared feature primitives 2026-07-12 17:31:13 +02:00
edde746 97f7508067 refactor(core): consolidate shared app foundations 2026-07-12 17:31:12 +02:00
Huzama Ahmad 7b917462c7 fix(plex): show PGS subtitles when transcoding (#1399)
* fix(plex): show PGS subtitles when transcoding

Bitmap subtitles (PGS/VOBSUB) disappeared from the menu while transcoding on Plex, leaving only "Off". The MKV transcode already supports them, so embed them in the stream like text subs. Now they show up and can be turned on.

* fix(plex): give bitmap subtitle codecs the right sidecar extension

getSubtitleExtension fell back to .srt for vobsub, pgssub and dvb subs. Map them to .sub/.sup so it stays consistent with isImageSubtitleCodec and any sidecar URL or download uses the correct extension.
2026-06-25 10:50:38 +02:00
edde746 4f422f1485 fix(plex): persist transcode audio stream switches
close #1326
2026-06-13 10:15:08 +02:00
edde746 74b8dc4561 refactor: type server identifiers 2026-06-01 11:06:03 +02:00
edde746 9a740b91c1 fix(plex): select available playback version 2026-05-31 02:21:15 +02:00
edde746 9825ce3c7c fix: select first playable Plex Part instead of always Part[0]
close #1201
2026-05-30 21:37:01 +02:00
edde746 d8f69f7acd fix(plex): copy mkv attachments during transcode
close #1083
2026-05-21 08:08:25 +02:00
edde746 c93a266722 fix(plex): support no-burn transcode subtitles 2026-05-20 17:21:31 +02:00