feat(backend): Mittelweg cascade — never download wrong song on no duration match
When a track's duration is known but nothing matches the duration window, the cascade no longer falls back to blind title-only/no-filter grabs (which pulled same-titled videos by other uploaders, e.g. a "recognize" fan clip). Instead it relaxes only the duration constraint while still REQUIRING the artist channel + title. If nothing matches, the download produces no file and the job now fails cleanly with "Nicht eindeutig gefunden — bitte Direktlink im Importer nutzen" (previously it falsely reported success with no file). Single, album and playlist paths all surface the not-found state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
cf6e75b312
commit
a870f50c87
3 files changed
+23
-29
No files matched your search
+12
-25
@@ -295,32 +295,19 @@ async function downloadTrack({ jobId, artist, rawArtist, albumArtist, title, alb
|
||||
// 4. YT top-10 — no channel check
|
||||
await spawnYtdlp([`ytsearch10:${query}`, ...PI_FILTERED_10, ...mf(dur, true), ...baseArgs]);
|
||||
}
|
||||
// No duration match. "Mittelweg": relax the duration constraint but REQUIRE the artist
|
||||
// channel (+ title). This catches legit tracks whose YT upload duration drifts from the
|
||||
// iTunes value, while never grabbing a same-titled video by someone else. If nothing
|
||||
// matches title+channel we download NOTHING and the job fails cleanly (see end of fn) —
|
||||
// far better than silently saving the wrong song (e.g. a "recognize" fan clip).
|
||||
if (!hasAudio()) {
|
||||
// 6. YTM all — no duration check
|
||||
log(jobId, '⚠️ Kein Treffer im Dauerbereich — suche ohne Dauerbeschränkung…');
|
||||
await tryYtm(0, mf(null, true));
|
||||
// 5. YTM all — no duration, but channel + title required
|
||||
log(jobId, '⚠️ Kein Treffer im Dauerbereich — suche ohne Dauer, aber mit Künstler-Channel…');
|
||||
await tryYtm(0, mf(null, true, true));
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
// 7. YT top-5 — no duration check
|
||||
await spawnYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, true), ...baseArgs]);
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
// 8. YTM all — title only (no variant filter)
|
||||
log(jobId, '⚠️ Letzter gefilterter Versuch…');
|
||||
await tryYtm(0, mf(null, false));
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
// 9. YT top-5 — title only
|
||||
await spawnYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, false), ...baseArgs]);
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
// 10. YTM — no filter at all, take first video result
|
||||
log(jobId, '⚠️ Letzter Versuch: keine Filter…');
|
||||
await tryYtm(3, []);
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
// 11. YT — no filter
|
||||
await spawnYtdlp([`ytsearch1:${query}`, ...PI_UNFILTERED, ...baseArgs]);
|
||||
// 6. YT top-10 — no duration, but channel + title required
|
||||
await spawnYtdlp([`ytsearch10:${query}`, ...PI_FILTERED_10, ...mf(null, true, true), ...baseArgs]);
|
||||
}
|
||||
} else {
|
||||
log(jobId, `🔍 Suche YT Music: "${query}"`);
|
||||
@@ -360,8 +347,8 @@ async function downloadTrack({ jobId, artist, rawArtist, albumArtist, title, alb
|
||||
|
||||
const audioFile = hasAudio() ? expectedAudio : null;
|
||||
if (!audioFile) {
|
||||
log(jobId, '⚠️ Keine Audio-Datei gefunden');
|
||||
return { success: true, dir };
|
||||
log(jobId, '⚠️ Nicht eindeutig gefunden — bitte Direktlink im Importer nutzen');
|
||||
return { success: false, notFound: true, dir };
|
||||
}
|
||||
|
||||
return embedMetadata({ jobId, audioFile, dir, trackPrefix, safeTitle, rawArtist, artist, mainArtist, album, year, trackNum, totalTracks, discNum, discTotal, compilation, genre, artworkUrl, explicit, title: searchTitle });
|
||||
|
||||
Reference in new issue
Block a user