v0.20.4: the song that wasn't there
trav couldn't play the first track of his playlist. Shuffle off gave him song two; shuffle on gave him a random one. The player was right both times: library.json named a song whose file was not on the device, and skipping an unplayable file advances one slot. Tapping any song that *is* there plays exactly that song, shuffle or not. The file was missing because a gvfs-MTP mount can hold a phantom directory -- one it lists happily while the device has no such folder. Every write into it fails EIO, and mkdir(exist_ok=True) sees the phantom and does nothing, so it never heals; only remounting clears it. That folder was new because Round 52's retag moved the file under a new artist. So a copy that raises OSError now costs that song, not the sync, and the song is left out of the m3u and library.json. Aborting cost 2,400 songs for one folder; counting it present anyway put a song in the manifest that isn't on the device, which is the bug you could hear. The names come back in the summary and a dialog rather than vanishing. Planning is a worker now. plan_andtunes_sync is pure and writes nothing, but it walks every file on the device, and over MTP that is thousands of round trips -- on the GUI thread it froze the window and GNOME offered to kill LinTunes, which is how a sync got force-quit halfway through. And the sync stopped going quiet at the end. "Album art 501/501" is emitted before the last album, and then _write_index, _ship_buttons and _prune_empty_dirs ran silently: 501 exists() stats over MTP, ~840 KB of writes and a full tree walk, with the progress line frozen. They report now, and _write_index reads Art/ in one listing instead of a stat per album. andTunes 0.2.2: the wheel scrolls the other way in lists, and smoothly -- a detent adds to a pixel debt that a Choreographer callback pays off a fraction per frame, so one detent eases to a stop and a fast spin blends into one movement instead of teleporting a row at a time. Volume is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SKXUgsBBwe3qaHEjeV8ubP
This commit is contained in:
@@ -1,5 +1,42 @@
|
||||
## Done
|
||||
|
||||
### Round 55 (2026-09-14) — the song that wasn't there (v0.20.4, andTunes 0.2.2)
|
||||
|
||||
trav couldn't play the first track of his playlist: shuffle off gave him song
|
||||
2, shuffle on gave him a random one. The player was right both times —
|
||||
`library.json` named a song whose file was not on the device, and skipping an
|
||||
unplayable file advances one slot. Verified on the Rabbit: tapping any song
|
||||
that *is* there plays exactly that song, shuffle or not.
|
||||
|
||||
- [x] **The phantom directory.** `Media/Dionne Farris/` existed as far as gvfs
|
||||
was concerned — it would list the album inside it — and did not exist at
|
||||
all per `adb`. Every write into it failed `EIO`, and
|
||||
`mkdir(exist_ok=True)` saw the phantom and did nothing, so it could never
|
||||
heal while the mount lived. Freshly made folders worked fine, so it was
|
||||
stale cache, not a broken mount. Only remounting clears it. That folder
|
||||
was new because Round 52's retag moved the file under a new artist.
|
||||
- [x] **A refused copy costs that song, not the sync**, and the song is left
|
||||
out of the m3u and `library.json`. Aborting cost 2,400 songs for one
|
||||
folder; counting it present anyway put a song in the manifest that isn't
|
||||
on the device, which is what trav was hearing. The names come back in
|
||||
the summary and a dialog, so it is never silent.
|
||||
- [x] **Planning is a worker** (`AndTunesPlanWorker`). It is pure and writes
|
||||
nothing, but it walks every file on the device, and over MTP that is
|
||||
thousands of round trips — on the GUI thread it froze the window and
|
||||
GNOME offered to kill LinTunes, which is how a sync got force-quit
|
||||
halfway. `disk_usage` rides along, being one more MTP call.
|
||||
- [x] **The tail of the sync says what it's doing.** "Album art 501/501" is
|
||||
emitted *before* the last album, and then `_write_index`,
|
||||
`_ship_buttons` and `_prune_empty_dirs` ran silently — 501 `exists()`
|
||||
stats over MTP, ~840 KB of writes and a full tree walk with the progress
|
||||
line frozen. They report now, and `_write_index` reads `Art/` in one
|
||||
listing instead of a stat per album.
|
||||
- [x] **The wheel scrolls smoothly, and the other way** (`WheelScroll`). A
|
||||
detent adds to a pixel debt that a Choreographer callback pays off a
|
||||
fraction per frame, so one detent eases to a stop and a fast spin blends
|
||||
into one movement instead of teleporting a row at a time. Direction
|
||||
flipped in lists only; volume on the other screens is unchanged.
|
||||
|
||||
### Round 54 (2026-09-14) — one artist spelled two ways (v0.20.3, andTunes 0.2.1)
|
||||
|
||||
trav synced to the Rabbit, the sync reported success, and andTunes answered
|
||||
|
||||
Reference in New Issue
Block a user