fix: spotify importer uses same iTunes lookup as global search for artwork + metadata
- _loadSpotifyTrackArtwork: rewritten to call itunesSearch() directly from browser (same as global search) instead of backend endpoint — same quality, works for all regions - enriches track objects with collectionName, releaseDate, trackNumber, trackExplicitness so downloads get full metadata too - 8-concurrent batch to avoid hammering iTunes - adds onerror fallback to makeGlobalTrackItem img so broken URLs show 🎵 placeholder Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
a31953d17e
commit
90c76fe9a7
2 files changed
+34
-29
No files matched your search
+1
-1
@@ -71,7 +71,7 @@ export function makeGlobalTrackItem(t, idx, list) {
|
||||
el.className = 'gtrack-item';
|
||||
const art = t.artworkUrl60 || t.artworkUrl100 || '';
|
||||
el.innerHTML = `
|
||||
<div class="gtrack-art">${art ? `<img src="${art}" loading="lazy">` : '<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:22px">🎵</div>'}</div>
|
||||
<div class="gtrack-art">${art ? `<img src="${art}" loading="lazy" onerror="this.parentNode.innerHTML='<div style=\\'width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:22px\\'>🎵</div>'">` : '<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:22px">🎵</div>'}</div>
|
||||
<div class="gtrack-info">
|
||||
<div class="gtrack-title">${esc(t.trackName||t.collectionName||'—')}${t.trackExplicitness==='explicit'?'<span class="explicit-badge">E</span>':''}</div>
|
||||
<div class="gtrack-sub">${esc(t.artistName||'—')} · ${esc(t.collectionName||'')} ${t.releaseDate?'· '+new Date(t.releaseDate).getFullYear():''}</div>
|
||||
|
||||
+33
-28
@@ -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');
|
||||
|
||||
// Render any artwork already present in the Spotify data immediately
|
||||
// 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;
|
||||
const img = document.createElement('img');
|
||||
img.src = url; img.loading = 'lazy';
|
||||
el.innerHTML = ''; el.appendChild(img);
|
||||
_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 = '<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:22px">🎵</div>'; };
|
||||
img.src = url;
|
||||
el.innerHTML = ''; el.appendChild(img);
|
||||
}
|
||||
Reference in new issue
Block a user