Skip to content

[bug] Returning to Original after a lower-version quality change leaves the subtitle off #2092

Open

Description

@blurbery

What happened

On a movie with two versions, where only the 4K version has an English subtitle and 4K transcoding is turned off, a quality change to a lower quality drops the subtitle. That part is expected. Choosing Original afterwards goes back to the 4K version, but the subtitle stays off, with no warning, even though that version has it and the viewer never turned it off. Returning to the original picture should bring the subtitle back. It happened in 3 of 3 runs.

Trimmed playback_plan from each response (files: 2 = 2160p with the English SRT, 1 = 1080p without subtitles):

1. start, 2160p, English SRT selected
     effective_media_file_id=2 delivery=original_http video=h264 2160p subtitle=file:2:subtitle:0 mode=render
     warnings: none
2. quality_change to 720p
     effective_media_file_id=1 delivery=server_transcode_hls video=h264 720p subtitle=null mode=off
     warnings: subtitle_track_unavailable: The selected subtitle track is not on this file; starting without it.
3. quality_change to original
     effective_media_file_id=2 delivery=original_http video=h264 2160p subtitle=null mode=off
     warnings: none

Steps to reproduce

Local dev build of main at ca186fe3a: a fresh database, no metadata plugins, and allow_4k_transcode set to false in admin settings. The movie folder is Silo Repro (2020) [tmdbid-<placeholder>] (the id tag only groups the two files as one title) and holds:

  • Silo Repro (2020) - 2160p.mkv: H.264, 3840x2160, 60 s
  • Silo Repro (2020) - 2160p.en.srt
  • Silo Repro (2020) - 1080p.mkv: H.264, 1920x1080, 60 s, no subtitles
  1. POST /api/v1/playback/start for the 2160p file with subtitle_track_index: 0 (the SRT) and quality_preference: auto, from a protocol v3 client that direct plays H.264 MKV up to 2160p and renders sidecar text subtitles. The plan direct plays file 2 with the subtitle rendered.
  2. POST /api/v1/playback/{session_id}/replan with operation: quality_change and quality_preference: 720p, sending only the audio track in selected_tracks (the docs allow a quality replan to omit unchanged tracks). The plan moves to a 720p transcode of file 1 with subtitles off and subtitle_track_unavailable.
  3. The same replan with quality_preference: original. The plan direct plays file 2 again with subtitles off and no warning.

Expected behavior

Step 3 should return to file 2 with the English SRT selected. docs/architecture/playback-protocol-v3.md (replan section) says quality replans may omit unchanged track identities and the server "preserves the durable selected subtitle otherwise". The viewer's selection was the English subtitle. Only the fallback version lacked it.

Silo version / commit

Local dev build of main at ca186fe3a

Deployment

Local dev build

Area

Playback and transcoding

Clients affected

  • Web UI
  • Android
  • iOS/tvOS/macOS
  • Jellyfin-compat client (Infuse, Findroid, ...)
  • Other/not client-specific

Client app and device

The protocol v3 API driven directly from a script, with capabilities for H.264 MKV direct play up to 2160p and sidecar text subtitles. I haven't checked which native clients send quality replans without the subtitle.

Media and playback details

Two versions of one movie. The 2160p version is H.264 MKV with an external English SRT. The 1080p version is H.264 MKV without subtitles. 4K transcoding is disabled. Step 1 direct play, step 2 transcode, step 3 direct play.

Diagnostics report reference

No response

Relevant logs

level=INFO msg="playback plan decided" component=playback request_id=39918e10bde4e7964a9afb71 outcome=plan decision_reason=validated_original_playback delivery=original_http play_method=direct requested_file_id=2 effective_file_id=2 dv_profile=0 dynamic_range=sdr target_resolution="" target_bitrate_kbps=0 quality_preference=auto bandwidth_estimate_kbps=0
level=INFO msg="api request" component=api component=api request_id=4b561a46897aae2faae22628 method=POST path=/api/v1/playback/fe6c126e-d076-493e-8009-cbf66c1d2351/replan path_pattern=/api/v1/playback/{session_id}/replan status=200 duration_ms=1033 client_ip=127.0.0.1 node_id=<redacted-host> user_id=1 session_id=318ab99a-07fc-4b32-a347-657d47ca3b06
level=INFO msg="api request" component=api component=api request_id=1dc2b8557b25dbce9544174e method=POST path=/api/v1/playback/fe6c126e-d076-493e-8009-cbf66c1d2351/replan path_pattern=/api/v1/playback/{session_id}/replan status=200 duration_ms=14 client_ip=127.0.0.1 node_id=<redacted-host> user_id=1 session_id=318ab99a-07fc-4b32-a347-657d47ca3b06

Replan decisions are not logged at info level. The plans above are from the API responses. node_id is redacted.

Technical notes

Suspected cause, separate from the observations above. The 720p fallback goes through the subtitle-dropping version fallback from #552. Its subtitles-off selection becomes the attempt's current plan. Every later replan reseeds its tracks from CurrentPlan.SelectedTracks (applySelectedTracksToStartV3 in internal/api/handlers/playback_v3.go), so by the time Original returns to the 4K file, the "durable selected subtitle" is already off. The server keeps no record of what the viewer asked for, only what the last plan could show.

A fix would keep the viewer's requested subtitle separate from the effective selection. That means storing it on the attempt, reapplying it when a later plan's file has an equivalent track, and only treating an explicit track_change as the viewer turning subtitles off. #2088 (a failed downloaded-subtitle lookup sticking for the session) runs into the same limit for sessions that start during an outage. #2081 is the related start/track-change case where the server picked a lower version only because of the subtitle.

The same reseeding probably also affects audio after an audio-dropping fallback. I haven't verified that.

AI harness

Claude Code (Claude desktop app)

AI tool(s)

Claude Code

AI model(s)

claude-opus-5-5

AI involvement

AI-assisted

Independent or adversarial review

n/a: this report has no code change. The reproduction ran on a local dev build, and the responses and log lines above are from that run.

Confirmations

  • I searched open and recently closed issues and pull requests, and this is not already reported
  • I reproduced this myself on a real deployment
  • Any logs provided are raw apart from marked redactions and are not AI-summarized; if none were available, I said so above

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions