diff --git a/CHANGELOG.md b/CHANGELOG.md index c2e5aff..8210235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,7 +80,6 @@ Big change: **the entire metadata/search stack moved from Apple's iTunes to Deez ### Fixed - **Web + Android** — Owned albums were wrongly shown as **"Unvollständig 1 / 0 Tracks"** on the artist page. Deezer's `/artist/{id}/albums` list omits `nb_tracks`, so the total read as 0 and every matched album looked incomplete. Web now resolves the real track count from the Deezer album endpoint for **matched (owned) albums only** and treats an unknown total as complete; Android treats an unknown total as complete (never a false partial) -- **Web + Android** — Local Navidrome artist pages with **no artist image** (niche artists like N!GHT that Navidrome's metadata agents have no art for) showed a blank hero. They now fall back to the **matched Deezer artist photo** (Navidrome image → Deezer photo → album cover) ### Notes - Deploy: `git pull`, then rebuild the frontend (`docker compose up -d --build novasonic-frontend`) and restart the backend (`docker compose restart novasonic-backend`). Service worker bumped to `ns-v54` diff --git a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ArtistDetailScreen.kt b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ArtistDetailScreen.kt index a92360e..fa95f72 100644 --- a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ArtistDetailScreen.kt +++ b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/ArtistDetailScreen.kt @@ -218,10 +218,7 @@ fun ArtistDetailScreen( // Hero card item { - // Navidrome's own artist image first; if its agents have none (niche artists like N!GHT), - // fall back to the matched Deezer artist photo, then the album cover. val imgUrl = state.info?.largeImageUrl?.takeIf { it.isNotBlank() } - ?: state.deezerImage ?: coverUrl(artist.coverArt) Box( Modifier diff --git a/android/app/src/main/kotlin/com/novasonic/app/viewmodel/ArtistViewModel.kt b/android/app/src/main/kotlin/com/novasonic/app/viewmodel/ArtistViewModel.kt index 11a7fa0..d9075c9 100644 --- a/android/app/src/main/kotlin/com/novasonic/app/viewmodel/ArtistViewModel.kt +++ b/android/app/src/main/kotlin/com/novasonic/app/viewmodel/ArtistViewModel.kt @@ -43,8 +43,6 @@ data class ArtistUiState( val showEventsSheet: Boolean = false, // Deezer fan count of the matched artist (shown under the name), null if unknown val fanCount: Long? = null, - // Deezer artist photo — hero fallback when Navidrome's agents have no image (niche artists) - val deezerImage: String? = null, // Preview + download val previewingId: Long? = null, val downloadSheetItem: ItunesItem? = null, @@ -123,10 +121,7 @@ class ArtistViewModel(app: Application, savedState: SavedStateHandle) : AndroidV ?: artists.firstOrNull() ) val iArtistId = iArtist?.artistId - if (iArtist != null) _state.value = _state.value.copy( - fanCount = iArtist.fanCount ?: _state.value.fanCount, - deezerImage = iArtist.artworkUrl100?.takeIf { it.isNotBlank() } ?: _state.value.deezerImage, - ) + if (iArtist?.fanCount != null) _state.value = _state.value.copy(fanCount = iArtist.fanCount) if (iArtistId != null) { coroutineScope { diff --git a/public/sw.js b/public/sw.js index 5a7e44c..932a2da 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'ns-v56'; +const CACHE = 'ns-v55'; const PRECACHE = ['/', 'index.html', 'manifest.json', 'icon.svg']; const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js']; // Auth token params stripped from cache keys so artwork survives across sessions/token refreshes. diff --git a/src/views/detail.js b/src/views/detail.js index 3b0b6fc..3e46658 100644 --- a/src/views/detail.js +++ b/src/views/detail.js @@ -178,21 +178,16 @@ export async function loadArtistDetail(artist) { const itunesArtistId = knownItunes ?? iArtistMatch?.artistId ?? null; if (!knownFans) _showFans(iArtistMatch?.fanCount); // local artist: show the matched Deezer artist's fans - // Artist photo: Navidrome (from its metadata agents) first; if it has none, fall back to the - // matched Deezer artist's picture. Covers non-local Deezer artists AND niche local artists - // (e.g. N!GHT) that Navidrome's agents have no art for — the hero was blank for those before. - if (itunesArtistId && pfpWrap) { - const ndImg = await imgP.catch(() => null); - if (!ndImg) { - const dzImg = iArtistMatch?.artworkUrl100 || await deezerArtistImage(itunesArtistId); - if (dzImg) { - const pfpImg = document.createElement('img'); - pfpImg.className = 'artist-hero-pfp'; - pfpImg.onload = pfpImg.onerror = () => { if (bgEl) bgEl.style.backgroundImage = `url('${dzImg}')`; _revealHero(); }; - pfpImg.src = dzImg; - pfpWrap.prepend(pfpImg); - document.getElementById('artist-hero-pfp-initial')?.style.setProperty('display', 'none'); - } + // Non-local (Deezer) artist has no Navidrome photo — fetch the Deezer artist picture for the hero. + if (!hasND && itunesArtistId && pfpWrap) { + const dzImg = iArtistMatch?.artworkUrl100 || await deezerArtistImage(itunesArtistId); + if (dzImg) { + const pfpImg = document.createElement('img'); + pfpImg.className = 'artist-hero-pfp'; + pfpImg.onload = pfpImg.onerror = () => { if (bgEl) bgEl.style.backgroundImage = `url('${dzImg}')`; _revealHero(); }; + pfpImg.src = dzImg; + pfpWrap.prepend(pfpImg); + document.getElementById('artist-hero-pfp-initial')?.style.setProperty('display', 'none'); } }