fix: a duplicate fold never deletes a lossless copy for a lossy one, nor folds two versions (M498)
release / govulncheck (push) Successful in 14s
release / web (push) Successful in 1m21s
release / go (push) Successful in 1m31s
release / android (push) Successful in 4m58s
release / integration (push) Successful in 5m0s
release / Build signed APK (releases and dev) (push) Successful in 5m7s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 15s
release / Verify release artifacts (tag releases only) (push) Skipped

The first album-copy fold pass on 2026-10-20 removed five FLACs (Måneskin
Rush!, Weezer 2016) in favour of mp3s whose only edge was a recording MBID,
and an explicit "Suite-Pee" for its clean version. ProposeSurvivor now ranks
lossless before tag fit; tag fit still outranks file size, which is what the
Humanz rips needed. The fold keys copies by their version words (title and
file name), so a clean, live or instrumental copy holding the same title tag
is linked, not folded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 12:36:55 -04:00
co-authored by Claude Opus 5.5
parent 16a152b862
commit f32abc974f
6 changed files with 102 additions and 21 deletions
+31
View File
@@ -1,7 +1,9 @@
package library
import (
"path/filepath"
"regexp"
"sort"
"strings"
"unicode"
)
@@ -90,6 +92,35 @@ func ClassifyDuplicateGroup(tier string, members []ClassifyMember) DuplicateClas
var versionSuffix = regexp.MustCompile(
`(?i)\s-\s[^-]*\b(version|instrumental|remix|mix|live|acoustic|demo|edit|reprise|rework|vip)\b[^-]*$`)
// versionWord matches a word that names a version of a song.
var versionWord = regexp.MustCompile(
`(?i)\b(clean|explicit|instrumentals?|live|acoustic|unplugged|demo|remix|mix|edit|radio|karaoke|a ?cappella|acapella|reprise)\b`)
// versionWords lists, sorted and lowercased, the version words in a copy's
// title and file name together. Two copies holding one title tag can still be
// two versions, told apart only by the file name: "Suite-Pee" and
// "Suite-Pee (Clean Version)", "Luv (sic) Grand Finale" and its instrumental.
func versionWords(title, path string) string {
seen := map[string]bool{}
name := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
for _, w := range versionWord.FindAllString(title+" "+name, -1) {
w = strings.ToLower(w)
switch {
case w == "instrumentals":
w = "instrumental"
case strings.Contains(w, "cappella") || w == "acapella":
w = "acappella"
}
seen[w] = true
}
words := make([]string, 0, len(seen))
for w := range seen {
words = append(words, w)
}
sort.Strings(words)
return strings.Join(words, ",")
}
// songKey is MatchTitleKey with every bracketed segment and a dashed version
// suffix dropped: the song a title names, whatever version of it.
// "Lovesick (Instrumental)", "Lovesick - Instrumental Version" and "Lovesick"