From 8bb2b0d181fbf94823e98bd20dadd066576c732e Mon Sep 17 00:00:00 2001 From: Honey Date: Tue, 28 Apr 2026 00:41:56 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20spotify=20importer=20artwork=20=E2=80=94?= =?UTF-8?q?=20split=20multi-artist=20before=20iTunes,=20render=20Spotify?= =?UTF-8?q?=20art=20immediately?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - track-artwork endpoint: split artist on ,/•/ before iTunes search (same as download flow) - track-artwork endpoint: fall back to existingArt when iTunes returns nothing - frontend: render any artwork already in Spotify data immediately, don't wait for iTunes Co-Authored-By: Claude Sonnet 4.6 --- backend/novasonic-server.js | 5 +++-- src/spotify.js | 28 +++++++++++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index b068b51..e19cf24 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -493,8 +493,9 @@ app.post('/api/spotify/track-artwork', async (req, res) => { for (let i = 0; i < tracks.length; i += CONC) { const batch = tracks.slice(i, i + CONC); const batchRes = await Promise.all(batch.map(async t => { - const meta = await lookupItunesTrackMeta(t.artistName || '', t.trackName || ''); - return { idx: t.idx, artworkUrl: meta.artworkUrl || '' }; + const searchArtist = (t.artistName || '').split(/\s*[,/•]\s*/)[0].trim(); + const meta = await lookupItunesTrackMeta(searchArtist, t.trackName || ''); + return { idx: t.idx, artworkUrl: meta.artworkUrl || t.existingArt || '' }; })); results.push(...batchRes); } diff --git a/src/spotify.js b/src/spotify.js index 6cfa91d..1ca5738 100644 --- a/src/spotify.js +++ b/src/spotify.js @@ -160,7 +160,25 @@ function _renderSpotifyPreview(data) { async function _loadSpotifyTrackArtwork(tracks, listEl) { try { - const payload = tracks.map((t, i) => ({ idx: i, trackName: t.trackName, artistName: t.artistName })); + const artEls = listEl.querySelectorAll('.gtrack-art'); + + // Render any artwork already present in the Spotify data immediately + tracks.forEach((t, i) => { + const url = t.artworkUrl100 || t.artworkUrl60 || ''; + if (!url) return; + const el = artEls[i]; + if (!el) return; + const img = document.createElement('img'); + img.src = url; img.loading = 'lazy'; + el.innerHTML = ''; el.appendChild(img); + }); + + const payload = tracks.map((t, i) => ({ + idx: i, + trackName: t.trackName, + artistName: t.artistName, + existingArt: t.artworkUrl100 || t.artworkUrl60 || '', + })); const res = await fetch(`${S.cfg.backendUrl}/api/spotify/track-artwork`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -168,17 +186,13 @@ async function _loadSpotifyTrackArtwork(tracks, listEl) { }); if (!res.ok) return; const { results } = await res.json(); - const artEls = listEl.querySelectorAll('.gtrack-art'); for (const r of results) { if (!r.artworkUrl || r.idx == null) continue; const el = artEls[r.idx]; if (!el) continue; const img = document.createElement('img'); - img.src = r.artworkUrl; - img.loading = 'lazy'; - el.innerHTML = ''; - el.appendChild(img); - // Update resolved data so downloads use the correct artwork + img.src = r.artworkUrl; img.loading = 'lazy'; + el.innerHTML = ''; el.appendChild(img); if (window._spotifyResolved?.tracks?.[r.idx]) { window._spotifyResolved.tracks[r.idx].artworkUrl100 = r.artworkUrl; }