Commit Graph

8 Commits

Author SHA1 Message Date
33d83618dc Fix correctness and robustness issues found in a full code review
Correctness:

- MPD command reads no longer abandon a response half-way. The socket's
  500 ms read timeout exists so the idle loop can poll the shutdown flag,
  but it applied to every read, so a slow reply left unread bytes that the
  next command parsed as its own. Add MpdConn::read_line/read_exact, which
  retry across that timeout up to a 15 s deadline, and a desynced flag that
  forces a reconnect when a read is genuinely abandoned. read_response now
  returns Option so a truncated reply is a failure rather than an empty
  result -- a timed-out currentsong used to look like "MPD is stopped".
- resolve_cover returned a covers/<name> path even when the file was
  missing, so a deleted image produced a broken <img> instead of the
  placeholder. Gate the return value on the same existence check as the
  copy list.
- enrich --force no longer runs DELETE FROM album_cache. That discarded
  pin-album entries, MPD-extracted cover paths, and any genre the re-fetch
  then failed to find. Select every album via all_scrobbled_albums and
  overwrite rows in place instead.
- Album grouping, cover fallback and source attribution disagreed: the
  dominant_source subquery and artist_cover matched on album name alone, so
  two records sharing a title cross-contaminated. Extract one
  album_group_key_expr and use it everywhere.
- Escape quotes and backslashes in MusicBrainz Lucene queries; a title
  containing a quote closed the term early and matched nothing.
- Size the HTML cover grid from the period's own limit, so all-time no
  longer shows fewer covers than the table beneath it.
- Fix a panic in the terminal report: top_genres ranks broad single-word
  genres last, so first() is not the busiest row, and a later genre with
  more plays overflowed the bar width. Scale to the true maximum and clamp
  the bar arithmetic.
- Bound readpicture transfers by size and chunk count so a bogus size:
  header cannot drive an unbounded allocation or an endless loop.

Robustness:

- Kill the playerctl children before joining their reader threads. The
  threads block until stdout closes, which only happened because an
  interactive Ctrl+C signals the whole process group; under a supervisor
  that signals one PID, shutdown hung and left orphans.
- Reject --limit and --all-time-limit below 1. SQLite reads a negative
  LIMIT as unlimited, silently dumping the whole library.
- Set a 5 s busy_timeout, so running report against a live watch does not
  fail immediately with "database is locked".
- Replace panics in default_db_path, covers_dir and the enrich queries with
  reported errors, and check PRAGMA table_info for the source column
  migration instead of discarding every ALTER TABLE error.

Cleanups:

- Collapse eight reset_*_timestamps functions into reset_retry_timestamps
  with a RetryScope enum.
- Share one FNV-1a helper (enrich::cover_stem) between the MPD and iTunes
  filename generators.
- Use params_from_iter to remove six duplicated query_map branches.
- Extract source_dot_cell and reuse db::split_genre_list in the report.
- Skip the genre lookup when only covers were requested, saving a request
  and a rate-limit sleep per album.
- Keep the cached genre in pin-album when MusicBrainz returns nothing and a
  --cover-url was supplied.
2026-07-25 23:19:21 +01:00
275449d6d7 Refactor enrich CLI into pipeline, fetch, and retry modes 2026-03-23 22:52:01 +00:00
02ff0b4c97 Fix incomplete MPD cover transfers, add cover revalidation, and allow retrying missing MPD genres 2026-03-23 22:33:45 +00:00
d199abdc68 Cover repair for MPD 2026-03-23 19:37:48 +00:00
10f3162b31 Clean-up 2026-03-22 19:56:47 +00:00
5bb968e255 README update 2026-03-22 19:55:23 +00:00
10483822ee Renaming to scrbblr 2026-03-22 08:46:09 +00:00
24df1854f5 The beginning of something great 2026-03-20 20:00:10 +00:00