From bff652a4159894aa6df9bbd2945f7b245efd93bb Mon Sep 17 00:00:00 2001 From: Honey Date: Fri, 19 Jun 2026 09:42:07 +0200 Subject: [PATCH] fix(backend): strip release-type suffixes from YTM search title + extend stopwords MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "recognize - Single" title → search uses "recognize" (strips "- Single/EP/Album/…") - Adds Single/EP/Remaster/Deluxe/… to titleKws STOPWORDS so they never become mandatory title-match filters (no YouTube video title contains "Single") Co-Authored-By: Claude Sonnet 4.6 --- backend/lib/ytdlp.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/lib/ytdlp.js b/backend/lib/ytdlp.js index 937b517..ffafee1 100644 --- a/backend/lib/ytdlp.js +++ b/backend/lib/ytdlp.js @@ -22,7 +22,7 @@ function buildTitleKeywords(title) { return escaped ? [escaped] : []; } 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 STOPWORDS = new Set(['the','a','an','and','or','of','in','on','at','to','for','feat','ft','with','single','ep','album','remaster','remastered','deluxe','edition','version','live','bonus']); const MIX_QUALIFIERS = ['radio', 'extended', 'club', 'vip', 'original', 'edit']; const lower = (title || '').toLowerCase(); const strippedTitle = (title || '').replace(/\([^)]*\)/g, '').trim(); @@ -110,8 +110,12 @@ async function downloadTrack({ jobId, artist, rawArtist, albumArtist, title, alb const escRe = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const variantFilter = buildVariantFilter(title); - const titleKws = buildTitleKeywords(title); + // Strip release-type suffixes Spotify sometimes appends to track/album names + // e.g. "recognize - Single" → "recognize", "Blue Moon - EP" → "Blue Moon" + const searchTitle = title.replace(/\s*[-–]\s*(single|ep|album|remaster(?:ed)?|deluxe(?:\s+edition)?|bonus\s+track)s?\s*$/i, '').trim() || title; + + const variantFilter = buildVariantFilter(searchTitle); + const titleKws = buildTitleKeywords(searchTitle); const variantPart = variantFilter ? ` & ${variantFilter}` : ''; const titleKwPart = titleKws.length ? titleKws.map(k => ` & title ~= (?i)${k}`).join('') : ''; @@ -139,7 +143,7 @@ async function downloadTrack({ jobId, artist, rawArtist, albumArtist, title, alb // For Western tracks, artist + title + duration matching is sufficient (matches spotdl behaviour). const albumQuerySuffix = hasJpTitle && albumWords ? ' ' + albumWords : ''; - const query = `${ytmArtist} - ${title}${albumQuerySuffix}`; + const query = `${ytmArtist} - ${searchTitle}${albumQuerySuffix}`; const baseArgs = [ '--extract-audio',