fix(download): stop wrong album/cover from same-titled tracks; add MusicBrainz fallback
Root cause: deezer pickTrackMatch fell back to a blind title-only match, grabbing a
same-titled song by a completely different artist (e.g. 'Memories' by Rab Noakes for
'☆NoAki☆') and applying its album/cover/year. The download's clean fallback existed
but was never reached because the lookup almost always returned *something*.
Stufe A — deezer.js: the title-only fallback now REQUIRES an artist correspondence
(equal, or one name contained in the other after normalization). No match → {} →
the existing clean fallback finally runs (album_artist = song artist, album =
'Singles', cover = source thumbnail, year/genre untouched). Test that encoded the old
blind behaviour replaced; NoAki + loose-match regression tests added.
Stufe B — musicbrainz.js: a rate-limited (~1/s, User-Agent) MusicBrainz recording
lookup with the SAME artist discipline + Cover Art Archive cover. trackmeta.js is the
single resolver (Deezer → MusicBrainz → clean fallback); the three single-track call
sites use it (album batch stays Deezer-only to avoid MB rate-limit slowdowns).
Unit-tested (17/17) + live-verified: NoAki→clean fallback, real track→Deezer, MB finds
real albums when Deezer misses. Backend-only; no frontend/Android change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
16f76a4236
commit
6d084a7b7a
6 files changed
+200
-9
No files matched your search
+14
-2
@@ -69,8 +69,20 @@ function pickTrackMatch(results, artist, title) {
|
||||
const strict = results.find(t => normalizeTitle(t.title) === normT && firstWord(t.artist?.name) === firstWord(normA));
|
||||
const JP_RE = /[-ヿ㐀-䶿一-鿿]/;
|
||||
const isJp = JP_RE.test(normT) || JP_RE.test(normA);
|
||||
const titleOnly = results.find(t => normalizeTitle(t.title) === normT);
|
||||
return strict || (!isJp ? titleOnly || null : null);
|
||||
// Loose artist match: same title AND the artist names correspond (equal, or one contains the
|
||||
// other after stripping all non-alphanumerics — covers "Okawari" vs "DJ Okawari"). This REPLACES
|
||||
// the old blind title-only fallback, which grabbed a same-titled song by a completely different
|
||||
// artist (e.g. "Memories" by Rab Noakes for "☆NoAki☆") and applied its wrong album/cover/year.
|
||||
const _n = s => stripLeadSym((s || '').toLowerCase()).replace(/[^\p{L}\p{N}]+/gu, '');
|
||||
const wantA = _n(normA);
|
||||
const artistOk = candA => {
|
||||
if (!wantA || !candA) return false;
|
||||
if (candA === wantA) return true;
|
||||
const short = Math.min(candA.length, wantA.length);
|
||||
return short >= 3 && (candA.includes(wantA) || wantA.includes(candA));
|
||||
};
|
||||
const loose = results.find(t => normalizeTitle(t.title) === normT && artistOk(_n(t.artist?.name)));
|
||||
return strict || (isJp ? null : loose) || null;
|
||||
}
|
||||
|
||||
// Single-track metadata — the Deezer equivalent of the old iTunes lookupTrackMeta.
|
||||
|
||||
Reference in new issue
Block a user