diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index 490ba37..44696a6 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -484,6 +484,22 @@ function _spTrackItem(t, defaultAlbum, defaultArt, defaultAlbumArtist) { // ─── ROUTES ────────────────────────────────────────────────────────────────── +app.post('/api/spotify/track-artwork', async (req, res) => { + const tracks = req.body.tracks || []; + if (!tracks.length) return res.json({ results: [] }); + const CONC = 8; + const results = []; + 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 || '' }; + })); + results.push(...batchRes); + } + res.json({ results }); +}); + app.get('/api/health', (req, res) => { res.json({ status: 'ok', ytdlpVersion, musicPath: CONFIG.musicPath, audioFormat: CONFIG.audioFmt, activeJobs: activeDownloads, queueLength: downloadQueue.length }); }); diff --git a/src/views.js b/src/views.js index 5a23d4f..ad750df 100644 --- a/src/views.js +++ b/src/views.js @@ -1043,6 +1043,39 @@ function _renderSpotifyPreview(data) { list.innerHTML = ''; (data.tracks || []).forEach((t, i) => list.appendChild(makeGlobalTrackItem(t, i, data.tracks))); section.style.display = ''; + + // Lazy-load correct per-track artwork via iTunes in the background + if ((data.type === 'playlist' || data.type === 'album') && count > 0 && S.cfg.backendUrl) { + _loadSpotifyTrackArtwork(data.tracks, list); + } +} + +async function _loadSpotifyTrackArtwork(tracks, listEl) { + try { + const payload = tracks.map((t, i) => ({ idx: i, trackName: t.trackName, artistName: t.artistName })); + 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(); + 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 + if (window._spotifyResolved?.tracks?.[r.idx]) { + window._spotifyResolved.tracks[r.idx].artworkUrl100 = r.artworkUrl; + } + } + } catch { /* artwork loading is non-critical */ } } // ─── HELPERS ───