Repository navigation
fix: dedup same song across youtube title format variants #582
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -99,6 +99,15 @@ | |
|
|
||
| // YouTube auto-generated "Topic" channel suffix | ||
| /\s{0,3}-\s{0,3}topic\b/gi, | ||
|
|
||
| // Brazilian/Portuguese YouTube title noise | ||
| /#\S+/g, | ||
| /\(tradu[çc][aã]o[^)]*\)/gi, | ||
| /\[tradu[çc][aã]o[^\]]*\]/gi, | ||
| /\blegendado\b/gi, | ||
| /\(clipe\s+oficial[^)]*\)/gi, | ||
| /\[clipe\s+oficial[^\]]*\]/gi, | ||
| /\blyrics\b/gi, | ||
| ] | ||
|
|
||
| const HYPHENATED_VERSION_SUFFIXES: RegExp[] = [ | ||
|
|
@@ -200,6 +209,56 @@ | |
| return `${cleanedTitle} ${cleanedAuthor}`.trim() | ||
| } | ||
|
|
||
| /** | ||
| * Extract the song-title core from a YouTube-style "Artist - Song" title. | ||
| * Uses the author field to determine which side of the separator is the artist | ||
| * (handles both "Artist - Song" and inverted "Song - Artist" formats). | ||
| * Strips secondary separators from the core (e.g. "Halo - VERSÃO FORROZINHO" → "Halo"). | ||
| * Returns null when no separator is found in the cleaned title. | ||
| */ | ||
| export function extractSongCore(title: string, author?: string): string | null { | ||
|
Check failure on line 219 in packages/bot/src/utils/music/searchQueryCleaner.ts
|
||
| const cleaned = cleanTitle(title) | ||
| for (const sep of [' - ', ' – ', ' — ']) { | ||
| const idx = cleaned.indexOf(sep) | ||
| if (idx < 2 || idx > 60) continue | ||
| const left = cleaned.slice(0, idx).trim() | ||
| if (/[()[\]]/.test(left)) continue | ||
| const right = cleaned.slice(idx + sep.length).trim() | ||
| if (left.length < 2 || right.length < 2) continue | ||
|
|
||
| let songPart: string | ||
| if (author) { | ||
| const norm = (s: string) => | ||
| s.toLowerCase().replaceAll(/[^a-z0-9]+/g, '') | ||
| const authNorm = norm(cleanAuthor(author)) | ||
| const overlaps = (a: string, b: string) => | ||
| a.length >= 3 && | ||
| b.length >= 3 && | ||
| (a.includes(b.slice(0, 4)) || b.includes(a.slice(0, 4))) | ||
| if (overlaps(authNorm, norm(left))) { | ||
| songPart = right | ||
| } else if (overlaps(authNorm, norm(right))) { | ||
| songPart = left | ||
| } else { | ||
| songPart = right | ||
| } | ||
|
Comment on lines
+231
to
+244
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The overlap check drops every character outside 🤖 Prompt for AI Agents |
||
| } else { | ||
| songPart = right | ||
| } | ||
|
|
||
| for (const innerSep of [' - ', ' – ', ' — ']) { | ||
| const innerIdx = songPart.indexOf(innerSep) | ||
| if (innerIdx > 0) { | ||
| songPart = songPart.slice(0, innerIdx).trim() | ||
| break | ||
| } | ||
| } | ||
|
|
||
| return songPart.length >= 2 ? songPart : null | ||
| } | ||
| return null | ||
| } | ||
|
|
||
| /** | ||
| * True if the author is a known noise/compilation channel. | ||
| * Callers can use this to invalidate a match and trigger a retry with a | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Core-key dedup still doesn't apply between candidates picked in the same replenish pass.
These additions only compare a track against the prebuilt exclusion set. If the same search batch returns both
Beyoncé - HaloandHalo - Beyoncé, they can still survive together becauseupsertScoredCandidate()keys by full title/author andselectDiverseCandidates()only blocksnormalizeTitleOnly(...). So this fixes queue/history duplicates, but not the “two variants added together right now” case.Also applies to: 1453-1454
🤖 Prompt for AI Agents