fix: revert to ytsearch1 with match-filter, avoid --max-downloads exit code issues

--max-downloads 1 exits with code 101 which varies by yt-dlp version — caused
ffmpeg to never run, leaving untagged files. Use ytsearch1: + --match-filter instead:
filters top result by duration, exits 0 in all cases, falls back cleanly if no match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-04-28 18:59:39 +02:00
1 parent 03394cac68
commit 7461e44980
1 file changed
+9 -12
+9 -12
View File
@@ -182,16 +182,16 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t
const query = `${mainArtist} - ${title} official audio`;
// Step 1: yt-dlp — audio only, no thumbnail (we embed iTunes artwork ourselves)
const commonArgs = [
const baseArgs = [
'--extract-audio',
'--audio-format', CONFIG.audioFmt,
'--audio-quality', CONFIG.audioQuality,
'-o', outputTemplate,
'--no-playlist',
'--playlist-items', '1',
'--no-warnings',
'--newline',
];
// --no-playlist + --playlist-items 1 only for single-result searches (prevents playlist bleed)
const singleArgs = [...commonArgs, '--no-playlist', '--playlist-items', '1'];
const spawnYtdlp = (args) => new Promise((resolve, reject) => {
const proc = spawn(CONFIG.ytdlpBin, args, { env: { ...process.env, PYTHONUNBUFFERED: '1' } });
@@ -210,9 +210,8 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t
.forEach(l => log(jobId, l.trim()));
});
proc.on('error', reject);
// code 101 = --max-downloads limit reached (success — file was downloaded)
proc.on('close', code =>
(code === 0 || code === 101) ? resolve() : reject(new Error(`yt-dlp code ${code}: ${stderr.slice(-300)}`))
code === 0 ? resolve() : reject(new Error(`yt-dlp code ${code}: ${stderr.slice(-300)}`))
);
});
@@ -221,22 +220,20 @@ async function downloadTrack({ jobId, artist, albumArtist, title, album, year, t
const minDur = Math.max(30, Math.round(durationSec * 0.6));
const maxDur = Math.round(durationSec * 1.5);
log(jobId, `🔍 Suche (~${durationSec}s): "${query}"`);
// Search 5 results, pick first matching duration — no --playlist-items so filter sees all 5
// Filter top result by duration — if it doesn't match, fall back to unfiltered
await spawnYtdlp([
`ytsearch5:${query}`,
`ytsearch1:${query}`,
'--match-filter', `duration > ${minDur} & duration < ${maxDur} & !is_live`,
'--max-downloads', '1',
...commonArgs,
...baseArgs,
]);
// Fallback if duration filter matched nothing
const hasFile = fs.readdirSync(dir).some(f => AUDIO_EXTS.some(e => f.endsWith(`.${e}`)));
if (!hasFile) {
log(jobId, '⚠️ Kein Treffer im Dauerbereich — Suche ohne Filter…');
await spawnYtdlp([`ytsearch1:${query}`, ...singleArgs]);
await spawnYtdlp([`ytsearch1:${query}`, ...baseArgs]);
}
} else {
log(jobId, `🔍 Suche: "${query}"`);
await spawnYtdlp([`ytsearch1:${query}`, ...singleArgs]);
await spawnYtdlp([`ytsearch1:${query}`, ...baseArgs]);
}
// Step 2: find the audio file yt-dlp wrote (newest audio file in dir)