summaryrefslogtreecommitdiff
path: root/CLAUDE.md
diff options
context:
space:
mode:
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md39
1 files changed, 32 insertions, 7 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 0ce7d44..41809a2 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -211,13 +211,38 @@ by default and correct; it is simply waiting for a publisher who marks breaks.
## Domain notes worth keeping
- **A feed's declared duration is often wrong, and not by rounding.** Radiolab publishes Grass
- Apes as `00:22:05`; the file ExoPlayer opens is 29:34. The seven and a half minutes between
- them are dynamically inserted ads -- the feed states the ad-free content length and the CDN
- stitches in the rest -- so the error is large, systematic, and worst on exactly the shows the
- midroll detector cares about. `PositionWriter` replaces the stored value with the player's
- once per episode, on the position tick, via the narrow `PlaybackStateDao`. Once per episode,
- not once per tick: a lookup on every tick for the length of an episode is the per-episode
- work the feed-scale note below warns about.
+ Apes as `00:22:05`; the file served is 27-30 minutes. The gap is dynamically inserted ads --
+ the feed states the ad-free content length and the CDN stitches in the rest -- so the error
+ is large, systematic, and worst on exactly the shows the midroll detector cares about.
+
+ **Two columns, not one.** `durationMs` is the feed's claim and stays the feed's:
+ `EpisodeMatcher.mergedWith` rewrites it on every refresh, correctly, which is why writing the
+ measured length there lasted exactly until the next refresh. `actualDurationMs` is what the
+ player measured and nothing else touches. **Read `playbackDurationMs`**, which prefers the
+ measured one -- the feed's figure otherwise sizes the decode buffer a full-episode scan
+ allocates, places the outro offset, and drives the finished check that would mark a
+ 29-minute episode played at 21.
+
+ `PositionWriter` records it once per episode on the position tick, via the narrow
+ `PlaybackStateDao` -- not once per tick, which would be the per-episode work the feed-scale
+ note below warns about.
+
+- **The difference between the two durations is the advertising**, free and with no audio
+ analysis: `insertedAdMs`, shown on the episode screen. Peter's observation. It is also a
+ check on the midroll detector, which should eventually account for roughly that much.
+ **It is per *file*, not per episode** -- the same Grass Apes measured 7:29 of ads on one
+ fetch and 5:34 on another, because insertion happens per request. Do not treat it as a
+ stable property of an episode, and do not be surprised when a re-download changes the
+ length.
+
+- **A timestamped link shared in starts playing there.** `WatchLink` parses the `t`, the
+ repository stores it as the position, and the share activity hands the episode to
+ `MainActivity.playIntent` so the app opens on the player already playing -- the point being
+ to move between YouTube and Listen without hunting for your place. Re-sharing a video
+ already in the library updates its position and unplays it, which is the case that was
+ broken: the insert ignores a clashing guid, so every share after the first silently did
+ nothing. A share carrying no timestamp still just adds it quietly, since interrupting
+ whatever is playing would be rude.
- **The media notification opens the app**, via `setSessionActivity` on the session. Without it
the transport buttons work and tapping the body does nothing, which reads as breakage rather