feat: disambiguate same-name artists by Deezer fan count + show fans
Deezer's artist search doesn't rank by popularity, so a name collision (5 artists named "Joji") returned a niche one first (62 fans) instead of the real Joji (559k) — the artist page then loaded the wrong discography and owned songs didn't show as downloaded. - api.js: _dzArtist carries fanCount (nb_fan); artist search sorts by fanCount desc. - search.js: artist cards show the fan count as subtitle; fan count passed to the artist page. - detail.js: pick the most-popular name match (not the first); for local Navidrome artists with a real collision (>=2 same-name candidates), tie-break by SONG OVERLAP — prefer the Deezer candidate whose top tracks actually overlap the library (each artist has different songs, so this nails the right person). Costs a few calls only on a collision. - Artist hero shows "<n> Fans" (immediately for global-search artists, after match for local). - Android: DzArtist.nb_fan + /search/artist sorted by nb_fan so the right artist is first. - Also: web global-search "unavailable" message now says Deezer, not Apple. SW ns-v52. Verified live against Deezer: both fan-sort and overlap select the real Joji (id 5078097). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
1e69831dd8
commit
065d2f7319
9 files changed
+69
-19
No files matched your search
@@ -49,7 +49,9 @@ object ItunesClient {
|
||||
val tracksD = async { runCatching { http.get("$BASE/search") { parameter("q", query); parameter("limit", limit) }.body<DzList<DzTrack>>().data }.getOrDefault(emptyList()) }
|
||||
val albumsD = async { runCatching { http.get("$BASE/search/album") { parameter("q", query); parameter("limit", 12) }.body<DzList<DzAlbum>>().data }.getOrDefault(emptyList()) }
|
||||
val artistsD = async { runCatching { http.get("$BASE/search/artist") { parameter("q", query); parameter("limit", 8) }.body<DzList<DzArtist>>().data }.getOrDefault(emptyList()) }
|
||||
val results = artistsD.await().map { it.toItunesItem() } +
|
||||
// Deezer doesn't rank artists by popularity — sort by nb_fan so a name collision
|
||||
// (5× "Joji") resolves to the artist the user means (real Joji has 559k fans, not 62).
|
||||
val results = artistsD.await().sortedByDescending { it.nb_fan ?: 0 }.map { it.toItunesItem() } +
|
||||
albumsD.await().map { it.toItunesItem() } +
|
||||
tracksD.await().map { it.toItunesItem() }
|
||||
ItunesSearchResponse(results = results)
|
||||
|
||||
@@ -18,6 +18,7 @@ data class DzArtist(
|
||||
val picture_medium: String? = null,
|
||||
val picture_big: String? = null,
|
||||
val picture_xl: String? = null,
|
||||
val nb_fan: Long? = null, // popularity — disambiguates same-name artists (5× "Joji")
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
Reference in new issue
Block a user