diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 33 |
1 files changed, 33 insertions, 0 deletions
@@ -104,6 +104,11 @@ claims three separate times across these repos. queue, play, variable speed, sleep timer, retention, unsubscribe, Android Auto with a browsable library and voice search. 207 unit tests plus instrumented tests covering the media browse tree. +**Since Phase 2**, in response to a round of Peter's feedback: yt-dlp sources play (they are +fetched first), playback errors are surfaced instead of being swallowed, anything queued is +downloaded at once, Facebook videos are supported, the library ranks shows by their newest +*unqueued* episode, and trailer/preview filter rules ship as defaults. + **Phase 2 is complete**, in three tracks. Peter deferred the audio-fingerprint skip detection (the design plan's Tier 3), so the simple skip work came last. @@ -149,6 +154,18 @@ out of a possibly-partial MP3, and the JSON path covers the shows that mark chap title+date. An episode that loses its identity loses its playback position. - **Retention is auto-delete-once-played** with a grace period, so a mis-tap doesn't destroy an episode you were still listening to. +- **A yt-dlp source has no playable URL until it is fetched.** A YouTube or Facebook episode's + `mediaUrl` is its watch page; the audio URL only exists once yt-dlp has resolved it. + `SubscriptionKind.needsExtraction` says so, `PlaybackAdvancer.start` reports `NeedsFetch` + rather than a `NextUp` that cannot work, and `fetchAndStart` does the fetch. Both the service + and the view model go through it -- the queue advances without the UI. + +- **Never `bestvideo+bestaudio` in `ytaudio.py`.** That is the form that needs ffmpeg, which is + not on Android. Every selector in `_FORMAT` picks one already-complete file. Audio-only comes + first, then a muxed mp4: **Facebook publishes no audio-only stream at all**, only pre-muxed + sd/hd, so the original audio-only-or-nothing selector failed with "Requested format is not + available". A muxed mp4 is still a single file and Media3 plays its audio track. + - **YouTube audio is yt-dlp, embedded.** Chaquopy runs an in-process CPython with real yt-dlp; see `youtube/YouTubeAudio.kt` and `src/main/python/ytaudio.py`. No Termux, no podkit, no server round trip. @@ -191,6 +208,22 @@ out of a possibly-partial MP3, and the JSON path covers the shows that mark chap alias and password, because that is the key already on the phone. Never swap it for a fresh one — that costs another uninstall. Never use it for a release. +- **Facebook is individual videos only.** Facebook pages publish no RSS, so there is no + equivalent of subscribing to a YouTube channel. Shared videos land in their own "Saved + Facebook videos" container, because the container carries the kind that decides how its + episodes are fetched and one row cannot be honest about two sources. + +- **Facebook tells a non-browser nothing.** It serves no `og:` tags at all -- verified with the + app's own user agent -- so a shared video gets a fallback title of "Video <id>". yt-dlp knows + the real title (`probe` in `ytaudio.py` returns it, and `YouTubeAudio.probe` is still sitting + in the reachability allowlist waiting for a caller). Backfilling from it is the obvious fix + and is not done yet. + +- **Anything entering the queue is downloaded immediately, on any network.** The hook is + `QueueRepository`'s `onQueued`, fired with newly-present ids only, because the queue is the + one choke point every route goes through. The one-off job deliberately has no network + constraint; only the periodic sweep is wifi-only. + - **Feeds are big.** A real subscription in testing produced 2753 episodes in one refresh. Any per-episode work — a query in a loop, an O(n²) match — needs to be considered at that scale. |
