fix(backend): fix hasAudio() race condition for parallel downloads
Snapshot dir before each yt-dlp call so hasAudio() only detects files added by the current attempt, not pre-existing files from parallel jobs downloading to the same album directory. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
016a231174
commit
4b6095f213
1 file changed
+22
-15
+22
-15
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user