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
+42
-37
No files matched your search
+1
-1
@@ -71,7 +71,7 @@ export function makeGlobalTrackItem(t, idx, list) {
|
|||||||
el.className = 'gtrack-item';
|
el.className = 'gtrack-item';
|
||||||
const art = t.artworkUrl60 || t.artworkUrl100 || '';
|
const art = t.artworkUrl60 || t.artworkUrl100 || '';
|
||||||
el.innerHTML = `
|
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-info">
|
||||||
<div class="gtrack-title">${esc(t.trackName||t.collectionName||'—')}${t.trackExplicitness==='explicit'?'<span class="explicit-badge">E</span>':''}</div>
|
<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>
|
<div class="gtrack-sub">${esc(t.artistName||'—')} · ${esc(t.collectionName||'')} ${t.releaseDate?'· '+new Date(t.releaseDate).getFullYear():''}</div>
|
||||||
|
|||||||
+41
-36
@@ -1,5 +1,6 @@
|
|||||||
import { S } from './state.js';
|
import { S } from './state.js';
|
||||||
import { esc, toast } from './utils.js';
|
import { esc, toast } from './utils.js';
|
||||||
|
import { itunesSearch } from './api.js';
|
||||||
// Circular imports — safe (only used inside function bodies)
|
// Circular imports — safe (only used inside function bodies)
|
||||||
import { makeGlobalTrackItem } from './components.js';
|
import { makeGlobalTrackItem } from './components.js';
|
||||||
import { openDlModal } from './ui.js';
|
import { openDlModal } from './ui.js';
|
||||||
@@ -159,43 +160,47 @@ function _renderSpotifyPreview(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function _loadSpotifyTrackArtwork(tracks, listEl) {
|
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
|
// Show any Spotify artwork that's already present immediately
|
||||||
tracks.forEach((t, i) => {
|
tracks.forEach((t, i) => {
|
||||||
const url = t.artworkUrl100 || t.artworkUrl60 || '';
|
const url = t.artworkUrl100 || t.artworkUrl60 || '';
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
const el = artEls[i];
|
const el = artEls[i];
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
const img = document.createElement('img');
|
_setArt(el, url);
|
||||||
img.src = url; img.loading = 'lazy';
|
});
|
||||||
el.innerHTML = ''; el.appendChild(img);
|
|
||||||
});
|
|
||||||
|
|
||||||
const payload = tracks.map((t, i) => ({
|
// Enrich each track with iTunes data — same call as global search
|
||||||
idx: i,
|
const CONC = 8;
|
||||||
trackName: t.trackName,
|
for (let i = 0; i < tracks.length; i += CONC) {
|
||||||
artistName: t.artistName,
|
const batch = tracks.slice(i, i + CONC);
|
||||||
existingArt: t.artworkUrl100 || t.artworkUrl60 || '',
|
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 }),
|
function _setArt(el, url) {
|
||||||
});
|
if (!el) return;
|
||||||
if (!res.ok) return;
|
const img = document.createElement('img');
|
||||||
const { results } = await res.json();
|
img.loading = 'lazy';
|
||||||
for (const r of results) {
|
img.onerror = () => { el.innerHTML = '<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:22px">🎵</div>'; };
|
||||||
if (!r.artworkUrl || r.idx == null) continue;
|
img.src = url;
|
||||||
const el = artEls[r.idx];
|
el.innerHTML = ''; el.appendChild(img);
|
||||||
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 */ }
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user