fix: classification errors surfaced by a TV box-set import #4
Loading…
Reference in a new issue
No description provided.
Delete branch "lessons/got-s1-import"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Four corrections surfaced while importing a Game of Thrones season 1 4K box set —
the first TV material this tooling has been pointed at. Three of them are cases
where a script returned a confident wrong answer rather than an error.
feat: decode MakeMKV StreamFlags in scan_streamsAttribute 22 of each
SINFOrow is a bitfield that names relationships we hadbeen inferring by measurement: which lossless track carries an embedded lossy
core (
HasCoreAudio), which track is that core (CoreAudio|DerivedStream),and which subtitle entries are derived forced-only views (
ForcedSubtitles).This settles a case the published specs cannot. Each episode exposes two English
DD 5.1 640 kb/s tracks; blu-ray.com lists one, because cores are never listed
separately. The flags say which is the Atmos companion — confirmed independently
by correlation (r=0.994) and by the other track failing to null against it.
A set bit is evidence; a clear bit is not. These same discs carry commentary
tracks with a flag value of
0. Forced-view detection previously string-matcheddescription text and now reads the bit, but the scan-vs-output warning is kept
and sharpened: 26 non-derived entries still became 32 output streams.
fix: classify forced subtitles against the file, not a constantThe 500-cue rule was calibrated on features. A 60-minute episode's full tracks
run 380–650 cues, so it reclassified Czech (450), Polish (472), Croatian (383),
Hebrew (431) and Slovenian (383) as forced — five languages spanning the entire
runtime.
Replacing the count with track span fails too, and that is the trap worth
recording: a Hungarian forced track carries four cues spread from 518s to 3694s,
covering nearly the whole episode, because the scenes it exists for are spread
out. Coverage says "full"; the track is forced.
What holds is the ratio — full and forced differ by ~100×, so a cut taken from
the file's own median needs no absolute number. New
--surveydoes this in onedemux pass.
--countno longer offers a verdict, because a single track inisolation genuinely cannot be classified.
fix: separate same-language alternate mixes from foreign dubsA remaster often ships its legacy mix alongside the new one. That track lands at
r=0.95–0.98 — below the companion-core threshold, squarely inside the foreign-dub
band. The 2011 broadcast DD 5.1 measured r=0.974/+3.1 dB against the 2018 Atmos,
and r=0.982/+1.5 dB against the Atmos's own core, where it was reported as a
foreign dub. It is English.
Only the language resolves it, so tags are now read from the file and compared
automatically; when the language is unknown the classifier returns the ambiguity
instead of picking a side.
Also widens the dub lift ceiling from 3 to 6. Gap lift is measured against the
window's quiet quartile and rises as that floor falls — the same sensitivity
already documented for companion cores. Known French and German dubs measured
+3.9 and +4.6 dB against a −53 dB floor, so at the old ceiling the German
control classified as UNCLEAR.
docs: record failure modes from a TV box-set importSix entries, including the one that actually wrote bad data to a file:
collides across a set, and because stream indices line up too, the wrong data
reads as plausible. Disc 2's E04 analysed disc 1's E01 subtitle packets; the
only symptom was Italian ending up with a forced track and no full track.
mkvpropeditchanges only what you name. A second pass renames tracks butleaves the first pass's flags in place — correct names over stale bits.
Russian DTS because it has none).
one without a commentary, readable from the scan log before decoding anything.
duration and near-identical frames, different video MD5s.
Verification
No unit suite, per
AGENTS.md; syntax check plus--helpon every script, theneach changed script run against real media:
scan_streams.pyon a real scan log — reportsHasCoreAudio/CoreAudio,DerivedStreamon the expected pairaudio_identify.pyon an episode — all five tracks classify correctly(alternate mix, core, commentary, two dubs); previously two were wrong
subtitle_inspect.py --surveyon two episodes — catches all forced tracks andmisclassifies no full track, including both counter-examples above
scan_streams.pynow exits 0 from--help, whichAGENTS.mddocuments as averification step.
pickthumb.py,probe_mkv.pyandscan_titles.pystillfail that check on
main— pre-existing, left alone as out of scope for thischange, but worth a follow-up since the repo currently fails its own documented
gate.
A rip that hit an unreadable sector printed Copy complete. 0 titles saved, 1 failed. and still returned 0. Driving makemkvcon in a loop over a season, $? reports success for an episode that produced no file, and the season is quietly short one episode. That is how S01E09 was nearly lost: the wrapper trusted the exit code and the monitor duly reported 'exit=0' for a title that did not exist. rip_result.py parses the completion message and confirms the output file exists, exiting non-zero when either check fails. It parses message CODES rather than rendered text, since the text is localisable and a locale change would make a failed rip look successful. Critically, SUCCESS AND FAILURE USE DIFFERENT CODES: a clean run emits 5036/5005 carrying only a saved count and never emits 5037 at all. The first version of this handled only the failure pair and therefore reported every successful rip as a failure -- caught by testing against a real success log as well as a real failure log. Read errors are reported but do not by themselves fail the check. The pass that recovered S01E09 logged 5 of them; MakeMKV retried through, the content hash table verified the M2TS, and the file decoded start to finish. Repeated byte offsets are surfaced because a fixed offset across runs indicates a real defect while a moving one indicates a marginal read worth retrying.