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:
HoneyandClaude Opus 4.8 committed 2026-08-12 01:51:29 +02:00
1 parent 16f76a4236
commit 6d084a7b7a
6 files changed
+200 -9

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
// Single-track metadata resolver — the ONE place callers use to enrich a downloaded single.
// Order: Deezer (artist-verified) → MusicBrainz (artist-verified) → nothing.
// Returning {} is intentional and safe: the download then uses its clean fallback
// (album_artist = song artist, album = "Singles", cover = source thumbnail, year/genre untouched).
//
// Both sources apply the SAME discipline — the artist must correspond — so a same-titled song by a
// different artist can never contaminate album/cover/year again.
const dz = require('./deezer');
const mb = require('./musicbrainz');
async function lookupTrackMeta(artist, title) {
const d = await dz.lookupTrackMeta(artist, title).catch(() => ({}));
if (d && d.album) return d; // Deezer matched (artist-verified)
const m = await mb.lookupTrackMeta(artist, title).catch(() => ({}));
if (m && m.album) return m; // MusicBrainz matched (artist-verified)
return d || {}; // nothing convincing → clean fallback
}
module.exports = { lookupTrackMeta };