From f831e1fd8f1e62fd97aebe75053011bf612b34cf Mon Sep 17 00:00:00 2001 From: Honey Date: Sat, 2 May 2026 18:25:51 +0200 Subject: [PATCH] fix: use exact expected filename instead of newest-file heuristic Parallel downloads to the same album dir (e.g. two tracks from same album) both resolved to the last-written file, causing wrong metadata to be embedded. Now each download targets its known output path (trackPrefix + safeTitle + audioFmt) directly. Co-Authored-By: Claude Sonnet 4.6 --- backend/novasonic-server.js | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) 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');