mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
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)
This commit is contained in:
@@ -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']);
|
||||
});
|
||||
});
|
||||
@@ -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[] {
|
||||
|
||||
Reference in New Issue
Block a user