summaryrefslogtreecommitdiff
path: root/tools
AgeCommit message (Collapse)Author
2026-10-05Find why Tier 3 never confirmed, and port detection to GoPeter Stone
Peter's report: the phone runs hot, uses a lot of battery, playback takes a moment to start, and intelligent skipping has never done anything. **The hot phone and the dead detection were one bug.** The candidate search correlated 117 window positions against the whole of the other clip, re-transforming that unchanged clip every time -- 199 seconds per episode end, measured on a desktop, so minutes more on a phone. FingerprintWorker is a plain WorkManager worker, Android stops one at ten minutes, and nothing was persisted until a comparison returned. So it was killed mid-flight, saved nothing, and was re-enqueued to start over, forever. IntroOutroDetectorScaleTest is the tripwire whose absence allowed this: the unit tests ran on 300-sample clips while production ran on 1,920,000. Fixed coarse-to-fine, plus AudioCorrelation.prepareTarget so the target clip is transformed once per search rather than 117 times. 10.6x, and a test asserts the chosen reference window is byte-identical to what a full-resolution search picks. **Midroll detection could never have run at all.** decodeFull buffered a whole episode at its native rate -- 434MB for a 13-minute episode, no largeHeap -- to produce 6MB of 8kHz mono. It threw OutOfMemoryError into a swallowing runCatching and the episode was then marked checked forever. MonoResampler now streams the decode so the peak allocation is the output, and maxDecodableMs skips a longer episode before spending anything and without marking it checked. **Detection is moving to a server**, Peter's call, in Go to match podkit and doot. server/ holds the ported core, verified three ways because this is the second port of this maths and the first shipped a normalization bug: numpy's literals extracted mechanically from AudioCorrelationTest, the FFT against a direct DFT, and the whole correlation path against a brute-force Pearson. On four real Radiolab episodes it confirms the intro on 4 of 4, at 48.0s, 8.65s, 8.65s and 65.15s -- the varying pre-roll, observed in working code at last. skips.SelfCheck verifies located segments against *each other* rather than against the reference, which is a different claim: correlation is not transitive, so two segments can each clear 0.6 against a reference and be unrelated. It rejected the outro on its first real run (r=0.746), withholding offsets that would have ended three episodes early. Offsets are published only when they verify. Also: the episode screen gets a working scrubber with ad-break marks and a declared-versus-measured length readout, queue rows open the episode instead of doing nothing, PrepareTrace logs playback-start timing against episode length for the still-open pause question, and -PphoneOnly drops the emulator's CPython from a sideload build (114MB -> 99MB). Full history and numbers in docs/detection-findings.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-09-26Drop ID3 CHAP frames, having measured the real libraryPeter Stone
Peter asked how expensive it would be to check before dropping them. Checking turned out to be cheaper than arguing about it, so here is the answer from his own 61 subscriptions rather than from general knowledge. Every feed's newest episode was fetched far enough to read its entire ID3 tag -- the declared size from the header, not a guessed window, which matters because several are multi-megabyte (Cruising the Waves carries 10MB of cover art) and a first pass with a 400KB window left thirteen of them inconclusive. ID3v2.2's 3-byte frame headers needed handling separately from v2.3/2.4's 4-byte ones, which is what made Dan Carlin's feeds look truncated at first. Not one of the 61 carries a CHAP frame. The frames present are all artwork and metadata: APIC, TALB, TIT2 and friends. Exactly one show publishes chapters at all -- Wards Way Podcast -- and it does so as Podcasting 2.0 JSON, which has been supported since the skip work landed. So CHAP is dropped rather than deferred. The two survey scripts are kept under tools/ so this can be rerun in a minute if a new subscription ever suggests otherwise, rather than re-derived. Two things fell out of the survey worth writing down. A feed can carry two chapters elements per item -- the podcastindex one with a url and a podlove one without; Buzzsprout emits both. The parser takes the first non-null url so it is unaffected, but a last-wins read would silently lose chapters. And the one show with chapters marks none of them toc:false, so ad-chapter skipping -- on by default as of this morning -- currently has nothing to act on anywhere in the library. It is correct and idle, waiting for a publisher who marks breaks. Documentation and tooling only; no behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B