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
@@ -19,6 +19,7 @@ const CONFIG = require('./lib/config');
|
||||
const { jobs, updateJob, log } = require('./lib/jobs');
|
||||
const { sanitize, findExistingTrack } = require('./lib/metadata');
|
||||
const dz = require('./lib/deezer');
|
||||
const tm = require('./lib/trackmeta'); // Deezer → MusicBrainz → clean fallback (single tracks)
|
||||
const { downloadTrack, downloadAlbumTracks } = require('./lib/downloader');
|
||||
const { buildRomajiIndex, clearRomajiCache, isTokenizerReady, fetchNdSongs, romajiForStorage } = require('./lib/romaji');
|
||||
|
||||
@@ -289,7 +290,7 @@ app.get('/api/meta/lookup', async (req, res) => {
|
||||
const { artist, title } = req.query;
|
||||
if (!artist || !title) return res.status(400).json({ error: 'artist and title required' });
|
||||
try {
|
||||
const meta = await dz.lookupTrackMeta(artist, title);
|
||||
const meta = await tm.lookupTrackMeta(artist, title);
|
||||
if (!meta.album && !meta.genre && !meta.year) return res.json({ found: false });
|
||||
res.json({
|
||||
found: true,
|
||||
@@ -524,7 +525,7 @@ app.post('/api/youtube/resolve', async (req, res) => {
|
||||
const title = e.title || '';
|
||||
const sourceUrl = e.webpage_url || url;
|
||||
let meta = {};
|
||||
try { meta = await dz.lookupTrackMeta(rawArtist.split(',')[0].trim(), title); } catch {}
|
||||
try { meta = await tm.lookupTrackMeta(rawArtist.split(',')[0].trim(), title); } catch {}
|
||||
|
||||
const artist = meta.artist || rawArtist;
|
||||
const artworkUrl = meta.artworkUrl || e.thumbnail || '';
|
||||
@@ -583,7 +584,7 @@ app.post('/api/download', async (req, res) => {
|
||||
const searchArtist = artist.split(',')[0].trim();
|
||||
if (!meta.album) {
|
||||
log(jobId, '📋 Suche Album-Metadaten von Deezer…');
|
||||
const extra = await dz.lookupTrackMeta(searchArtist, title);
|
||||
const extra = await tm.lookupTrackMeta(searchArtist, title);
|
||||
if (extra.album) log(jobId, `📋 Album: "${extra.album}" (${extra.year || '?'})`);
|
||||
meta = { ...meta, ...extra, artworkUrl: spArt || extra.artworkUrl || '', genre: meta.genre || extra.genre || undefined };
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user