fix(download): remix/live/acoustic/instrumental no longer skipped as duplicate of base track

findExistingTrack normalized titles by stripping ALL bracketed content, so
"Therapy (Remix)" collapsed to "Therapy" and was skipped because the base
track existed. Keep brackets that contain a version marker (remix/live/acoustic/
instrumental/vip/flip/edit/mix/…) so distinct recordings are recognized as new;
still strip noise brackets (feat. X, Official Video). Verified via Deezer that the
remix track carries the marker in its title. Unit-tested both directions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 4.8 committed 2026-08-07 17:06:00 +02:00
1 parent 71190488ca
commit eb55184118
1 file changed
+8 -1
+8 -1
View File
@@ -51,13 +51,20 @@ const AUDIO_EXTS = ['mp3', 'flac', 'opus', 'm4a', 'ogg', 'wav'];
// Strips release-type suffixes like "- Single", "- EP", "2024" from end of title/stem.
const _RELEASE_SUFFIX_RE = /\s*[-–]\s*(?:(?:official\s+)?(?:music\s+)?(?:single|ep|lp|album|ost|soundtrack|remaster(?:ed)?|deluxe(?:\s+edition)?|bonus\s+tracks?|b-?sides?|version|edit)\b.*|(?:20|19)\d{2})\s*$/i;
// Version markers that make a track a DISTINCT recording — brackets containing these must NOT be
// stripped during duplicate detection, or a remix/live/acoustic/instrumental collapses onto the
// base track and gets falsely skipped (e.g. "Therapy (Remix)" was skipped because "Therapy" existed).
const _VERSION_KW = /\b(?:remix|live|acoustic|instrumental|version|vip|flip|bootleg|re-?work|reprise|unplugged|demo|mashup|cover|sped|slowed|nightcore|extended|edit|mix|dub)\b/i;
// Robust duplicate detection: search case-insensitively across all album folders
// under the artist folder matching firstArtist, compare normalized title ignoring
// release suffixes and track prefixes. Handles DJ OKAWARI vs DJ Okawari, and
// "recognize - Single.mp3" vs "recognize.mp3" (drift between runs).
function findExistingTrack(musicRoot, firstArtist, cleanTitle) {
const norm = s => (s || '').toLowerCase()
.replace(/[\[((][^\]))]*[\]))]/g, '')
// Strip bracketed noise (feat. X, Official Video, Prod. by …) but KEEP version markers
// (Remix/Live/Acoustic/…) so distinct recordings aren't collapsed onto the base track.
.replace(/[\[((]([^\]))]*)[\]))]/g, (m, inner) => _VERSION_KW.test(inner) ? m : '')
// Filesystem-sanitized chars (/ \ : * ? " < > |) become "_" on disk — map those AND "_"
// to a space so a title tag "Kwik Flip | YTPMV" matches the file "Kwik Flip _ YTPMV".
.replace(/[/\\:*?"<>|_]/g, ' ')