From 116196f0d455e540e8366a73bc7dcd99dadce7ee Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:50:47 +0200 Subject: [PATCH] fix(albums): order each artist's albums by title when sorting by artist (#1115) * fix(albums): order each artist's albums by title when sorting by artist Browsing albums by artist left the albums within each artist in an undefined order. The local-index sort only emitted the artist key; it now appends album title as a secondary key (and artist as the tiebreak for the by-name sort), matching the network path's per-page ordering. * docs(changelog): note album sort-within-artist fix (#1115) --- CHANGELOG.md | 6 +++ src/utils/library/albumBrowseSort.test.ts | 51 +++++++++++++++++++++++ src/utils/library/albumBrowseSort.ts | 13 +++++- 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/utils/library/albumBrowseSort.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 75553adc..5fd9304f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * The Previous / Play-Pause / Next buttons in the Windows taskbar thumbnail preview (the popup shown when hovering the taskbar icon) had stopped appearing. They are back, and the middle button's icon again reflects the current playback state. +### Album order within each artist when sorting by artist + +**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1115](https://github.com/Psychotoxical/psysonic/pull/1115)**, suggested by [@kingley82](https://github.com/kingley82) + +* When browsing albums sorted by artist, each artist's albums appeared in an arbitrary order. They are now ordered A–Z by album title within each artist. + ## [1.48.1] - 2026-06-15 diff --git a/src/utils/library/albumBrowseSort.test.ts b/src/utils/library/albumBrowseSort.test.ts new file mode 100644 index 00000000..ad5c51e8 --- /dev/null +++ b/src/utils/library/albumBrowseSort.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from 'vitest'; +import type { SubsonicAlbum } from '../../api/subsonicTypes'; +import { albumSortClauses, sortSubsonicAlbums } from './albumBrowseSort'; + +const album = (artist: string, name: string): SubsonicAlbum => + ({ id: `${artist}-${name}`, artist, name }) as SubsonicAlbum; + +describe('albumSortClauses', () => { + it('sorts by artist then album name', () => { + expect(albumSortClauses('alphabeticalByArtist')).toEqual([ + { field: 'artist', dir: 'asc' }, + { field: 'name', dir: 'asc' }, + ]); + }); + + it('sorts by album name then artist', () => { + expect(albumSortClauses('alphabeticalByName')).toEqual([ + { field: 'name', dir: 'asc' }, + { field: 'artist', dir: 'asc' }, + ]); + }); +}); + +describe('sortSubsonicAlbums', () => { + it('orders each artist group by album name when sorting by artist', () => { + const input = [ + album('Rammstein', 'Sehnsucht'), + album('Duran Duran', 'Rio'), + album('Rammstein', 'Mutter'), + album('Duran Duran', 'DD'), + album('Duran Duran', 'The Wedding Album'), + ]; + const ordered = sortSubsonicAlbums(input, 'alphabeticalByArtist').map(a => `${a.artist} - ${a.name}`); + expect(ordered).toEqual([ + 'Duran Duran - DD', + 'Duran Duran - Rio', + 'Duran Duran - The Wedding Album', + 'Rammstein - Mutter', + 'Rammstein - Sehnsucht', + ]); + }); + + it('breaks album-name ties by artist when sorting by name', () => { + const input = [ + album('Zebra', 'Greatest Hits'), + album('Alpha', 'Greatest Hits'), + ]; + const ordered = sortSubsonicAlbums(input, 'alphabeticalByName').map(a => a.artist); + expect(ordered).toEqual(['Alpha', 'Zebra']); + }); +}); diff --git a/src/utils/library/albumBrowseSort.ts b/src/utils/library/albumBrowseSort.ts index 9d054443..dc55087e 100644 --- a/src/utils/library/albumBrowseSort.ts +++ b/src/utils/library/albumBrowseSort.ts @@ -4,10 +4,19 @@ import type { LibrarySortClause } from '../../api/library'; export type AlbumBrowseSort = 'alphabeticalByName' | 'alphabeticalByArtist'; export function albumSortClauses(sort: AlbumBrowseSort): LibrarySortClause[] { + // Always append a secondary key so albums sharing the primary key keep a + // stable order — by artist groups each artist's albums by title (rather than + // an undefined order within the artist), mirroring `sortSubsonicAlbums`. if (sort === 'alphabeticalByArtist') { - return [{ field: 'artist', dir: 'asc' }]; + return [ + { field: 'artist', dir: 'asc' }, + { field: 'name', dir: 'asc' }, + ]; } - return [{ field: 'name', dir: 'asc' }]; + return [ + { field: 'name', dir: 'asc' }, + { field: 'artist', dir: 'asc' }, + ]; } export function sortSubsonicAlbums(albums: SubsonicAlbum[], sort: AlbumBrowseSort): SubsonicAlbum[] {