From a65613b5a1683ec53eb95443142ff1b67cf01cd0 Mon Sep 17 00:00:00 2001 From: Honey Date: Thu, 30 Jul 2026 22:01:59 +0200 Subject: [PATCH] fix(web): Deezer artist images + rename the search label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Search artist cards now use the Deezer artist picture directly (Deezer artists carry a photo, unlike iTunes entities) instead of a null image + an extra album lookup; the lookup stays only as a fallback when no picture is present. - Non-local (Deezer) artist pages: fetch the Deezer artist picture (/artist/{id}) for the hero photo, which was blank because getArtistImage only runs for Navidrome artists. - Label "iTunes Künstler" → "Deezer Künstler". Co-Authored-By: Claude Opus 4.8 --- public/sw.js | 2 +- src/api.js | 8 ++++++++ src/views/detail.js | 15 ++++++++++++++- src/views/search.js | 3 ++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/public/sw.js b/public/sw.js index d418d83..8bcb74d 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'ns-v49'; +const CACHE = 'ns-v50'; 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/api.js b/src/api.js index 1efb610..2eddffb 100644 --- a/src/api.js +++ b/src/api.js @@ -372,6 +372,14 @@ export async function itunesLookupArtistAlbums(artistId, limit=100) { } catch { return []; } } +// Deezer artist photo (for the hero on non-local artist pages). iTunes artist entities had none. +export async function deezerArtistImage(artistId) { + try { + const a = await (await fetch(`${_deezerBase()}/artist/${artistId}`)).json(); + return a.picture_xl || a.picture_big || a.picture_medium || null; + } catch { return null; } +} + export async function itunesTopCharts() { const urls = [_chartsUrl('de', 'albums'), _chartsUrl('us', 'albums')]; for(const url of urls) { diff --git a/src/views/detail.js b/src/views/detail.js index 2a52b73..87b5296 100644 --- a/src/views/detail.js +++ b/src/views/detail.js @@ -1,6 +1,6 @@ import { S } from '../state.js'; import { esc, toast } from '../utils.js'; -import { api, coverUrl, itunesSearch, itunesLookupAlbum, itunesLookupArtistSongs, itunesLookupArtistAlbums, fetchRomajiIndex, dispArtistById, dispAlbumName, cacheArtistSortNames, ensureArtistSortMap } from '../api.js'; +import { api, coverUrl, itunesSearch, itunesLookupAlbum, itunesLookupArtistSongs, itunesLookupArtistAlbums, deezerArtistImage, fetchRomajiIndex, dispArtistById, dispAlbumName, cacheArtistSortNames, ensureArtistSortMap } from '../api.js'; import { makeCard, makeAlbumCard, makeGlobalTrackItem, makeSongItem } from '../ui/components.js'; import { playQueue, dlSongLocal, toggleStar, parseArtists } from '../player/player.js'; import { showCtxMenu, openDlModal } from '../ui/ui.js'; @@ -146,6 +146,19 @@ export async function loadArtistDetail(artist) { }); const itunesArtistId = knownItunes ?? iArtistMatch?.artistId ?? null; + // 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'); + } + } + const [iAlbumsRes, iTracksRes] = itunesArtistId ? await Promise.allSettled([ itunesLookupArtistAlbums(itunesArtistId), diff --git a/src/views/search.js b/src/views/search.js index 919c59b..41825b9 100644 --- a/src/views/search.js +++ b/src/views/search.js @@ -130,9 +130,10 @@ export async function doGlobalSearch(q) { const s = mkSection('Künstler'); const row = document.createElement('div'); row.className = 'scroll-row'; artists.forEach(a => { - const c = makeCard(a.artistName, 'iTunes Künstler', null, '🎤'); + const c = makeCard(a.artistName, 'Deezer Künstler', a.artworkUrl100 || null, '🎤'); c.onclick = () => loadItunesArtistResults(a.artistId, a.artistName); row.appendChild(c); + if (a.artworkUrl100) return; // Deezer artists already carry a photo — no extra lookup itunesSearch(a.artistName,'album',1).then(res => { if(!res[0]) return; const imgUrl = res[0].artworkUrl100?.replace('100x100','300x300');