Files
lintunes/TASKS.md
trav d46374967d Fix silent resume after walking away: seek-in-place re-primes the sink
Rediagnosed (was thought Bluetooth-specific): on the Debian 13 machine,
resuming after the machine sat idle plays nothing even though decoding
runs (visualizer moves) — the OS suspends the audio sink and it comes
back dead until a seek re-primes it. Resuming after a pause of 30s+ now
does a seek-in-place before play(), automating the manual "rewind
slightly" workaround without losing the position. The volume re-apply
from the earlier fix stays (harmless, covers device swaps).

Also fold the new backlog items into the TASKS.md board.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 15:56:11 -04:00

8.2 KiB
Raw Blame History

LinTunes — task board

Legend: [ ] todo · [~] in progress · [x] done. When a round closes, move its finished items to tasks-done.md.

New backlog (added 2026-07-02, next round)

  • we might need an equalizer at some point but I'm not sure how it should show up
  • I need to be able to drag a track or tracks from the gnome file browser into a playlist or just into the Library. It should copy the files into the 'itunes music' folder where we store all songs. It should store them where it makes sense for them to go 'artist -> album'. If there is no artist it should go in a standard unknown artist folder. The tracks should show up where they are dragged to in the playlist (if they're being dragged into a playlist) (Note: window/table drops + copy-to-Artist/Album/ + unknown-artist fallback already exist — file_importer.organized_destination, MainWindow.dropEvent, playlist_view.files_dropped. Remaining gap to verify/build: inserting at the exact drop position in the playlist rather than appending.)
  • if we rename an artist or album we should move the files. Basically we should keep the music folder organized in the same way the library itself is organized inside lintunes. If we add an artist the track moves from the unknown folder to the artist folder (make a folder if we need to) (Touches the "never move the user's music files" rule in CLAUDE.md — this would deliberately relax it for metadata renames; needs care around Syncthing + the other machine.)
  • one of the sliders in the gray adjustments in preferences should be for the color of the equalizer visualizer. This will set the color of the gray when it's in gray mode. We still toggle between color, gray and off when clicking. Also what mode it's in (gray/color/off) should persist between launches of the app.

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_tags only writes fields in tagging.EDITABLE_FIELDS; rating/size edits become library-JSON-only and no longer rewrite music files (library_manager.py)
  • 1b. Replace print() with logging; new tag_write_failed signal surfaced in the status bar (library_manager.py, file_importer.py, main_window.py, main.py)
  • 1c. O(n²) import fix: {location: track} index per import_paths + cached _max_track_id for O(1) new_track_id (file_importer.py, library_manager.py)
  • 1d. TrackTableModel._row_by_id index → O(1) refresh_track / reveal_track (track_table.py)
  • 1e. threading.Lock around 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_json in json_storage.py; drop the duplicates in conflict_resolver.py
  • 1h. lastfm._call: try JSON first, fall back to raise_for_status() on non-JSON bodies

Phases 24 — player & desktop integration

  • Task F: silent resume after pause — REDIAGNOSED per trav 2026-07-02: not Bluetooth-specific; happens on the Debian 13 machine whenever he walks away, resumes, and gets no sound despite the visualizer moving (decoding runs, the idle-suspended audio sink comes back dead; a slight rewind fixed it). Two-part fix in player.py: (1) _apply_volume() re-applied on resume / setDevice / BufferedMedia; (2) resume after a pause ≥30 s (RESUME_NUDGE_THRESHOLD_S) does a seek-in-place first — the automated version of the manual rewind, without losing the playback position.
  • verify Task F on the Debian 13 machine: play, pause, walk away ≥1 min, resume — must be audible without manually seeking
  • Task E: exit segfault — idempotent Player.shutdown() (stop → clear source → detach buffer/audio outputs → disconnect QMediaDevices), called from closeEvent + 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: _notify sent PropertiesChanged with invalidated_properties marshalled as av instead of as, so gsd-media-keys dropped it and never bumped LinTunes in its media-key MRU. Now an explicit empty string-array via QDBusArgument (mpris.py). Verified 2026-07-02 by D-Bus loopback: old code's signature was sa{sv}av, new is sa{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 57 — 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_edited signal, wired to manager.edit_track_fields in 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 (new art_search.py, new gui/album_art_dialog.py, track_table.py, main_window.py, mpris.py). Follow-up 2026-07-02: "Use for All N Songs in Album" button applies the art to every library track on that album, not just the selection.
  • 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 (new gui/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.py again to confirm Note: lintunes/itc.py is a live dependency of recover_artwork.py (tests in test_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_track and 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.