diff --git a/backend/lib/metadata.js b/backend/lib/metadata.js index 33a5de6..b090435 100644 --- a/backend/lib/metadata.js +++ b/backend/lib/metadata.js @@ -67,20 +67,38 @@ async function fetchLyrics(artist, title, album) { } catch { return null; } } +// Normalize a track title for comparison: unify variant suffixes so that +// "don't rush - slowed" and "don't rush (slowed)" are treated as equal. +function normalizeTitle(s) { + return (s || '') + .toLowerCase() + .trim() + // convert "title - variant" → "title (variant)" so both forms compare equal + .replace(/\s+-\s+([^-]+)$/, ' ($1)') + // strip all remaining parens/brackets content for the base comparison + .replace(/[\[((][^\]))]*[\]))]/g, '') + .trim(); +} + // iTunes track lookup — fills in album, year, trackNum, totalTracks, genre when missing async function lookupItunesTrackMeta(artist, title) { try { - const url = `https://itunes.apple.com/search?term=${encodeURIComponent(artist + ' ' + title)}&media=music&entity=musicTrack&limit=5&country=us&explicit=Yes`; + // Search with base title (strip variant suffix) for better iTunes hit rate — + // e.g. "don't rush - slowed" → search "don't rush" so iTunes finds the single + const baseTitle = title.replace(/\s*[-–]\s*\S[^-]*$/, '').trim() || title; + const searchTitle = baseTitle !== title ? baseTitle : title; + const url = `https://itunes.apple.com/search?term=${encodeURIComponent(artist + ' ' + searchTitle)}&media=music&entity=musicTrack&limit=10&country=us&explicit=Yes`; const results = await new Promise((resolve, reject) => { https.get(url, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => { try { resolve(JSON.parse(d).results || []); } catch { reject(new Error('parse')); } }); }).on('error', reject); }); - const norm = s => s?.toLowerCase().trim() || ''; - const match = results.find(t => - norm(t.trackName) === norm(title) && - norm(t.artistName).split(' ')[0] === norm(artist).split(' ')[0] + const normT = normalizeTitle(title); + const normA = (artist || '').toLowerCase().trim(); + const match = results.find(t => + normalizeTitle(t.trackName) === normT && + (t.artistName || '').toLowerCase().trim().split(' ')[0] === normA.split(' ')[0] ); if (!match) return {}; return {