v0.20.0: the Rabbit's plays come home

andTunes phase 3. The R1 is now one more machine in the Round 38 play-count
model, and nothing lands on it that Android can't play.

Play counts: andTunes 0.2.0 counts a play on a natural finish (LinTunes'
rule) and keeps Music/andTunes/plays/andtunes-<install id>.json in exactly
the per-machine-totals journal shape, written beside the old one and renamed
over it. Each sync first folds it into <data_dir>/plays/ with a new
play_journal.merge_totals, the per-track max: the file now has two homes and
both desktops may bring it back, so the max converges and an older copy can
never pull a count down. Nothing is written when nothing moved, and
PlayJournal.load needed no change.

Format gate: the planner reuses export/web_support.conversion_for outright,
since Android's MediaPlayer decodes the browser's set. FairPlay is refused
and reported; ALAC, AIFF and oddities land as FLAC, converted once into a
per-track cache and size-diffed after that. With no ffmpeg, the export's
"sync without them?" question. A file ffmpeg can't read costs that song,
not the sync.

"Sync Playlist to Rabbit (Auxio)" is retired from the menu; device_sync's
helpers stay because export and andTunes import them.

The dev fixture grew a real ALAC track, a Protected AAC .m4p and an "Odd
Formats" playlist. Verified on the R1 with it: AIFF and ALAC arrived as
FLAC and played, the FairPlay track was refused, four plays came back on the
next sync and each track's effective count rose by exactly one, and a third
sync copied nothing.

Found along the way: USB re-enumeration can wedge gvfsd-mtp, after which
anything touching the mount (find_device, the GUI tests) hangs in an
uninterruptible wait. CLAUDE.md now has the recovery.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ZCVBTJRFJ2XfMshu2gtUv
This commit is contained in:
2026-09-11 15:20:44 -05:00
co-authored by Claude Opus 5
parent 9483d8951b
commit 81b7f6767c
17 changed files with 864 additions and 127 deletions
+17 -10
View File
@@ -6,7 +6,9 @@ A music player for the Rabbit R1 that never scans anything: LinTunes writes
Legend: `[ ]` todo · `[~]` in progress · `[x]` done.
**Status: 0.1.0 plays music (2026-09-11, LinTunes round 50).** Built with
**Status: phases 1–3 done (2026-09-11, LinTunes rounds 50–51, andTunes
0.2.0).** What's left is the *Parked* list, pulled in as trav finds he needs
it. Built with
`python3 andtunes/build.py` — plain Java, no Gradle, nothing downloaded (see
`README.md`, *Toolchain*). Phases are andTunes' own; which LinTunes round
each lands in is decided when it starts.
@@ -81,15 +83,20 @@ each lands in is decided when it starts.
Reinstall from the version on the device; with no adb, copies the APK
to `Download/` over MTP for a manual tap. APK committed at
`lintunes/android/andTunes.apk` (+ `andTunes.json`, its version).
- [ ] Format gate shaped like `export/web_support.py`: deny-by-default on
suffix, protected AAC refused, anything else unplayable converted to
**FLAC** (never a lossy re-encode).
- [ ] Play counts back: the app writes `plays/andtunes-<installid>.json` in
the per-machine-totals shape of `storage/play_journal.py`; sync copies
it back and folds it in. The R1 becomes one more machine in the Round 38
model — no new merge rules.
- [ ] Retire `Sync Playlist to Rabbit (Auxio)` now that andTunes covers it.
(trav's R1 has no old `Music/<Playlist>/` folders, so no cleanup.)
- [x] Format gate (LinTunes 0.20.0): sync reuses
`export/web_support.conversion_for` outright, because Android's
MediaPlayer decodes the same set a browser does. Protected AAC is
refused and reported; ALAC, AIFF and anything else unplayable becomes
**FLAC** (never a lossy re-encode). Conversions are cached per track, so
they're converted once and size-diffed after that.
- [x] Play counts back (andTunes 0.2.0): a natural finish counts a play, as
it does in LinTunes. The app keeps `plays/andtunes-<install id>.json` in
the per-machine-totals shape of `storage/play_journal.py`. Sync folds it
into `<data_dir>/plays/` with the per-track max *before* copying
anything, so the R1 is one more machine in the Round 38 model and there
are no new merge rules.
- [x] Retired `Sync Playlist to Rabbit (Auxio)` from the menu. (trav's R1
has no old `Music/<Playlist>/` folders, so no cleanup was needed.)
## Parked