Skip to content

Align generated chapters to the played audio timeline via fingerprint timing#4666

Draft
SergioEstevao wants to merge 3 commits into
trunkfrom
sergio/chapters_with_fingerprint
Draft

Align generated chapters to the played audio timeline via fingerprint timing#4666
SergioEstevao wants to merge 3 commits into
trunkfrom
sergio/chapters_with_fingerprint

Conversation

@SergioEstevao

@SergioEstevao SergioEstevao commented Jul 1, 2026

Copy link
Copy Markdown
Contributor
Fixes POC-654

Summary

Generated chapters are timed against the reference (clean) audio the transcript was generated from. When the played file contains dynamic ads, the real audio positions shift, so the generated chapter markers drift out of sync with what's actually playing.

This PR re-maps each generated chapter's start time from the reference timeline onto the played file's timeline using FingerprintTimingManager (the same mapping already used for synced-transcript seeking), and recomputes chapter durations from the adjusted start times.

Because the fingerprint mapping is built asynchronously and grows around the current playback position during playback, chapters are re-aligned both at parse time and again whenever the mapping advances — via a new fingerprintTimingMappingUpdated notification. Chapters ahead of the covered region keep their reference times until coverage reaches them, then self-correct.

Notes for reviewers

  • ChapterManager compiles into targets that do not include the Fingerprint sources (Watch, App Clip, TV). To avoid breaking those builds, the dependency is decoupled behind the ChapterReferenceTimeMapping protocol + a ChapterReferenceTimeMappingProvider registry. The provider stays unset in those targets, so the adjustment is a no-op there and generated chapters simply keep their reference times.
  • FingerprintTimingManager conforms to the protocol (hasChapterReferenceMapping is true once .active, and the existing playbackTime(forReferenceTime:) satisfies the mapping requirement) and registers itself as the provider in the main app.
  • The adjustment is gated on chaptersOrigin == .generated and an active mapping. The active-mapping requirement implicitly covers "has generated transcripts", since the fingerprint reference only exists for generated-transcript episodes.
  • No new analytics events were added.

To test

  1. Play an episode that has generated transcripts and generated chapters, where the played file contains dynamic ads.
  2. Open the transcript so synced-transcript preparation kicks in (this builds the fingerprint mapping).
  3. As playback progresses and the mapping reaches .active, confirm the chapter markers/boundaries line up with the real audio content rather than being offset by the inserted ads.
  4. Regression: on an episode with embedded/native or Podcast Index chapters, confirm nothing changes.

Checklist

  • I have considered if this change warrants user-facing release notes and have added them to CHANGELOG.md if necessary.
  • I have considered adding unit tests for my changes.
  • I have updated (or requested that someone edit) the Event Horizon schema to reflect any new or changed analytics.

… timing

Generated chapters are timed against the reference (clean) audio the
transcript was generated from, so when a played file has dynamic ads the
chapter markers drift out of sync with what's actually playing.

When an episode has generated transcripts and generated chapters, re-map
each generated chapter's start time from the reference timeline onto the
played file's timeline using FingerprintTimingManager, recomputing chapter
durations from the adjusted start times. The fingerprint mapping is built
asynchronously and grows during playback, so chapters are re-aligned both at
parse time and whenever the mapping advances (via a new
fingerprintTimingMappingUpdated notification).

ChapterManager compiles into targets that don't include the Fingerprint
sources (Watch, App Clip, TV), so the dependency is decoupled behind the
ChapterReferenceTimeMapping protocol and a provider registry that stays unset
(no-op) in those targets. FingerprintTimingManager conforms to the protocol
and registers itself in the main app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dangermattic

Copy link
Copy Markdown
Collaborator
1 Message
📖 This PR is still a Draft: some checks will be skipped.

Generated by 🚫 Danger

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants