fix: use top-2 title keywords for precise match-filter
Single-word filter was too loose: "Echoes of Eternity" produced only "Eternity" which matched unrelated tracks. Now the top 2 longest words are used so the filter becomes e.g. title ~= (?i)Eternity & title ~= (?i)Echoes? title ~= (?i)Amethyst & title ~= (?i)Flowers? Both words must appear — drastically reduces false matches. Trailing-s stays optional (Whispers? matches Whisper/Whispers). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
5e7e700d6a
commit
51d2ea257f
1 file changed
+14
-9
@@ -68,25 +68,30 @@ function artistFilterWord(name) {
|
||||
}
|
||||
|
||||
// Extract distinctive words from a track title for case-insensitive yt-dlp title ~= (?i) filters.
|
||||
// Returns an array: [mainWord, optionalMixQualifier].
|
||||
// "Memories" → ["Memories"] (matches "MEMORIES", "memories", etc.)
|
||||
// "Eclipse - Radio Mix" → ["Eclipse", "radio"]
|
||||
// Returns up to 2 of the most distinctive words so the filter is precise:
|
||||
// "Echoes of Eternity" → ["Eternity", "Echoes"] → title ~= (?i)Eternity & title ~= (?i)Echoes
|
||||
// "Amethyst Flowers" → ["Amethyst", "Flowers?"] → title ~= (?i)Amethyst & title ~= (?i)Flowers?
|
||||
// "Whispers" → ["Whispers?"] → title ~= (?i)Whispers?
|
||||
// "Eclipse - Radio Mix"→ ["Eclipse", "Radio"] → title ~= (?i)Eclipse & title ~= (?i)Radio
|
||||
// Trailing 's' is made optional (Whispers? matches "Whisper" or "Whispers") to handle
|
||||
// cases where the YouTube video title drops the plural form.
|
||||
function buildTitleKeywords(title) {
|
||||
const VARIANT_WORDS = new Set(['sped','speedup','slowed','reverb','nightcore','karaoke','parody','remix','cover','acoustic','instrumental']);
|
||||
const STOPWORDS = new Set(['the','a','an','and','or','of','in','on','at','to','for','feat','ft','with']);
|
||||
const MIX_QUALIFIERS = ['radio', 'extended', 'club', 'vip', 'original', 'edit'];
|
||||
const lower = (title || '').toLowerCase();
|
||||
// Keep original case so the word looks right in logs, case-insensitivity is handled by (?i) flag
|
||||
const words = (title || '')
|
||||
.replace(/[^\w\s]/g, ' ')
|
||||
.split(/\s+/)
|
||||
.filter(w => w.length >= 3 && !VARIANT_WORDS.has(w.toLowerCase()) && !STOPWORDS.has(w.toLowerCase()));
|
||||
const main = words.sort((a, b) => b.length - a.length)[0] || '';
|
||||
const qualifier = lower.includes('mix') ? MIX_QUALIFIERS.find(q => lower.includes(q)) : null;
|
||||
// Make trailing 's' optional so "Whispers?" matches both "Whisper" and "Whispers" in yt-dlp regex
|
||||
// Take top 2 by length — two words together are far more precise than one alone
|
||||
const top2 = words.sort((a, b) => b.length - a.length).slice(0, 2);
|
||||
// Make trailing 's' optional so "Whispers?" matches "Whisper" or "Whispers"
|
||||
const toPattern = w => (w.length >= 4 && w.endsWith('s')) ? w + '?' : w;
|
||||
const result = main ? [toPattern(main)] : [];
|
||||
if (qualifier && main.toLowerCase() !== qualifier) result.push(qualifier);
|
||||
const result = top2.map(toPattern);
|
||||
// Add mix qualifier only if neither top word already covers it
|
||||
const qualifier = lower.includes('mix') ? MIX_QUALIFIERS.find(q => lower.includes(q)) : null;
|
||||
if (qualifier && !top2.some(w => w.toLowerCase() === qualifier)) result.push(qualifier);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user