mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
9509b78073
* fix(library): sort albums by the artist the row actually shows (#1217) The album browse ordered by MAX(t.artist) -- the raw track artist -- while the row mappers derive the displayed artist with pick_album_group_artist, which prefers the album artist. On an album with featured guests the two differ ("Alpha feat. Zulu" vs "Alpha"), so the album sorted under a name the user never sees and fell out of its artist's year run, sometimes landing behind an entirely different artist. Order by the same rule the row displays, via a shared SQL expression (sql_display_artist_from) that both query shapes feed their own columns into: aggregates for the grouped album browse, projected columns for the multi-library dedup path. That dedup path used to build its ORDER BY by string-replacing MAX(t.x) out of the grouped SQL, which only held while every sort key was a bare aggregate and would have silently mangled the new expression -- it now builds directly from its own columns. Regression test uses a featured-guest album and a second artist that sorts between the two spellings; it fails against the old expression (album lands last) and passes with the fix. * docs(changelog): add entry for PR #1292 * fix(library): bind the album sort to the album aggregates in the scoped browse The scoped browse (scope_merge) feeds the same ORDER BY into three query shapes. Two of them GROUP BY t.album_id and selected the sort columns unaliased, so `artist` / `album_artist` in the ORDER BY resolved to bare table columns -- taken from an arbitrary row of the group -- rather than to the MAX() aggregates the row mapper reads. The featured-guest defect (#1217) therefore survived on that path: sorting could still key on a track credit ("Alpha feat. Zulu") instead of the album artist. Alias the sort columns so the ORDER BY binds to the aggregates. Adds a scoped regression test that fails against the previous expression (featured album lands behind a different artist) and passes now. * fix(library): give the scoped GROUP BY browse its own album sort key Review F1. The scoped browse ran `GROUP BY t.album_id` but received the dedup shape's ORDER BY, whose display-artist key is a CASE over bare `artist` / `album_artist`. SQLite substitutes a result alias into ORDER BY only when the whole term is a plain identifier: `ORDER BY artist COLLATE NOCASE` does bind to `MAX(t.artist) AS artist`, but the same name inside a CASE resolves against `track` instead — and a bare column in a grouped query is read from an arbitrary row of the group. Aliasing the select list, the first attempt at this, therefore never fixed the CASE form: an album whose tracks carry `album_artist` unevenly sorted under whichever row SQLite happened to pick. Verified both halves against SQLite directly. Split the parameter: the two `GROUP BY t.album_id` branches now take a grouped key (aggregates inside the CASE), and only the dedup subquery, which really does project plain columns, takes the deduped one. The genre and scope-list callers pass plain-identifier keys, which alias- resolve correctly in either shape, so they hand the same string to both. Tests (F2, F3): - Deterministic guard on the grouped key — no bare column may survive in it. The behavioural tests can pass by luck when the arbitrary row is a favourable one; this one cannot. - Multi-track album with a sparse `album_artist`, the shape the previous single-track tests could not reach. - SQL/Rust parity for the aggregate form of the display-artist rule, over multi-row groups, guarding drift from `pick_album_group_artist`.