fix(backend): scan top 5 results per search instead of only item 1
--playlist-items 1-5 --max-downloads 1 on all filtered steps so yt-dlp skips non-matching results (wrong artist, sped up, wrong duration) and lands on the first result that actually passes all active filters. Unfiltered last-resort fallbacks keep --playlist-items 1. YouTube fallback uses ytsearch5: to match the 5-result window. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
cf8ba70af8
commit
5db9938f49
1 file changed
+19
-16
+19
-16
@@ -218,10 +218,13 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t
|
|||||||
'--audio-quality', CONFIG.audioQuality,
|
'--audio-quality', CONFIG.audioQuality,
|
||||||
'-o', outputTemplate,
|
'-o', outputTemplate,
|
||||||
'--no-playlist',
|
'--no-playlist',
|
||||||
'--playlist-items', '1',
|
|
||||||
'--no-warnings',
|
'--no-warnings',
|
||||||
'--newline',
|
'--newline',
|
||||||
];
|
];
|
||||||
|
// Filtered searches scan top 5 results and stop at first match.
|
||||||
|
// Unfiltered fallback only needs item 1 (no filter to skip past).
|
||||||
|
const PI_FILTERED = ['--playlist-items', '1-5', '--max-downloads', '1'];
|
||||||
|
const PI_UNFILTERED = ['--playlist-items', '1'];
|
||||||
|
|
||||||
const spawnYtdlp = (args) => new Promise((resolve, reject) => {
|
const spawnYtdlp = (args) => new Promise((resolve, reject) => {
|
||||||
const proc = spawn(CONFIG.ytdlpBin, args, { env: { ...process.env, PYTHONUNBUFFERED: '1' } });
|
const proc = spawn(CONFIG.ytdlpBin, args, { env: { ...process.env, PYTHONUNBUFFERED: '1' } });
|
||||||
@@ -274,61 +277,61 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t
|
|||||||
const dur = { min: Math.max(30, durationSec - margin), max: durationSec + margin };
|
const dur = { min: Math.max(30, durationSec - margin), max: durationSec + margin };
|
||||||
|
|
||||||
log(jobId, `🔍 Suche YT Music (~${durationSec}s ±${margin}s): "${query}"`);
|
log(jobId, `🔍 Suche YT Music (~${durationSec}s ±${margin}s): "${query}"`);
|
||||||
// 1. YTM — Dauer + Artist + Varianten-Filter (streng)
|
// 1. YTM — Dauer + Artist + Varianten-Filter (Top 5, erster Treffer)
|
||||||
await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(dur, true, true), ...baseArgs]);
|
await tryYtdlp([ytmUrl, ...PI_FILTERED, ...mf(dur, true, true), ...baseArgs]);
|
||||||
|
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 2. YT — Dauer + Artist + Varianten-Filter
|
// 2. YT — Dauer + Artist + Varianten-Filter
|
||||||
log(jobId, '⚠️ Kein YTM-Treffer — Fallback zu YouTube…');
|
log(jobId, '⚠️ Kein YTM-Treffer — Fallback zu YouTube…');
|
||||||
await tryYtdlp([`ytsearch1:${query}`, ...mf(dur, true, true), ...baseArgs]);
|
await tryYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(dur, true, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 3. YTM — Dauer + Varianten-Filter (kein Artist)
|
// 3. YTM — Dauer + Varianten-Filter (kein Artist)
|
||||||
log(jobId, '⚠️ Kein Treffer mit Artist-Filter — ohne Artist…');
|
log(jobId, '⚠️ Kein Treffer mit Artist-Filter — ohne Artist…');
|
||||||
await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(dur, false, true), ...baseArgs]);
|
await tryYtdlp([ytmUrl, ...PI_FILTERED, ...mf(dur, false, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 4. YT — Dauer + Varianten-Filter
|
// 4. YT — Dauer + Varianten-Filter
|
||||||
await tryYtdlp([`ytsearch1:${query}`, ...mf(dur, false, true), ...baseArgs]);
|
await tryYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(dur, false, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 5. YTM — nur Varianten-Filter (kein Dauer-Check)
|
// 5. YTM — nur Varianten-Filter (kein Dauer-Check)
|
||||||
log(jobId, '⚠️ Kein Treffer im Dauerbereich — suche ohne Dauerbeschränkung…');
|
log(jobId, '⚠️ Kein Treffer im Dauerbereich — suche ohne Dauerbeschränkung…');
|
||||||
await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(null, false, true), ...baseArgs]);
|
await tryYtdlp([ytmUrl, ...PI_FILTERED, ...mf(null, false, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 6. YT — nur Varianten-Filter
|
// 6. YT — nur Varianten-Filter
|
||||||
await tryYtdlp([`ytsearch1:${query}`, ...mf(null, false, true), ...baseArgs]);
|
await tryYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, false, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 7+8. Letzter Ausweg: komplett ungefiltert
|
// 7+8. Letzter Ausweg: komplett ungefiltert
|
||||||
log(jobId, '⚠️ Letzter Versuch: keine Filter…');
|
log(jobId, '⚠️ Letzter Versuch: keine Filter…');
|
||||||
await tryYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]);
|
await tryYtdlp([ytmUrl, ...PI_UNFILTERED, ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
await tryYtdlp([`ytsearch1:${query}`, ...baseArgs]);
|
await tryYtdlp([`ytsearch1:${query}`, ...PI_UNFILTERED, ...baseArgs]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log(jobId, `🔍 Suche YT Music: "${query}"`);
|
log(jobId, `🔍 Suche YT Music: "${query}"`);
|
||||||
// Ohne Dauer: Artist + Varianten → ohne Artist → ohne alles
|
// Ohne Dauer: Artist + Varianten → ohne Artist → ohne alles
|
||||||
await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(null, true, true), ...baseArgs]);
|
await tryYtdlp([ytmUrl, ...PI_FILTERED, ...mf(null, true, true), ...baseArgs]);
|
||||||
|
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
await tryYtdlp([`ytsearch1:${query}`, ...mf(null, true, true), ...baseArgs]);
|
await tryYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, true, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
log(jobId, '⚠️ Kein Treffer mit Artist — ohne Artist-Filter…');
|
log(jobId, '⚠️ Kein Treffer mit Artist — ohne Artist-Filter…');
|
||||||
await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(null, false, true), ...baseArgs]);
|
await tryYtdlp([ytmUrl, ...PI_FILTERED, ...mf(null, false, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
await tryYtdlp([`ytsearch1:${query}`, ...mf(null, false, true), ...baseArgs]);
|
await tryYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, false, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
log(jobId, '⚠️ Letzter Versuch: keine Filter…');
|
log(jobId, '⚠️ Letzter Versuch: keine Filter…');
|
||||||
await tryYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]);
|
await tryYtdlp([ytmUrl, ...PI_UNFILTERED, ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
await tryYtdlp([`ytsearch1:${query}`, ...baseArgs]);
|
await tryYtdlp([`ytsearch1:${query}`, ...PI_UNFILTERED, ...baseArgs]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user