From be21f7834f7e8761096bb6f910261dce447b4fd2 Mon Sep 17 00:00:00 2001 From: cucadmuh <49571317+cucadmuh@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:45:52 +0300 Subject: [PATCH] fix(composers): hide performer-only artists with zero composer credits (#963) * fix(composers): drop Navidrome role rows with zero composer albums Navidrome can list performer-only artists under role=composer with stats.composer.albumCount 0; filter them out of the Composers catalog so search no longer surfaces ghost entries like Apollo 440. * docs(changelog): credit zunoz on Psysonic Discord for PR #963 --- CHANGELOG.md | 7 +++++++ src/pages/Composers.tsx | 3 ++- src/utils/library/composerBrowse.test.ts | 17 +++++++++++++++++ src/utils/library/composerBrowse.ts | 10 ++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/utils/library/composerBrowse.test.ts create mode 100644 src/utils/library/composerBrowse.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 415f7174..e5246cc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -443,6 +443,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Multi-select rings on Artists, All Albums, Playlists, and related card grids use an inset `::after` overlay (same approach as card focus rings) instead of `outline` on `overflow: hidden` tiles — fixes top-row clipping and the ~1px gap vs the inner border on Wayland/WebKitGTK. +### Composers — hide performer-only artists from role catalog + +**By [@cucadmuh](https://github.com/cucadmuh), reported by zunoz on the Psysonic Discord, PR [#963](https://github.com/Psychotoxical/psysonic/pull/963)** + +* Navidrome's composer role list can include artists with zero composer album credits (e.g. Apollo 440 with performer albums only). Composers browse/search now drops rows where `stats.composer.albumCount` is zero so ghost composer cards no longer appear. + + ### In-page browse — virtual scroll and cover-art priority **By [@cucadmuh](https://github.com/cucadmuh), PR [#783](https://github.com/Psychotoxical/psysonic/pull/783)** diff --git a/src/pages/Composers.tsx b/src/pages/Composers.tsx index 619c3862..cc2da0fe 100644 --- a/src/pages/Composers.tsx +++ b/src/pages/Composers.tsx @@ -19,6 +19,7 @@ import { useNavigateToComposer } from '../hooks/useNavigateToComposer'; import { peekComposerBrowseScrollRestore } from '../store/composerBrowseSessionStore'; import { useScopedBrowseSearchQuery } from '../store/liveSearchScopeStore'; import { readComposerBrowseRestore } from '../utils/navigation/albumDetailNavigation'; +import { filterArtistsWithRoleAlbumCredits } from '../utils/library/composerBrowse'; import { usePerfProbeFlags } from '../utils/perf/perfFlags'; import { VirtualCardGrid } from '../components/VirtualCardGrid'; import OverlayScrollArea from '../components/OverlayScrollArea'; @@ -153,7 +154,7 @@ export default function Composers() { ndListArtistsByRole('composer', 0, 10000) .then(data => { if (cancelled) return; - setComposers(data); + setComposers(filterArtistsWithRoleAlbumCredits(data)); setLoading(false); }) .catch(err => { diff --git a/src/utils/library/composerBrowse.test.ts b/src/utils/library/composerBrowse.test.ts new file mode 100644 index 00000000..99199759 --- /dev/null +++ b/src/utils/library/composerBrowse.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest'; +import { filterArtistsWithRoleAlbumCredits } from './composerBrowse'; + +describe('filterArtistsWithRoleAlbumCredits', () => { + it('removes artists with zero role-scoped album count', () => { + const artists = [ + { id: '1', name: 'Bach', albumCount: 12 }, + { id: '2', name: 'Apollo 440', albumCount: 0 }, + ]; + expect(filterArtistsWithRoleAlbumCredits(artists)).toEqual([artists[0]]); + }); + + it('removes artists when role album count is missing', () => { + const artists = [{ id: '1', name: 'Ghost', albumCount: undefined }]; + expect(filterArtistsWithRoleAlbumCredits(artists)).toEqual([]); + }); +}); diff --git a/src/utils/library/composerBrowse.ts b/src/utils/library/composerBrowse.ts new file mode 100644 index 00000000..8e34a344 --- /dev/null +++ b/src/utils/library/composerBrowse.ts @@ -0,0 +1,10 @@ +import type { SubsonicArtist } from '../../api/subsonicTypes'; + +/** + * Navidrome's `/api/artist?role=composer` can include artists whose + * `stats.composer.albumCount` is zero (performer-only credits with no composer + * tags). Drop them from the Composers browse catalog. + */ +export function filterArtistsWithRoleAlbumCredits(artists: SubsonicArtist[]): SubsonicArtist[] { + return artists.filter(a => (a.albumCount ?? 0) > 0); +}