From 5d53a63553d1dad40b152c3bb3bc3f09fa667210 Mon Sep 17 00:00:00 2001 From: cucadmuh <49571317+cucadmuh@users.noreply.github.com> Date: Thu, 14 May 2026 22:17:26 +0300 Subject: [PATCH] fix(search): hide search3 artists with zero albums (#697) * fix(search): hide search3 artists with zero albums * docs: changelog and credits for search zero-album filter (PR #697) --- CHANGELOG.md | 7 +++++++ src/api/subsonicSearch.test.ts | 18 ++++++++++++++++++ src/api/subsonicSearch.ts | 15 ++++++++++++++- src/config/settingsCredits.ts | 1 + 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/api/subsonicSearch.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a760c32..58c4a31c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -255,6 +255,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Fixed +### Search — hide duplicate artist hits with zero albums + +**By [@cucadmuh](https://github.com/cucadmuh), thanks to zunoz for the report on the Psysonic Discord, PR [#697](https://github.com/Psychotoxical/psysonic/pull/697)** + +* Subsonic **`search3`** sometimes returns extra **artist** rows that duplicate a real name but list **0 albums** (server-side indexing noise). **`search()`** now strips artists whose **`albumCount` is exactly `0`**. Rows with **no** `albumCount` field are kept so strict or legacy servers are unchanged. +* Applies everywhere **`search()`** is used: header live search, mobile overlay, search results page, advanced search free-text query, and hooks that call **`search()`** (e.g. similarity fallbacks). + ### Offline downloads — the cancel button works again + the sidebar toast keeps its size **By [@Psychotoxical](https://github.com/Psychotoxical), PR [#694](https://github.com/Psychotoxical/psysonic/pull/694)** diff --git a/src/api/subsonicSearch.test.ts b/src/api/subsonicSearch.test.ts new file mode 100644 index 00000000..c826344e --- /dev/null +++ b/src/api/subsonicSearch.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'vitest'; +import { filterSearchArtistsWithNoAlbums } from './subsonicSearch'; +import type { SubsonicArtist } from './subsonicTypes'; + +describe('filterSearchArtistsWithNoAlbums', () => { + it('removes artists with albumCount 0', () => { + const artists: SubsonicArtist[] = [ + { id: '1', name: 'Real', albumCount: 2 }, + { id: '2', name: 'Ghost', albumCount: 0 }, + ]; + expect(filterSearchArtistsWithNoAlbums(artists)).toEqual([artists[0]]); + }); + + it('keeps artists when albumCount is omitted', () => { + const artists: SubsonicArtist[] = [{ id: '1', name: 'Unknown count' }]; + expect(filterSearchArtistsWithNoAlbums(artists)).toEqual(artists); + }); +}); diff --git a/src/api/subsonicSearch.ts b/src/api/subsonicSearch.ts index e369e3fe..39ffe339 100644 --- a/src/api/subsonicSearch.ts +++ b/src/api/subsonicSearch.ts @@ -6,6 +6,15 @@ import type { SubsonicSong, } from './subsonicTypes'; +/** + * search3 sometimes returns duplicate or junk artist rows with **zero** albums (e.g. Navidrome indexing). + * Drop only rows that explicitly report `albumCount === 0`; keep artists when the field is absent. + * Thanks to zunoz for the report on the Psysonic Discord. + */ +export function filterSearchArtistsWithNoAlbums(artists: SubsonicArtist[]): SubsonicArtist[] { + return artists.filter((a) => a.albumCount !== 0); +} + export async function search(query: string, options?: { albumCount?: number; artistCount?: number; songCount?: number }): Promise { if (!query.trim()) return { artists: [], albums: [], songs: [] }; const data = await api<{ @@ -22,7 +31,11 @@ export async function search(query: string, options?: { albumCount?: number; art ...libraryFilterParams(), }); const r = data.searchResult3 ?? {}; - return { artists: r.artist ?? [], albums: r.album ?? [], songs: r.song ?? [] }; + return { + artists: filterSearchArtistsWithNoAlbums(r.artist ?? []), + albums: r.album ?? [], + songs: r.song ?? [], + }; } /** diff --git a/src/config/settingsCredits.ts b/src/config/settingsCredits.ts index 23e40902..15d0ca30 100644 --- a/src/config/settingsCredits.ts +++ b/src/config/settingsCredits.ts @@ -106,6 +106,7 @@ export const CONTRIBUTORS = [ 'Analysis queue control: prune stale http-backfill / cpu-seed jobs when tracks leave the playback queue, cap loudness backfill warmup to current + next 5 tracks, plus debug counters for diagnostics (PR #480)', 'CachedImage / useCachedUrl: blob URL only for matching cacheKey, layout load reset on key change; fixes broken player cover flash on track switch (#606) (PR #695)', 'OpenSubsonic albumArtists in album header; track artists in player bar, mobile, mini + songToTrack (#552) (PR #696)', + 'Search: filter search3 artist rows with zero albums (report: zunoz on Psysonic Discord) (PR #697)', ], }, {