Skip to content

Cut adverts a host inserts as it serves, whatever their language - #19

Merged
antoine-debroye merged 1 commit into
mainfrom
ad/podcast-ad-localization-a43a8d
Oct 28, 2026
Merged

antoine-debroye merged 1 commit into
mainfrom
ad/podcast-ad-localization-a43a8d

Conversation

@antoine-debroye

Copy link
Copy Markdown
Collaborator

Why

Hosts such as Simplecast and Acast insert adverts into the MP3 per request, picked by the listener's country. A French show fetched from the UK carries English adverts; from Germany, German ones. The goal is no inserted advert at all, so detection cannot rely on language, and a proxy only changes which adverts arrive.

The evidence used instead: a host splices in a file it already has, so the same advert is byte-for-byte the same MP3 frames in every episode, while the programme (theme included) is encoded afresh each time.

What changes

  • Copied audio (src/lib/copied-audio.js): stretches identical across two or more episodes are cut. New catalogue kind inserted, migration 014.
  • Breaks between markers: Acast stitches a 2.2 s clip at both edges of every break. What lies between is cut, even adverts never heard elsewhere. One row per slot (pre-roll, mid-roll n, post-roll), so Restore here restores one break.
  • Frame hashes cached beside each fingerprint. Existing episodes are read once, without decoding.
  • Second download for feeds whose host inserts (set automatically, or with a new switch on the subscription form): same address, same user agent, a day later. Never a disguise.
  • Guards: over 2.5 minutes, or a copy only two episodes share mid-file (a best-of could share programme), is offered rather than cut. Breaks never add up to more than half an episode. An approved cut is never taken away for a row still waiting.
  • Jingle fix (bug also in 1.10.1): on Le Rendez-vous Tech the jingle search auto-confirmed the show's own theme and cut the host's spoken intro, up to 1:33. A jingle starting more than 3 s after the host's inserted audio ends is no longer auto-confirmed, and an earlier auto-confirmation is taken back. Jingles the owner confirmed are untouched.
  • scripts/check-copied-audio.mjs <folder> runs the search over downloaded episodes. Spec §19.3 and §19.7 and the README are updated.

Measured on real feeds (2026-10-28, from the UK)

Show Host Result
Les chroniques d'Arnaud Demanche Simplecast 13.9 s opener + 10.1 s closer, identical in 30/30 episodes, best-ofs included
Le Rendez-vous Tech Acast pre-, mid- and post-roll breaks cut in 5/5 marked episodes; served episodes open on the host's intro
Laurent Gerra Audiomeans nothing inserted, nothing cut

Neither Simplecast nor Acast leaves stitch marks, so the old second-download check never ran for these shows.

Testing

  • npm test: 1476/1476 (was 1428). Each new rule has a test, and each test was checked to fail with its rule removed.
  • test/acceptance/run.sh against a locally built image: 65/65, including a new step 24.
  • The real episodes above, run through the built container in automatic mode; transcribed either side of every cut and at the start of the served files.
  • Browser check of the Adverts page and the subscription switch.

Test fixtures that model a repeated theme or read now use a variant header option (test/helpers/mp3.js): same sound, different bytes, as a real re-encode is. Otherwise every synthetic repeat would count as inserted.

Known gaps

  • Not yet measured: whether Simplecast changes its adverts from one day to the next.
  • An advert inserted into one episode only, outside any marked break, and served identically to both downloads is not found by this.
  • One untested safeguard: re-looking for a pre-roll when a jingle is taken back late in a pass.

🤖 Generated with Claude Code

Hosts such as Simplecast and Acast stitch adverts in per request, chosen by
the listener's country, so a French show fetched from the UK carries English
adverts. Language and proxies cannot remove them; a proxy only changes which
adverts arrive. What every inserted advert has is that the host concatenates
a file it already has: the same advert is the same MP3 frames in every
episode, while the programme, encoded once per episode, never is.

- src/lib/copied-audio.js finds stretches byte-identical across episodes,
  and the breaks between the slot markers Acast stitches around them, so
  adverts never heard elsewhere are cut too. New catalogue kind `inserted`
  (migration 014), one row per stretch or per break slot.
- MP3 frame hashes are kept beside each fingerprint; older episodes are
  read once without decoding.
- A feed whose host is seen inserting gets every new episode downloaded a
  second time a day later (same address and user agent), with a switch on
  the subscription form.
- Guards: over 2.5 minutes, or a copy two episodes share mid-file, is
  offered not cut; breaks never exceed half an episode; an approved cut is
  never taken away for a row still waiting.

Also fixes the jingle anchor cutting a show's spoken intro (present in
1.10.1): on Le Rendez-vous Tech it auto-confirmed the show's own theme and
cut up to 1:33. A jingle starting more than 3 s after the host's inserted
audio ends is no longer auto-confirmed, and an earlier auto-confirmation is
taken back; owner-confirmed jingles are untouched.

Measured on real feeds on 2026-10-28: RMC opener and closer in 30/30
episodes; Rendez-vous Tech pre-, mid- and post-rolls in 5/5 marked
episodes, intro kept; RTL nothing. npm test 1476/1476, acceptance 65/65.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antoine-debroye
antoine-debroye merged commit e73d88b into main Oct 28, 2026
5 checks passed
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