fix(backend): add uploader/artist filter to yt-dlp match-filter
Strips special chars from artist name (☆NoAki☆ → NoAki) and adds uploader *= check so wrong-artist results are rejected early. Falls back to duration-only, then no-filter if artist filter yields nothing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
801a3ae0fd
commit
34afeb77ba
1 file changed
+33
-7
@@ -56,6 +56,15 @@ function sanitize(s) {
|
||||
return (s || 'Unknown').replace(/[\\/:*?"<>|]/g, '_').trim().slice(0, 100);
|
||||
}
|
||||
|
||||
// Extract a plain ASCII-ish word from an artist name for yt-dlp --match-filter uploader check.
|
||||
// "☆NoAki☆" → "NoAki", "Chiru-san" → "Chiru", "A, B" → "A"
|
||||
function artistFilterWord(name) {
|
||||
const first = (name || '').split(/[,/•]/)[0].trim();
|
||||
const clean = first.replace(/[^\w\s-]/g, '').trim(); // strip ☆ etc.
|
||||
const word = clean.split(/[\s-]+/)[0]; // first word/segment
|
||||
return word.length >= 3 ? word : '';
|
||||
}
|
||||
|
||||
// Convert multi-artist strings to " / " separator so Navidrome splits correctly.
|
||||
// Handles: "feat." / "ft." / "featuring" and comma-separated artists from Spotify/iTunes.
|
||||
function navidromeArtist(name) {
|
||||
@@ -218,33 +227,50 @@ 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)}`;
|
||||
|
||||
const uploaderWord = artistFilterWord(mainArtist);
|
||||
const uploaderFilter = uploaderWord ? ` & uploader *= ${uploaderWord}` : '';
|
||||
|
||||
if (durationMs) {
|
||||
const durationSec = Math.round(durationMs / 1000);
|
||||
const minDur = Math.max(30, Math.round(durationSec * 0.6));
|
||||
const maxDur = Math.round(durationSec * 1.5);
|
||||
const durFilter = ['--match-filter', `duration > ${minDur} & duration < ${maxDur} & !is_live`];
|
||||
const durArtistFilter = ['--match-filter', `duration > ${minDur} & duration < ${maxDur} & !is_live${uploaderFilter}`];
|
||||
const durOnlyFilter = ['--match-filter', `duration > ${minDur} & duration < ${maxDur} & !is_live`];
|
||||
|
||||
log(jobId, `🔍 Suche YT Music (~${durationSec}s): "${query}"`);
|
||||
await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...durFilter, ...baseArgs]);
|
||||
await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...durArtistFilter, ...baseArgs]);
|
||||
|
||||
if (!hasAudio()) {
|
||||
log(jobId, '⚠️ Kein YT-Music-Treffer im Dauerbereich — Fallback zu YouTube…');
|
||||
await spawnYtdlp([`ytsearch1:${query}`, ...durFilter, ...baseArgs]);
|
||||
log(jobId, '⚠️ Kein YT-Music-Treffer (Dauer+Artist) — Fallback zu YouTube…');
|
||||
await spawnYtdlp([`ytsearch1:${query}`, ...durArtistFilter, ...baseArgs]);
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
log(jobId, '⚠️ Kein YouTube-Treffer im Dauerbereich — Suche YT Music ohne Filter…');
|
||||
log(jobId, '⚠️ Kein Treffer mit Artist-Filter — Suche nur nach Dauer…');
|
||||
await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...durOnlyFilter, ...baseArgs]);
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
await spawnYtdlp([`ytsearch1:${query}`, ...durOnlyFilter, ...baseArgs]);
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
log(jobId, '⚠️ Kein Treffer im Dauerbereich — Suche ohne Filter…');
|
||||
await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]);
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
log(jobId, '⚠️ Letzter Versuch: YouTube ohne Filter…');
|
||||
await spawnYtdlp([`ytsearch1:${query}`, ...baseArgs]);
|
||||
}
|
||||
} else {
|
||||
log(jobId, `🔍 Suche YT Music: "${query}"`);
|
||||
await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]);
|
||||
await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...(uploaderFilter ? ['--match-filter', `!is_live${uploaderFilter}`] : []), ...baseArgs]);
|
||||
|
||||
if (!hasAudio()) {
|
||||
log(jobId, '⚠️ Kein YT-Music-Treffer — Fallback zu YouTube…');
|
||||
await spawnYtdlp([`ytsearch1:${query}`, ...(uploaderFilter ? ['--match-filter', `!is_live${uploaderFilter}`] : []), ...baseArgs]);
|
||||
}
|
||||
if (!hasAudio() && uploaderFilter) {
|
||||
log(jobId, '⚠️ Kein Treffer mit Artist-Filter — Suche ohne…');
|
||||
await spawnYtdlp([ytmUrl, '--playlist-items', '1', ...baseArgs]);
|
||||
}
|
||||
if (!hasAudio()) {
|
||||
await spawnYtdlp([`ytsearch1:${query}`, ...baseArgs]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user