v3 library: Albums view — the client half of the album-condense work (#716)

* v3 library: Albums view — the client half of the album-condense work

Follow-up to the query_albums endpoint: the UI that consumes it, plus the
track-order plumbing the endpoint's track list needs.

- Albums view (a fourth view toggle next to grid/tree/folder): album cards
  (cover / title / artist / track count) from /api/library/albums,
  respecting the active filter drawer; clicking one opens the track list
  with per-track play and a Play-album button that feeds the play queue
  (falls back to plain playSong when the queue plugin is absent).
- Track order: the scanner now reads the feedpak `track`/`disc` fields
  (spec 1.12.0) into new nullable songs columns (idempotent ALTERs), and
  the album track list orders by the new `track` sort — disc, then track
  number, unauthored charts to the bottom by title. Charts without
  authored numbers keep working; they just sort alphabetically.
- The albums view persists like the other view choices.

3 new tests: manifest track/disc extraction (and unauthored -> None),
the disc->track->title sort order over /api/library, and the put()
round-trip. Full-suite failure set matches the known env baseline (one
tuner-config name swapped inside the suite-ordering flake family — the
file passes 25/25 in isolation on clean main).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nm7tHs1Yvjjtnnu4nzJgdN

* v3 albums: honour Genre/Match filters in album grid + detail (review fixes)

The Albums view only partially respected the filter drawer:

- /api/library/albums silently dropped the `genre` and `match` params the
  client sends via queryParams(), so with a Genre or Match filter active the
  album grid surfaced albums with zero matching tracks. Thread match_states/
  genre through the endpoint -> query_albums -> _build_where, mirroring the
  /api/library grid route. (SmartCollection/pass-through providers keep their
  existing kwarg handling.)

- The album-detail track list built its own params (provider/artist/album/
  sort only), so it ignored ALL active filters — the track list and the
  Play-album queue could include songs the user had filtered out. Reuse
  queryParams({...}, {catalog: true}) so detail honours the same filters as
  the grid while pinning this album's artist/album and track order.

+1 regression test (albums endpoint honours the Genre filter).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: byrongamatos <xasiklas@gmail.com>
This commit is contained in:
ChrisBeWithYou
2026-07-02 15:57:26 +02:00
committed by GitHub
co-authored by Claude Fable 5 byrongamatos
parent 2e4383524f
commit 0a8c8945ea
5 changed files with 185 additions and 8 deletions
+4
View File
@@ -921,6 +921,10 @@ def extract_meta(path: Path) -> dict:
"year": str(manifest.get("year", "") or ""),
# Primary genre from the feedpak `genres` list (spec 1.12.0); [0] = primary.
"genre": (lambda g: str(g[0]) if isinstance(g, list) and g else "")(manifest.get("genres")),
# Album track order from the feedpak `track`/`disc` fields (spec 1.12.0);
# None when unauthored (the album view then falls back to title order).
"track_number": (lambda v: int(v) if str(v if v is not None else "").strip().isdigit() else None)(manifest.get("track")),
"disc": (lambda v: int(v) if str(v if v is not None else "").strip().isdigit() else None)(manifest.get("disc")),
"duration": float(manifest.get("duration", 0) or 0),
"tuning_offsets": tuning_offsets, # caller maps to a name via tunings.tuning_name
"arrangements": arrangements,