summaryrefslogtreecommitdiff
path: root/CLAUDE.md
diff options
context:
space:
mode:
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md33
1 files changed, 33 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 3e41672..623ba1b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -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.