diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index 8413383..fc53f58 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -218,10 +218,13 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t '--audio-quality', CONFIG.audioQuality, '-o', outputTemplate, '--no-playlist', - '--playlist-items', '1', '--no-warnings', '--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 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 }; log(jobId, `🔍 Suche YT Music (~${durationSec}s ±${margin}s): "${query}"`); - // 1. YTM — Dauer + Artist + Varianten-Filter (streng) - await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(dur, true, true), ...baseArgs]); + // 1. YTM — Dauer + Artist + Varianten-Filter (Top 5, erster Treffer) + await tryYtdlp([ytmUrl, ...PI_FILTERED, ...mf(dur, true, true), ...baseArgs]); if (!hasAudio()) { // 2. YT — Dauer + Artist + Varianten-Filter 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()) { // 3. YTM — Dauer + Varianten-Filter (kein 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()) { // 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()) { // 5. YTM — nur Varianten-Filter (kein Dauer-Check) 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()) { // 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()) { // 7+8. Letzter Ausweg: komplett ungefiltert log(jobId, '⚠️ Letzter Versuch: keine Filter…'); - await tryYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]); + await tryYtdlp([ytmUrl, ...PI_UNFILTERED, ...baseArgs]); } if (!hasAudio()) { - await tryYtdlp([`ytsearch1:${query}`, ...baseArgs]); + await tryYtdlp([`ytsearch1:${query}`, ...PI_UNFILTERED, ...baseArgs]); } } else { log(jobId, `🔍 Suche YT Music: "${query}"`); // 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()) { - await tryYtdlp([`ytsearch1:${query}`, ...mf(null, true, true), ...baseArgs]); + await tryYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, true, true), ...baseArgs]); } if (!hasAudio()) { 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()) { - await tryYtdlp([`ytsearch1:${query}`, ...mf(null, false, true), ...baseArgs]); + await tryYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, false, true), ...baseArgs]); } if (!hasAudio()) { log(jobId, '⚠️ Letzter Versuch: keine Filter…'); - await tryYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]); + await tryYtdlp([ytmUrl, ...PI_UNFILTERED, ...baseArgs]); } if (!hasAudio()) { - await tryYtdlp([`ytsearch1:${query}`, ...baseArgs]); + await tryYtdlp([`ytsearch1:${query}`, ...PI_UNFILTERED, ...baseArgs]); } }