feat: count text subtitle cues and allow declaring the forced track #16

Merged
nugget merged 1 commit from feat/declare-forced-subtitle into fix/no-subtitle-tracks-crash 2026-10-22 18:53:16 -05:00
Collaborator

Stacked on #15 — based on fix/no-subtitle-tracks-crash, so merge that first.
The diff here is this change alone.

Two independent reasons the forced-narrative track of a streaming pull could not
be flagged at all.

1. Text subtitle tracks measured zero cues

Cue counting drops packets at or below CLEAR_SEGMENT_MAX_BYTES (1000). That
threshold exists for exactly one reason — discarding PGS clear-screen segments —
but it applied to every codec. In a text track one small packet is one cue, so
the threshold discarded the entire track and every SubRip stream measured 0.

Known text codecs are now counted one packet per cue via a new
TEXT_SUB_CODECS. Anything unrecognised keeps the threshold and still reads 0,
which the forced rule already refuses to act on — an unmeasured track should be
left alone, not guessed at.

On a real file the SubRip track goes from 0 cues to 2372 cues, against its
1,235-cue DVB companion. The ~2× is the known teletext roll-up duplication, so
the number is plausible as well as non-zero.

2. The heuristic cannot work on a bilingual show

FORCED_MEDIAN_FRACTION assumes a forced-narrative track is a small fraction of
a full one. True when a few lines of signage are translated; false whenever the
show is substantially in another language. Shōgun is around 70% Japanese, so its
forced track subtitles nearly every line and lands at roughly the full track's
count. Nothing sits below the median, so no forced track is found — silently,
looking exactly like a file that has none.

No measurement of the file recovers this, so --forced-sub N declares it
(1-based, matching the printed sN column, repeatable). Supplying it bypasses
the heuristic for every track and prints that it did, because a bypassed
measurement that says nothing is how this repo produces confident wrong answers.
--default-sub-lang still picks exactly one forced track to carry
flag-default.

Counter-example recorded in the README per AGENTS.md.

Verification

ast.parse and the --help gate pass across lib/*.py. Real media in
~/Movies/Rips (DVB + SubRip, both eng):

case result
no flags s1 1235 cues, s2 2372 cues (was 0)
--forced-sub 2 s2 → flag-forced flag-default; bypass line printed
--forced-sub 2 --default-sub-lang none s2 → flag-forced only
--forced-sub 1 --forced-sub 2 both forced, only s1 takes flag-default
--forced-sub 99 --forced-sub 99: file has 2 subtitle track(s), exit 1
--forced-sub 1 on a 0-subtitle file file has 0 subtitle track(s), exit 1

--apply run for real against a 2-minute clip. Before: both subtitle tracks
forced=False default=False, unnamed. After: track 3 forced=True default=True
'English (forced)', track 2 default=False 'English SDH'.

🤖 Generated with Claude Code

Stacked on #15 — based on `fix/no-subtitle-tracks-crash`, so merge that first. The diff here is this change alone. Two independent reasons the forced-narrative track of a streaming pull could not be flagged at all. ## 1. Text subtitle tracks measured zero cues Cue counting drops packets at or below `CLEAR_SEGMENT_MAX_BYTES` (1000). That threshold exists for exactly one reason — discarding PGS clear-screen segments — but it applied to every codec. In a text track one small packet *is* one cue, so the threshold discarded the entire track and every SubRip stream measured 0. Known text codecs are now counted one packet per cue via a new `TEXT_SUB_CODECS`. Anything unrecognised keeps the threshold and still reads 0, which the forced rule already refuses to act on — an unmeasured track should be left alone, not guessed at. On a real file the SubRip track goes from `0 cues` to `2372 cues`, against its 1,235-cue DVB companion. The ~2× is the known teletext roll-up duplication, so the number is plausible as well as non-zero. ## 2. The heuristic cannot work on a bilingual show `FORCED_MEDIAN_FRACTION` assumes a forced-narrative track is a small fraction of a full one. True when a few lines of signage are translated; false whenever the show is substantially in another language. Shōgun is around 70% Japanese, so its forced track subtitles nearly every line and lands at roughly the full track's count. Nothing sits below the median, so no forced track is found — **silently**, looking exactly like a file that has none. No measurement of the file recovers this, so `--forced-sub N` declares it (1-based, matching the printed `sN` column, repeatable). Supplying it bypasses the heuristic for every track and prints that it did, because a bypassed measurement that says nothing is how this repo produces confident wrong answers. `--default-sub-lang` still picks exactly one forced track to carry `flag-default`. Counter-example recorded in the README per AGENTS.md. ## Verification `ast.parse` and the `--help` gate pass across `lib/*.py`. Real media in `~/Movies/Rips` (DVB + SubRip, both `eng`): | case | result | |---|---| | no flags | `s1 1235 cues`, `s2 2372 cues` (was `0`) | | `--forced-sub 2` | s2 → `flag-forced flag-default`; bypass line printed | | `--forced-sub 2 --default-sub-lang none` | s2 → `flag-forced` only | | `--forced-sub 1 --forced-sub 2` | both forced, **only s1** takes `flag-default` | | `--forced-sub 99` | `--forced-sub 99: file has 2 subtitle track(s)`, exit 1 | | `--forced-sub 1` on a 0-subtitle file | `file has 0 subtitle track(s)`, exit 1 | `--apply` run for real against a 2-minute clip. Before: both subtitle tracks `forced=False default=False`, unnamed. After: track 3 `forced=True default=True` `'English (forced)'`, track 2 `default=False` `'English SDH'`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Two reasons the forced-narrative track of a streaming pull could not be
flagged.

Cue counting discards packets at or below CLEAR_SEGMENT_MAX_BYTES, a
threshold that exists only to drop PGS clear-screen segments. Applied to
a text codec, where one small packet is one cue, it discarded everything
and every SubRip track measured 0. Count one packet per cue for known
text codecs instead. Anything unrecognised keeps the threshold and still
reads 0, which the forced rule already refuses to act on -- an
unmeasured track should not be guessed at.

The heuristic itself cannot work on a bilingual show. It assumes a
forced track is a fraction of a full one, which is false when the show
is largely in another language: Shogun is around 70% Japanese, so its
forced track carries nearly every line and matches the full track's
count. Nothing falls below the median and no forced track is found,
silently. Add --forced-sub N to declare it, which bypasses the heuristic
for every track and prints that it has done so, because a bypassed
measurement that says nothing is how this repository produces confident
wrong answers.

--default-sub-lang still picks exactly one of the forced tracks to carry
flag-default.
nugget merged commit 3063f5e033 into fix/no-subtitle-tracks-crash 2026-10-22 18:53:16 -05:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nugget/media-import-tooling!16
No description provided.