Commit Graph
43 Commits
Author SHA1 Message Date
edde746 daab4f1e24 fix(player): preserve the forced-subtitle class across episode boundaries
Plex treats a subtitle stream as forced when its title says "Forced" even
with the API flag unset. Every forced comparison now uses that effective
forced-ness on both sides: the match scorer, the low-metadata hard gate,
the Jellyfin OnlyForced/Smart profile modes, and stream-index negotiation.

Carrying a track choice into the next episode no longer reuses the
same-item identity matchers. A sealed SubtitlePreference (off / track
reference / semantic intent) replaces the id-'navigation' pseudo-track
through the whole preference channel, and cross-item intents hard-require
language and forced-class parity. When the next episode has no track of
the same class, the intent declines and selection falls through to the
server's own per-episode choice instead of latching onto a full track by
position and persisting that mistake back to the server.

Intents wait for pending native tracks under the same catalog-completeness
rule as source ids, so an early decline cannot retire the selection
listener before the real track arrives.

Ref #1716
2026-07-30 02:46:10 +02:00
edde746 d5f7c5d7ac fix(player): skip the deferred track pass when its switch is superseded
Persisting the deferred choice suspends, so the source switch can be
superseded before the pass is armed. Return early when the continuation is
stale, and refuse to arm a disposed or inactive TrackManager at all.

The per-callback generation checks only stopped the work; the subscription
and the five-second timer were still allocated on a manager whose dispose had
already run, so nothing would ever cancel them.
2026-07-27 17:51:07 +02:00
edde746 db593e1255 fix(player): keep an explicit transcode subtitle choice through the deferred pass
Persist the choice before arming the deferred selection pass. The screen
callback routes to onSubtitleTrackSelectedByUser, which invalidates the
pending selection, so arming first retired the very listener that applies
the choice once mpv discovers the sidecar.

The existing test stubbed the persist callback and so could not observe the
invalidation; it now routes through the manager like production does.
2026-07-27 17:44:51 +02:00
edde746 c68ffe9ed0 refactor: share the toolbar scrim and dedupe playback and download paths
Extracts the repeated toolbar fade into a single ToolbarScrim widget, folds
duplicated request/retry handling in the media server HTTP client, and
collapses the parallel playback-source, download-manager and live TV helper
paths into shared implementations.
2026-07-26 06:09:49 +02:00
edde746 83f4e2a263 refactor: fold single-use helpers into their call sites
Collapses indirection layers and one-caller abstractions across the video
player, shortcut dispatch, shader loading and context-menu code, including
the VideoPIPManager pass-through over PipService.
2026-07-26 06:09:49 +02:00
edde746 2b3853a882 fix(player): preserve transcoded subtitles at high speed
close #1622
2026-07-25 04:21:37 +02:00
edde746 0643787fbe fix(android): prevent ghost playback after autoplay failures
close #1673
2026-07-25 04:12:54 +02:00
edde746 c7b9eec087 fix(player): preserve subtitles across episode changes
close #1635
2026-07-24 21:40:22 +02:00
edde746 269bb7a322 fix(playback): recover episode navigation without Plex queues 2026-07-24 08:08:10 +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 c885c0c6bc fix(player): intercept spurious mid-file EOF and recover in place
close #1520

