mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
fix: show album artist on featured compilation cards (#979)
* fix: show album artist on featured compilation cards The 'Also featured on' cards are synthesised from search3 child songs, which only read the flat `albumArtist` field. Compilation children leave that empty — the album-artist credit lives in OpenSubsonic's structured `albumArtists` / `displayAlbumArtist` — so the card fell back to the '—' placeholder. Carry the structured credit (and the display string) onto the synthesised album so it resolves a name (and stays navigable when the server supplies an id). * docs: changelog for featured-compilation artist credit (#979)
This commit is contained in:
committed by
GitHub
parent
47e16ebfef
commit
a7c79ee210
@@ -872,6 +872,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
* Album cards on a **Genre** page split multi-artist credits into individually clickable artist links, matching the rest of the app.
|
||||
* On the **Artists** page the `#` index button now holds only names that start with a number; accented and non-Latin names (Æ Ø Å, Chinese, Japanese, Cyrillic, …) move to a new **Other** section instead of the `#` catch-all.
|
||||
|
||||
### Artist detail — credit on "Also featured on" compilations
|
||||
|
||||
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#979](https://github.com/Psychotoxical/psysonic/pull/979)**
|
||||
|
||||
* Compilation albums under **Also featured on** show their album artist (e.g. *Various Artists*) again instead of a bare `—`, and the credit links to the artist when the server provides one.
|
||||
|
||||
## [1.46.0] - 2026-05-18
|
||||
|
||||
> **🙏 Special thanks to [@zz5zz](https://github.com/zz5zz)** for his tireless quirk-spotting and bug reports on the [Psysonic Discord](https://discord.gg/AMnDRErm4u) — several of the polish fixes in this release landed directly off the back of his messages.
|
||||
|
||||
@@ -72,6 +72,8 @@ export interface SubsonicSong {
|
||||
genre?: string;
|
||||
path?: string;
|
||||
albumArtist?: string;
|
||||
/** OpenSubsonic: single-string album-artist for display (mirrors `albumArtists` joined). */
|
||||
displayAlbumArtist?: string;
|
||||
/** ISRC code when available (e.g., Navidrome) */
|
||||
isrc?: string;
|
||||
/** Times the track has been played, surfaced by Navidrome's Subsonic API. */
|
||||
|
||||
@@ -72,6 +72,38 @@ describe('useArtistDetailData — id-gated info', () => {
|
||||
await waitFor(() => expect(result.current.info).toEqual({ largeImageUrl: 'B.jpg' }));
|
||||
});
|
||||
|
||||
it('keeps the album-artist credit on featured compilation albums', async () => {
|
||||
// "Also featured on" synthesises albums from search3 child songs. A
|
||||
// compilation has no flat `albumArtist` on the child — the credit lives in
|
||||
// OpenSubsonic's structured `albumArtists` (and/or `displayAlbumArtist`).
|
||||
// Dropping it made the card render "—" instead of "Various Artists".
|
||||
vi.mocked(getArtist).mockResolvedValue({ artist: { id: 'A', name: 'A' }, albums: [] } as any);
|
||||
vi.mocked(search).mockResolvedValue({
|
||||
artists: [],
|
||||
albums: [],
|
||||
songs: [
|
||||
{
|
||||
id: 's1', title: 'Track', artistId: 'A', artist: 'A',
|
||||
album: 'A Compilation', albumId: 'comp1', coverArt: 'c1', duration: 100,
|
||||
albumArtists: [{ id: 'va', name: 'Various Artists' }],
|
||||
},
|
||||
{
|
||||
id: 's2', title: 'Other', artistId: 'A', artist: 'A',
|
||||
album: 'Display Only', albumId: 'comp2', coverArt: 'c2', duration: 90,
|
||||
displayAlbumArtist: 'Various Artists',
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
|
||||
const { result } = renderHook(() => useArtistDetailData('A'));
|
||||
|
||||
await waitFor(() => expect(result.current.featuredAlbums).toHaveLength(2));
|
||||
const structured = result.current.featuredAlbums.find(a => a.id === 'comp1');
|
||||
const displayOnly = result.current.featuredAlbums.find(a => a.id === 'comp2');
|
||||
expect(structured?.artists).toEqual([{ id: 'va', name: 'Various Artists' }]);
|
||||
expect(displayOnly?.artist).toBe('Various Artists');
|
||||
});
|
||||
|
||||
it('ignores a late-arriving resolve for a stale id', async () => {
|
||||
vi.mocked(getArtist).mockImplementation(async (id) => (
|
||||
{ artist: { id, name: id }, albums: [] } as any
|
||||
|
||||
@@ -150,8 +150,13 @@ export function useArtistDetailData(
|
||||
albumMap.set(song.albumId, {
|
||||
id: song.albumId,
|
||||
name: song.album,
|
||||
artist: song.albumArtist ?? '',
|
||||
// search3 children carry the album-artist credit in OpenSubsonic's
|
||||
// structured `albumArtists` / `displayAlbumArtist` (e.g. "Various
|
||||
// Artists" on compilations), not the flat `albumArtist` field — keep
|
||||
// all of them so the card resolves a name instead of "—".
|
||||
artist: song.albumArtist ?? song.displayAlbumArtist ?? '',
|
||||
artistId: '',
|
||||
artists: song.albumArtists,
|
||||
coverArt: song.coverArt,
|
||||
songCount: 1,
|
||||
duration: song.duration,
|
||||
|
||||
Reference in New Issue
Block a user