diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index 0e836f6..8413383 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -245,8 +245,15 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t ); }); - const hasAudio = () => fs.readdirSync(dir).some(f => AUDIO_EXTS.some(e => f.endsWith(`.${e}`))); const ytmUrl = `https://music.youtube.com/search?q=${encodeURIComponent(query)}`; + // Snapshot files already in dir before we start — so hasAudio() only detects NEW files + // from the current download attempt, not pre-existing files from parallel jobs. + let snapBefore = new Set(fs.readdirSync(dir)); + const resetSnap = () => { snapBefore = new Set(fs.readdirSync(dir)); }; + const hasAudio = () => fs.readdirSync(dir).some( + f => !snapBefore.has(f) && AUDIO_EXTS.some(e => f.endsWith(`.${e}`)) + ); + const tryYtdlp = async (args) => { resetSnap(); await spawnYtdlp(args); }; const uploaderWord = artistFilterWord(mainArtist); const variantFilter = buildVariantFilter(title); @@ -268,60 +275,60 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t log(jobId, `🔍 Suche YT Music (~${durationSec}s ±${margin}s): "${query}"`); // 1. YTM — Dauer + Artist + Varianten-Filter (streng) - await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...mf(dur, true, true), ...baseArgs]); + await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(dur, true, true), ...baseArgs]); if (!hasAudio()) { // 2. YT — Dauer + Artist + Varianten-Filter log(jobId, '⚠️ Kein YTM-Treffer — Fallback zu YouTube…'); - await spawnYtdlp([`ytsearch1:${query}`, ...mf(dur, true, true), ...baseArgs]); + await tryYtdlp([`ytsearch1:${query}`, ...mf(dur, true, true), ...baseArgs]); } if (!hasAudio()) { // 3. YTM — Dauer + Varianten-Filter (kein Artist) log(jobId, '⚠️ Kein Treffer mit Artist-Filter — ohne Artist…'); - await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...mf(dur, false, true), ...baseArgs]); + await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(dur, false, true), ...baseArgs]); } if (!hasAudio()) { // 4. YT — Dauer + Varianten-Filter - await spawnYtdlp([`ytsearch1:${query}`, ...mf(dur, false, true), ...baseArgs]); + await tryYtdlp([`ytsearch1:${query}`, ...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 spawnYtdlp([ytmUrl, '--playlist-items', '1', ...mf(null, false, true), ...baseArgs]); + await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(null, false, true), ...baseArgs]); } if (!hasAudio()) { // 6. YT — nur Varianten-Filter - await spawnYtdlp([`ytsearch1:${query}`, ...mf(null, false, true), ...baseArgs]); + await tryYtdlp([`ytsearch1:${query}`, ...mf(null, false, true), ...baseArgs]); } if (!hasAudio()) { // 7+8. Letzter Ausweg: komplett ungefiltert log(jobId, '⚠️ Letzter Versuch: keine Filter…'); - await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]); + await tryYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]); } if (!hasAudio()) { - await spawnYtdlp([`ytsearch1:${query}`, ...baseArgs]); + await tryYtdlp([`ytsearch1:${query}`, ...baseArgs]); } } else { log(jobId, `🔍 Suche YT Music: "${query}"`); // Ohne Dauer: Artist + Varianten → ohne Artist → ohne alles - await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...mf(null, true, true), ...baseArgs]); + await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(null, true, true), ...baseArgs]); if (!hasAudio()) { - await spawnYtdlp([`ytsearch1:${query}`, ...mf(null, true, true), ...baseArgs]); + await tryYtdlp([`ytsearch1:${query}`, ...mf(null, true, true), ...baseArgs]); } if (!hasAudio()) { log(jobId, '⚠️ Kein Treffer mit Artist — ohne Artist-Filter…'); - await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...mf(null, false, true), ...baseArgs]); + await tryYtdlp([ytmUrl, '--playlist-items', '1', ...mf(null, false, true), ...baseArgs]); } if (!hasAudio()) { - await spawnYtdlp([`ytsearch1:${query}`, ...mf(null, false, true), ...baseArgs]); + await tryYtdlp([`ytsearch1:${query}`, ...mf(null, false, true), ...baseArgs]); } if (!hasAudio()) { log(jobId, '⚠️ Letzter Versuch: keine Filter…'); - await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]); + await tryYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]); } if (!hasAudio()) { - await spawnYtdlp([`ytsearch1:${query}`, ...baseArgs]); + await tryYtdlp([`ytsearch1:${query}`, ...baseArgs]); } }