Classify player EOF signals against the best-known duration: a mid-file
EOF means the stream died (transcode reaped or idle connection closed
during a long pause), not that the media ended — it must never mark the
item watched, prompt Play Next, or exit a movie. Recover with a bounded
in-place reload at the parked position; if the server is still refusing,
park on the old frame and rebuild the stream on user play/seek or when
the server-status monitor sees it come back online.
2026-07-10 19:11:18 +02:00
edde746 d07d849fe4 refactor(player): return an outcome enum from _reloadMediaInPlace
The old bool meant "attempt was current and handled", so callers could
not distinguish a real pre-open failure (old stream still loaded) from
success — the distinction the #1520 recovery needs.
2026-07-10 19:06:51 +02:00
edde746 a4a2942546 fix(tv): release native AV pipeline when app stays backgrounded
A paused-but-alive player keeps its MediaCodec decoders and tunneled
passthrough AudioTrack while Plezy sits in the background, which can
degrade playback in every other app on shared-pipeline TV SoCs until
force-stop. After a 30s grace on Android TV the player is stopped
(retaining session state); returning reloads in place, or re-tunes the
channel for live TV.
2026-07-09 15:23:29 +02:00
edde746 ee3c75ba9e fix(playback): remember last played media version for continue watching
close #1492
2026-07-05 14:29:57 +02:00
edde746 86abf3e9da fix(watch-together): retry guest media switches until they commit
Guest switch dispatch pre-marked its dedup key and fired-and-forgot, so
any failure (fetch error, reload busy with an auto-advance, navigation
race with the host exiting) silently stranded the guest on the old media.
A CurrentPlaybackDispatcher now marks a key handled only after the sink
reports success against the committed identity, with a serialized
in-flight slot, timeout, and generation reset; the reconciler re-offers
unattached media on every host heartbeat, making the heartbeat the retry
channel. Fetches that outlive their dispatch are re-validated against the
current snapshot so a stale switch can't override the live one.
hostExitedPlayer now rides the controller's ordered message queue with
host authentication instead of racing state handling in the provider.
2026-07-02 12:44:15 +02:00
edde746 21c4ab430e feat(android): tune frame rate and subtitle rendering 2026-06-24 01:34:03 +02:00
edde746 618251b8eb fix(player): attach external subtitles during open 2026-06-15 17:21:08 +02:00
edde746 8bf5dbb379 fix(player): resume mpv startup frame after subtitles 2026-06-15 12:06:04 +02:00
edde746 7195702242 fix(player): repair Plex transcode seeking
close #1341
2026-06-14 23:39:05 +02:00
edde746 f3cb508649 fix(player): wait for mpv subtitles after load
close #1331
2026-06-13 23:52:19 +02:00
edde746 4f422f1485 fix(plex): persist transcode audio stream switches
close #1326
2026-06-13 10:15:08 +02:00
edde746 90662e2173 fix(player): scale mpv stream ring buffer for poorly interleaved mp4/mov 2026-06-12 22:21:17 +02:00
edde746 bb92b6083b refactor(player): clarify requested vs session-effective media source id 2026-06-12 14:21:12 +02:00
edde746 a5c7d5b52a refactor(watch-together): host-authoritative declarative sync protocol
Replaces the imperative play/pause/seek/positionSync message soup with a
single host-authored PlaybackState (seq-ordered, anchor-extrapolated,
phase machine: loading/waitingForPeers/paused/playing) that doubles as
the heartbeat, plus guest status reports and host-applied control
requests. Fixes the guest seek-back loop while the host loads (readiness
was keyed on a pre-load !buffering snapshot and heartbeats broadcast
frozen positions), adds real group buffering coordination (stall grace,
scheduled simultaneous resumes, 15s safety timeout), rate-nudge drift
correction with passthrough-aware seek fallback, session-scoped message
handling (no lost messages during episode-switch detach gaps), and an
expected-state ledger replacing the racy remote-action flag.
2026-06-11 09:42:38 +02:00
edde746 cd9498abb8 refactor(player): centralize playback opening
close #1280
2026-06-10 02:26:26 +02:00
edde746 24b5080c38 fix(video): stabilize player chrome overlays 2026-06-02 14:20:24 +02:00
edde746 5671f6e5d1 fix(plex): send playback session stream header 2026-05-31 06:30:51 +02:00
edde746 9a740b91c1 fix(plex): select available playback version 2026-05-31 02:21:15 +02:00
edde746 dba01f14bb fix(playback): harden watch progress edge cases 2026-05-29 21:16:01 +02:00
edde746 d93ea9813f fix(playback): harden offline source reporting 2026-05-29 19:55:01 +02:00
edde746 7501f461b9 fix(playback): sync downloaded watch progress
close #1171, close #1183
2026-05-29 19:55:01 +02:00
edde746 4c43d6fdec fix(jellyfin): prefer direct playback by default 2026-05-23 18:38:38 +02:00
edde746 c93a266722 fix(plex): support no-burn transcode subtitles 2026-05-20 17:21:31 +02:00
edde746 324d145292 fix(jellyfin): support external audio playback 2026-05-18 13:43:58 +02:00
edde746 5df3ec0c12 fix(playback): use display criteria for matching 2026-05-15 19:46:04 +02:00
edde746 12b006ee0c fix(tvos): prime display criteria from metadata 2026-05-14 19:51:33 +02:00
edde746 ec93062600 fix(app): harden playback and plex flows 2026-05-10 00:35:01 +02:00
edde746 05c4d6dff3 feat(playback): restart before previous episode
close #979
2026-05-05 08:28:17 +02:00
edde746 ed4be7b96d refactor: strip obvious comments 2026-05-04 22:40:18 +02:00
edde746 11e1a1464e fix(playback): sync resume progress on exit 2026-05-03 06:45:54 +02:00
edde746 8fabf56337 refactor: split playback and media hotspots 2026-05-02 06:33:05 +02:00