Features:
- Rating hover dots: hovering a rating cell shows 5 clickable slots
(RatingDelegate); click slot k sets k stars, clicking the current count
clears. Undoable, library-only (ratings never rewrite music files).
- Search bar moved into the Library header strip, right of the Library
button, so the tracklist top aligns with the playlist tree.
- Right-click "Download Album Art…": iTunes Search API (no key), off-thread
fetch, preview/confirm dialog with Next Result, embeds via write_artwork
+ size refresh, invalidates the MPRIS art cache.
Fixes:
- MPRIS media keys: PropertiesChanged sent invalidated_properties as "av"
instead of "as", so gsd-media-keys dropped it and never MRU-bumped
lintunes (why the play/pause key kept waking stale players). Now an
explicit QDBusArgument string array; loopback-verified sa{sv}as.
- Exit segfault: ordered Player.shutdown() (stop, clear source, detach
buffer/audio outputs) from closeEvent/aboutToQuit; scripted run exits 0.
- BT zero-volume after pause/resume: volume re-applied on resume, device
swap, and BufferedMedia (needs verify on the affected machine).
Cruft sweep:
- Tag writes filtered to EDITABLE_FIELDS; failures logged + surfaced in
the status bar (was a swallowed print).
- O(n²) import fixed (location index + cached max track id); O(1)
refresh/reveal via TrackTableModel row index.
- lastfm: scrobble-queue thread lock, login prefs write marshalled to the
GUI thread, JSON/raise_for_status order fixed.
- Shared read_json/write_json; TableSettingsMixin dedupes view settings.
TASKS.md rewritten as a resumable board; tests in tests/test_round17.py
(251 total pass).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.0 KiB
6.0 KiB
LinTunes — task board
Legend: [ ] todo · [~] in progress · [x] done.
When a round closes, move its finished items to tasks-done.md.
Round 17 (current) — TASKS.md batch + cruft cleanup
Plan reference: ~/.claude/plans/can-you-take-a-drifting-sonnet.md.
Tests land in tests/test_round17.py.
Phase 1 — cruft & correctness quick wins
- 1a. Tag-write filter:
_write_track_tagsonly writes fields intagging.EDITABLE_FIELDS; rating/size edits become library-JSON-only and no longer rewrite music files (library_manager.py) - 1b. Replace
print()with logging; newtag_write_failedsignal surfaced in the status bar (library_manager.py,file_importer.py,main_window.py,main.py) - 1c. O(n²) import fix:
{location: track}index perimport_paths+ cached_max_track_idfor O(1)new_track_id(file_importer.py,library_manager.py) - 1d.
TrackTableModel._row_by_idindex → O(1)refresh_track/reveal_track(track_table.py) - 1e.
threading.Lockaround the scrobble-queue load/mutate/save (lastfm.py) - 1f. Last.fm login: prefs write marshalled to the GUI thread via an
internal signal (
lastfm.py) - 1g. Shared
read_json/write_jsoninjson_storage.py; drop the duplicates inconflict_resolver.py - 1h.
lastfm._call: try JSON first, fall back toraise_for_status()on non-JSON bodies
Phases 2–4 — player & desktop integration
- Task F: Bluetooth zero-volume after pause/resume — factored
_apply_volume(); re-applied on resume, aftersetDevice, and onBufferedMedia(player.py) - verify Task F on the Bluetooth machine: pause ≥30 s on BT headphones, resume, must be audible without seeking
- Task E: exit segfault — idempotent
Player.shutdown()(stop → clear source → detach buffer/audio outputs → disconnect QMediaDevices), called fromcloseEvent+aboutToQuit. Verified 2026-07-02: scripted run (import → play to end → close) exits 0, no segfault. - Task C: MPRIS play/pause commandeering — root cause found & fixed:
_notifysentPropertiesChangedwithinvalidated_propertiesmarshalled asavinstead ofas, so gsd-media-keys dropped it and never bumped LinTunes in its media-key MRU. Now an explicit empty string-array viaQDBusArgument(mpris.py). Verified 2026-07-02 by D-Bus loopback: old code's signature wassa{sv}av, new issa{sv}as(test_round17). - verify Task C in practice: play in LinTunes with a stale YouTube
tab around; the media key should control only LinTunes. Fallbacks if
GNOME still misroutes (documented, not built):
org.gnome.SettingsDaemon.MediaKeys.GrabMediaPlayerKeys, bus-name re-registration on play.
Phases 5–7 — features
- Task B: search bar moved into the Library header strip
(
_top_strip), directly right of the sidebar Library button, so the browse/tracklist top aligns with the playlist tree (library_view.py) - Task A: rating hover dots — hovering a rating cell shows 5 slots
(★ where rated, • where not); click slot k sets k stars; clicking the
current rating clears it.
RatingDelegate+rating_editedsignal, wired tomanager.edit_track_fieldsin both views (track_table.py,library_view.py,playlist_view.py). Rating is library-only (no music-file rewrite), undoable with Ctrl+Z. - Task D: right-click "Download Album Art…" — iTunes Search API
(no key; upscale artworkUrl100 → 600x600), off-thread fetch,
confirmation dialog with preview + Next Result, embed via
tagging.write_artwork+ size refresh, invalidate MPRIS art cache (newart_search.py, newgui/album_art_dialog.py,track_table.py,main_window.py,mpris.py) - verify Tasks A/B/D by eye in the running app (hover/click ratings, search-bar alignment, art download on a real album)
Phase 8
TableSettingsMixin: dedupe sort/columns/width persistence between LibraryView and PlaylistView (newgui/table_settings.py)
Standalone maintenance (manual, not code work)
- Artwork recovery against the LIVE data dir (embeds the 790
album-propagated covers; gets art-bearing coverage to 100%):
1.
python3 scripts/audit_artwork.py --data-dir <data>(read-only re-check) 2.python3 scripts/recover_artwork.py --data-dir <data> --dry-run→ review 3. re-run with--write;audit_artwork.pyagain to confirm Note:lintunes/itc.pyis a live dependency ofrecover_artwork.py(tests intest_round9.py) — do not delete as "unused".
Parked / deferred
- Smart playlists Phase 2: nested-group editing UI in the criteria dialog (import + evaluation of nested groups already works; imported nested playlists are read-only until then). Big change, on hold.
- Live-sync v2: per-playlist tombstones (so a delete on one machine isn't resurrected by the union merge during a simultaneous edit) + optional per-change accept/refuse review. Low priority — trav doesn't edit on both machines at once; v1 handles sequential use.
- Data-dir location as an in-app preference (currently only
--data-dir/--save-config). - Album-art grid view: a browsable grid of album covers for the library (the artwork-coverage investigation that motivated it is answered & tooled — see Standalone maintenance above).
- Off-thread artwork reads:
SidebarArt.set_trackand the Get Info dialog read embedded art synchronously on the GUI thread (fine for local files; worth revisiting for large FLACs). - Progress UI (or background thread) for multi-selection tag writes in
edit_tracks_fields— a big batch currently blocks the UI. - More aggressive media-key commandeering beyond the Task C fix, if the real-machine verification shows GNOME still routing keys elsewhere.