diff --git a/backend/lib/metadata.js b/backend/lib/metadata.js index b1130a5..8707c79 100644 --- a/backend/lib/metadata.js +++ b/backend/lib/metadata.js @@ -95,11 +95,13 @@ async function lookupItunesTrackMeta(artist, title) { res.on('end', () => { try { resolve(JSON.parse(d).results || []); } catch { reject(new Error('parse')); } }); }).on('error', reject); }); - const normT = normalizeTitle(title); - const normA = (artist || '').toLowerCase().trim(); + const normT = normalizeTitle(title); + const normA = (artist || '').toLowerCase().trim(); + // Split by space OR comma/semicolon/slash so "VXLLAIN, iGRES" → "vxllain" not "vxllain," + const firstWord = s => (s || '').toLowerCase().split(/[\s,;/]/)[0]; const match = results.find(t => normalizeTitle(t.trackName) === normT && - (t.artistName || '').toLowerCase().trim().split(' ')[0] === normA.split(' ')[0] + firstWord(t.artistName) === firstWord(normA) ); if (!match) return {}; return { diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index 72e2ed7..352ca17 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -553,7 +553,9 @@ app.get('/api/musicbrainz/lookup', async (req, res) => { const { artist, title } = req.query; if (!artist || !title) return res.status(400).json({ error: 'artist and title required' }); try { - const query = `recording:"${title.replace(/"/g, '')}" AND artist:"${artist.replace(/"/g, '')}"`; + // Use only the first artist — MB won't match "VXLLAIN, iGRES, ENXK" against "VXLLAIN feat. iGRES & ENXK" + const firstArtist = artist.split(/[,;&•]/)[0].trim().replace(/"/g, ''); + const query = `recording:"${title.replace(/"/g, '')}" AND artistname:"${firstArtist}"`; const url = `https://musicbrainz.org/ws/2/recording/?query=${encodeURIComponent(query)}&fmt=json&limit=5`; const r = await fetch(url, { headers: { 'User-Agent': 'NovaSonic/1.0 (me_honeyy@proton.me)' }, signal: AbortSignal.timeout(10000) }); if (!r.ok) return res.status(502).json({ error: `MusicBrainz ${r.status}` });