diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index 8e340f5..4435b34 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -289,14 +289,10 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t // Without it yt-dlp gets a mixed feed of videos, channels, and playlists as well. const YTM_SONGS_PARAM = 'EgWKAQIIAWoKEAkQBRAKEAMQBA%3D%3D'; const ytmUrl = `https://music.youtube.com/search?q=${encodeURIComponent(query)}¶ms=${YTM_SONGS_PARAM}`; - // 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); }; + // We know the exact output filename before yt-dlp runs — no snapshot heuristic needed. + const expectedAudio = path.join(dir, `${trackPrefix}${safeTitle}.${CONFIG.audioFmt}`); + const hasAudio = () => fs.existsSync(expectedAudio); + const tryYtdlp = async (args) => { await spawnYtdlp(args); }; const variantFilter = buildVariantFilter(title); const titleKws = buildTitleKeywords(title); @@ -387,11 +383,8 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t } } - // Step 2: find the audio file yt-dlp wrote (newest audio file in dir) - const audioFile = fs.readdirSync(dir) - .filter(f => AUDIO_EXTS.some(e => f.endsWith(`.${e}`))) - .map(f => path.join(dir, f)) - .sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)[0]; + // Step 2: locate the audio file yt-dlp wrote (exact expected path) + const audioFile = hasAudio() ? expectedAudio : null; if (!audioFile) { log(jobId, '⚠️ Keine Audio-Datei gefunden');