diff --git a/src/components.js b/src/components.js index 3940cfa..909074f 100644 --- a/src/components.js +++ b/src/components.js @@ -71,7 +71,7 @@ export function makeGlobalTrackItem(t, idx, list) { el.className = 'gtrack-item'; const art = t.artworkUrl60 || t.artworkUrl100 || ''; el.innerHTML = ` -
${art ? `` : '
๐ŸŽต
'}
+
${art ? `` : '
๐ŸŽต
'}
${esc(t.trackName||t.collectionName||'โ€”')}${t.trackExplicitness==='explicit'?'E':''}
${esc(t.artistName||'โ€”')} ยท ${esc(t.collectionName||'')} ${t.releaseDate?'ยท '+new Date(t.releaseDate).getFullYear():''}
diff --git a/src/spotify.js b/src/spotify.js index 1ca5738..a0ac455 100644 --- a/src/spotify.js +++ b/src/spotify.js @@ -1,5 +1,6 @@ import { S } from './state.js'; import { esc, toast } from './utils.js'; +import { itunesSearch } from './api.js'; // Circular imports โ€” safe (only used inside function bodies) import { makeGlobalTrackItem } from './components.js'; import { openDlModal } from './ui.js'; @@ -159,43 +160,47 @@ function _renderSpotifyPreview(data) { } async function _loadSpotifyTrackArtwork(tracks, listEl) { - try { - const artEls = listEl.querySelectorAll('.gtrack-art'); + 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); - }); + // Show any Spotify artwork that's already present immediately + tracks.forEach((t, i) => { + const url = t.artworkUrl100 || t.artworkUrl60 || ''; + if (!url) return; + const el = artEls[i]; + if (!el) return; + _setArt(el, url); + }); - const payload = tracks.map((t, i) => ({ - idx: i, - trackName: t.trackName, - artistName: t.artistName, - existingArt: t.artworkUrl100 || t.artworkUrl60 || '', + // Enrich each track with iTunes data โ€” same call as global search + const CONC = 8; + for (let i = 0; i < tracks.length; i += CONC) { + const batch = tracks.slice(i, i + CONC); + await Promise.all(batch.map(async (t, bi) => { + const idx = i + bi; + try { + const firstArtist = (t.artistName || '').split(/\s*[,/โ€ข]\s*/)[0].trim(); + const results = await itunesSearch(`${t.trackName} ${firstArtist}`, 'musicTrack', 1); + const match = results[0]; + if (!match) return; + // Enrich track object so downloads get correct metadata + if (match.artworkUrl100) t.artworkUrl100 = match.artworkUrl100; + if (match.collectionName) t.collectionName = match.collectionName; + if (match.releaseDate) t.releaseDate = match.releaseDate; + if (match.trackNumber) t.trackNumber = match.trackNumber; + if (match.trackExplicitness) t.trackExplicitness = match.trackExplicitness; + if (window._spotifyResolved?.tracks?.[idx]) Object.assign(window._spotifyResolved.tracks[idx], t); + const url = match.artworkUrl100 || match.artworkUrl60 || ''; + if (url) _setArt(artEls[idx], url); + } catch { /* non-critical */ } })); - const res = await fetch(`${S.cfg.backendUrl}/api/spotify/track-artwork`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ tracks: payload }), - }); - if (!res.ok) return; - const { results } = await res.json(); - 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); - if (window._spotifyResolved?.tracks?.[r.idx]) { - window._spotifyResolved.tracks[r.idx].artworkUrl100 = r.artworkUrl; - } - } - } catch { /* artwork loading is non-critical */ } + } +} + +function _setArt(el, url) { + if (!el) return; + const img = document.createElement('img'); + img.loading = 'lazy'; + img.onerror = () => { el.innerHTML = '
๐ŸŽต
'; }; + img.src = url; + el.innerHTML = ''; el.appendChild(img); }