Skip to content

fix(subtitles): a zero offset reach stops the forward prefetch's second download (#1015) - #1017

Merged
superuser404notfound merged 2 commits into
mainfrom
fix/issue-1015-prefetch-off-at-zero-reach
Nov 10, 2026
Merged

superuser404notfound merged 2 commits into
mainfrom
fix/issue-1015-prefetch-off-at-zero-reach

Conversation

@superuser404notfound

Copy link
Copy Markdown
Owner

Addresses #1015.

What changes

maximumSubtitleOffsetSeconds = 0 now also stops the #151 forward subtitle prefetch, for text and bitmap tracks. That reader downloads an interleaved MKV or MP4 a second time, and its cues beyond the pump's forward park only serve an advance offset.

The prefetch still runs when something reads ahead:

  • maximumSubtitleOffsetSeconds above 0 (the default, 60, so nothing changes for hosts that set nothing)
  • a negative softwareSubtitleDelaySeconds
  • an armed OCR worker (bitmap track shown through PiP, AirPlay or an external display)

Changing the reach or the software delay starts or stops it mid-session. A stop logs reason=noAdvanceReach.

No new API: the reporter's proposed .whenAdvancing is what a zero reach plus a negative software delay already does. A shorter lead would not save bytes, the reader is sequential.

Measurement

aetherctl play --subs srt --start-position 30 --seconds 40 over a threaded range server, 720p 6 Mbit/s MKV with one embedded SRT, bytes counted at the origin:

Arm Run 1 Run 2
subtitles off 53.4 MB
embedded SRT, default reach 92.9 MB 92.4 MB
embedded SRT, reach 0 53.8 MB 54.1 MB

Every cue from 30 to 69 s arrived in both arms; with reach 0 the pump's harvest still reached 116 s.

Tests

Issue1015PrefetchAdvanceReachTests (gating, delay sign, mid-session stop and start, no start without a drain target). Full suite green: XCTest 836, swift-testing 5819.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SeT6NgMt1vv32kPBhUmsUz

…nd download (#1015)

Selecting an embedded subtitle on a remote source started the #151 forward
prefetch, a subtitle-only side reader that downloads the interleaved video a
second time. Its cues beyond the pump's forward park only serve an advance
offset or the OCR worker, so it now runs only when something reads ahead:
maximumSubtitleOffsetSeconds above 0, a negative softwareSubtitleDelaySeconds,
or an armed OCR worker. Either change starts or stops it mid-session, under the
new noAdvanceReach stop reason. The default reach (60) keeps today's behaviour.

Measured with aetherctl over HTTP, 40 s of a 720p 6 Mbit/s MKV with an
embedded SRT, two runs each: 92.9 / 92.4 MB with the default reach, 53.8 /
54.1 MB with reach 0, 53.4 MB with subtitles off. Every cue from 30 to 69 s
arrived in both arms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SeT6NgMt1vv32kPBhUmsUz

@quint-aether quint-aether Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review

Status: 🟡 Minor follow-ups. Only small touch-ups left.

The PR gates the #151 forward subtitle prefetch on an actual need: a host offset reach above 0, a negative softwareSubtitleDelaySeconds, or an armed OCR worker. Changing the reach or the delay starts or stops the prefetch mid-session. The fix is in the engine, it removes the duplicate download at its cause, and the default (60) keeps today's behaviour, so the direction is right.

I checked every path that starts the prefetch: selection (startAt), drain-tick re-anchor, OCR arm and disarm through syncSubtitleOCRWithNativeSelection, and the new reach and delay setters. Each ends up at the new guard or at the shared shouldRunSubtitleForwardPrefetch. Disarming OCR at reach 0 changes the lead, so it skips the in-place re-anchor and stops under .noAdvanceReach as intended. The sign in subtitleAdvanceReach matches the compositor (subtitleTime = seconds - delaySeconds), so only a negative delay reads ahead.

Findings

  • Nit: the new test file is named after the issue, which CONTRIBUTING.md asks to avoid. These cases fit in the existing forward-prefetch or offset-reach test files (Tests/AetherEngineTests/Issue1015PrefetchAdvanceReachTests.swift:153).
  • Nit: in docs/formats.md the new paragraph separates the #151 paragraph from its bullet list (docs/formats.md:983).

Checklist: CHANGELOG yes, docs yes, tests yes, traits both ways n.a.

Needs a device run: a device run with reach 0 on a remote MKV over the native route, a bitmap (PGS) track and the software host, to confirm that the pump's harvest alone delivers cues on time after seeks.

Thanks for the measured byte counts and the careful gating.

Line comments

  • Tests/AetherEngineTests/Issue1015PrefetchAdvanceReachTests.swift:153: Nit: CONTRIBUTING.md asks that a test file be named after the behaviour, not the issue, with the issue number in the test name. These cases fit in Issue151SubtitleForwardPrefetchTests.swift, which this PR already touches, or in Issue929OffsetReachTests.swift. Adding them there, with AE#1015 in each @Test name, avoids yet another Issue<N> file.
  • docs/formats.md:983: Nit: this paragraph now sits between the #151 paragraph and the bullet list that describes it. A reader can then take bullets like "So subtitleCues holds cues ahead of the playhead..." as following from the gating rule. Moving the paragraph below the list, or turning it into a bullet such as "Runs only when ...", keeps the list attached to the paragraph it explains.

Automated review by Quint (Claude) of 015e606. It reads the code, it does not build or run it; a maintainer reviews and merges.

…ts, keep the docs list attached

Quint's review on #1017: CONTRIBUTING.md names test files after behaviour, not
issues, so the advance-reach cases move into
Issue151SubtitleForwardPrefetchTests.swift with AE#1015 in each test name. The
new gating paragraph in formats.md becomes a bullet of the #151 list instead of
splitting the paragraph from the list that explains it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SeT6NgMt1vv32kPBhUmsUz

@quint-aether quint-aether Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review

Status: 🟢 Clean. Nothing left to fix, ready for a maintainer.

I checked the new head (9849fbb). Both nits from the last review are fixed, and the gating logic is the same as before.

  • Fixed: the AE#1015 cases are now in Issue151SubtitleForwardPrefetchTests.swift as SubtitleForwardPrefetchAdvanceReachTests, with AE#1015 in each @Test name.
  • Fixed: the docs/formats.md paragraph is now a bullet under the #151 list, so the list stays with the paragraph it explains.

Findings
No open findings.

Checklist: CHANGELOG yes, docs yes, tests yes, traits both ways n.a.

Needs a device run: set the reach to 0 and check that the pump's harvest alone delivers cues on time after seeks. Cover a remote MKV on the native route, a bitmap (PGS) track and the software host.

Thanks for the quick follow-up.

Automated review by Quint (Claude) of 9849fbb. It reads the code, it does not build or run it; a maintainer reviews and merges.

@superuser404notfound
superuser404notfound merged commit 0624eb9 into main Nov 10, 2026
6 checks passed
@superuser404notfound
superuser404notfound deleted the fix/issue-1015-prefetch-off-at-zero-reach branch November 10, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant