fix: normalize title comparison in iTunes lookup to handle Spotify dash-variants vs iTunes paren-variants

This commit is contained in:
Honey committed 2026-05-14 18:53:06 +02:00
1 parent 55370ad760
commit 757c7045ba
1 file changed
+23 -5
+23 -5
View File
@@ -67,20 +67,38 @@ async function fetchLyrics(artist, title, album) {
} catch { return null; } } 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 // iTunes track lookup — fills in album, year, trackNum, totalTracks, genre when missing
async function lookupItunesTrackMeta(artist, title) { async function lookupItunesTrackMeta(artist, title) {
try { 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) => { const results = await new Promise((resolve, reject) => {
https.get(url, res => { https.get(url, res => {
let d = ''; res.on('data', c => d += c); let d = ''; res.on('data', c => d += c);
res.on('end', () => { try { resolve(JSON.parse(d).results || []); } catch { reject(new Error('parse')); } }); res.on('end', () => { try { resolve(JSON.parse(d).results || []); } catch { reject(new Error('parse')); } });
}).on('error', reject); }).on('error', reject);
}); });
const norm = s => s?.toLowerCase().trim() || ''; const normT = normalizeTitle(title);
const match = results.find(t => const normA = (artist || '').toLowerCase().trim();
norm(t.trackName) === norm(title) && const match = results.find(t =>
norm(t.artistName).split(' ')[0] === norm(artist).split(' ')[0] normalizeTitle(t.trackName) === normT &&
(t.artistName || '').toLowerCase().trim().split(' ')[0] === normA.split(' ')[0]
); );
if (!match) return {}; if (!match) return {};
return { return {