feat: count text subtitle cues and allow declaring the forced track (re-target of #16) #17

Merged
nugget merged 1 commit from feat/declare-forced-subtitle into main 2026-10-22 19:01:06 -05:00
Collaborator

Re-targets the commit from #16 at main. No code change from #16 — the
content is byte-identical, as git diff origin/fix/no-subtitle-tracks-crash origin/feat/declare-forced-subtitle confirms (empty).

#16 was opened with its base set to fix/no-subtitle-tracks-crash to keep its
diff reviewable while #15 was still open. That was a mistake: #15 merged that
branch into main before #16 merged into it, so #16's merge landed on a branch
that main had already taken, and the feature commit was never in main. All
three PRs report merged=True, which is how it went unnoticed.

main..origin/feat/declare-forced-subtitle is a single commit and merges cleanly.


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

1. Text subtitle tracks measured zero cues

Cue counting drops packets at or below CLEAR_SEGMENT_MAX_BYTES (1000), a
threshold that exists for exactly one reason — discarding PGS clear-screen
segments — but which applied to every codec. In a text track one small packet
is one cue, so it discarded the whole 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 ~2x is the known teletext roll-up duplication, so
the figure 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

Re-targets the commit from #16 at `main`. **No code change from #16** — the content is byte-identical, as `git diff origin/fix/no-subtitle-tracks-crash origin/feat/declare-forced-subtitle` confirms (empty). #16 was opened with its base set to `fix/no-subtitle-tracks-crash` to keep its diff reviewable while #15 was still open. That was a mistake: #15 merged that branch into `main` *before* #16 merged into it, so #16's merge landed on a branch that `main` had already taken, and the feature commit was never in `main`. All three PRs report `merged=True`, which is how it went unnoticed. `main..origin/feat/declare-forced-subtitle` is a single commit and merges cleanly. --- Two independent reasons the forced-narrative track of a streaming pull could not be flagged. ## 1. Text subtitle tracks measured zero cues Cue counting drops packets at or below `CLEAR_SEGMENT_MAX_BYTES` (1000), a threshold that exists for exactly one reason — discarding PGS clear-screen segments — but which applied to every codec. In a text track one small packet *is* one cue, so it discarded the whole 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 ~2x is the known teletext roll-up duplication, so the figure 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 f3cf1d4819 into main 2026-10-22 19:01:06 -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!17
No description provided.