fix(backend): add ytmsearch5 step + expand YTM pool to 20 for better coverage
ytmsearch5 (yt-dlp native YTM search) added as step 2 — returns different result ordering than the manual YTM URL, recovering songs that the URL approach ranks too low. YTM pool expanded from 10 → 20 results to reduce fallthrough to YouTube. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4804023a98
commit
3c54850cca
1 file changed
+16
-12
+16
-12
@@ -247,7 +247,7 @@ async function downloadTrack({ jobId, artist, rawArtist, albumArtist, title, alb
|
|||||||
// Fetch YTM URLs once upfront so every cascade step filters the SAME result set.
|
// Fetch YTM URLs once upfront so every cascade step filters the SAME result set.
|
||||||
// Re-fetching produced different orderings, causing the correct video (found in step 1
|
// Re-fetching produced different orderings, causing the correct video (found in step 1
|
||||||
// but failing the channel filter) to disappear by step 3 and a wrong one to slip through.
|
// but failing the channel filter) to disappear by step 3 and a wrong one to slip through.
|
||||||
const ytmUrls = await getYtmVideoUrls(10);
|
const ytmUrls = await getYtmVideoUrls(20);
|
||||||
const tryYtm = async (n, mfArgs) => {
|
const tryYtm = async (n, mfArgs) => {
|
||||||
const urls = n ? ytmUrls.slice(0, n) : ytmUrls;
|
const urls = n ? ytmUrls.slice(0, n) : ytmUrls;
|
||||||
if (!urls.length) return;
|
if (!urls.length) return;
|
||||||
@@ -260,47 +260,51 @@ async function downloadTrack({ jobId, artist, rawArtist, albumArtist, title, alb
|
|||||||
const dur = { min: Math.max(30, durationSec - margin), max: durationSec + margin };
|
const dur = { min: Math.max(30, durationSec - margin), max: durationSec + margin };
|
||||||
|
|
||||||
log(jobId, `🔍 Suche YT Music (~${durationSec}s ±${margin}s): "${query}"`);
|
log(jobId, `🔍 Suche YT Music (~${durationSec}s ±${margin}s): "${query}"`);
|
||||||
// 1. YTM top-5 — strict: duration + title + variant + channel check
|
// 1. YTM top-5 via manual URL — strict: duration + title + variant + channel check
|
||||||
await tryYtm(5, mf(dur, true, true));
|
await tryYtm(5, mf(dur, true, true));
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 2. YT top-5 — strict: same filters including channel check
|
// 2. ytmsearch5 (yt-dlp native YTM) — different result ordering than manual URL
|
||||||
|
await spawnYtdlp([`ytmsearch5:${query}`, ...PI_FILTERED, ...mf(dur, true, true), ...baseArgs]);
|
||||||
|
}
|
||||||
|
if (!hasAudio()) {
|
||||||
|
// 3. YT top-5 — strict: same filters including channel check
|
||||||
log(jobId, '⚠️ Kein YTM-Treffer — Fallback zu YouTube…');
|
log(jobId, '⚠️ Kein YTM-Treffer — Fallback zu YouTube…');
|
||||||
await spawnYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(dur, true, true), ...baseArgs]);
|
await spawnYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(dur, true, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 3. YTM all-10 — no channel check (label channels, etc.)
|
// 4. YTM all-20 — no channel check (label channels, etc.)
|
||||||
log(jobId, '⚠️ Erweitere Suche auf top 10 mit Dauerbeschränkung…');
|
log(jobId, '⚠️ Erweitere Suche auf top 20 mit Dauerbeschränkung…');
|
||||||
await tryYtm(0, mf(dur, true));
|
await tryYtm(0, mf(dur, true));
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 4. YT top-10 — no channel check
|
// 5. YT top-10 — no channel check
|
||||||
await spawnYtdlp([`ytsearch10:${query}`, ...PI_FILTERED_10, ...mf(dur, true), ...baseArgs]);
|
await spawnYtdlp([`ytsearch10:${query}`, ...PI_FILTERED_10, ...mf(dur, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 5. YTM all — no duration check
|
// 6. YTM all — no duration check
|
||||||
log(jobId, '⚠️ Kein Treffer im Dauerbereich — suche ohne Dauerbeschränkung…');
|
log(jobId, '⚠️ Kein Treffer im Dauerbereich — suche ohne Dauerbeschränkung…');
|
||||||
await tryYtm(0, mf(null, true));
|
await tryYtm(0, mf(null, true));
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 6. YT top-5 — no duration check
|
// 7. YT top-5 — no duration check
|
||||||
await spawnYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, true), ...baseArgs]);
|
await spawnYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, true), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 7. YTM all — title only (no variant filter)
|
// 8. YTM all — title only (no variant filter)
|
||||||
log(jobId, '⚠️ Letzter gefilterter Versuch…');
|
log(jobId, '⚠️ Letzter gefilterter Versuch…');
|
||||||
await tryYtm(0, mf(null, false));
|
await tryYtm(0, mf(null, false));
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 8. YT top-5 — title only
|
// 9. YT top-5 — title only
|
||||||
await spawnYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, false), ...baseArgs]);
|
await spawnYtdlp([`ytsearch5:${query}`, ...PI_FILTERED, ...mf(null, false), ...baseArgs]);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 9. YTM — no filter at all, take first video result
|
// 10. YTM — no filter at all, take first video result
|
||||||
log(jobId, '⚠️ Letzter Versuch: keine Filter…');
|
log(jobId, '⚠️ Letzter Versuch: keine Filter…');
|
||||||
await tryYtm(3, []);
|
await tryYtm(3, []);
|
||||||
}
|
}
|
||||||
if (!hasAudio()) {
|
if (!hasAudio()) {
|
||||||
// 10. YT — no filter
|
// 11. YT — no filter
|
||||||
await spawnYtdlp([`ytsearch1:${query}`, ...PI_UNFILTERED, ...baseArgs]);
|
await spawnYtdlp([`ytsearch1:${query}`, ...PI_UNFILTERED, ...baseArgs]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in new issue
Block a user