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
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:
@@ -52,16 +52,18 @@ var losslessFormats = map[string]bool{"flac": true, "wav": true}
|
||||
// the report shows it and the merge (#3911) lets the operator choose another.
|
||||
//
|
||||
// In order:
|
||||
// 0. the copy whose tags fit the album best (TagFitScore). It is zero for
|
||||
// 1. lossless over lossy — the one difference no later step can recover. It
|
||||
// comes before tag fit: tags can be fixed, a deleted FLAC cannot, and on
|
||||
// 2026-10-20 five FLACs went for mp3s that only carried a recording MBID
|
||||
// 2. the copy whose tags fit the album best (TagFitScore). It is zero for
|
||||
// every copy when the caller does not know it, and the order falls through
|
||||
// to the quality rules below
|
||||
// 1. lossless over lossy — the one difference no later step can recover
|
||||
// 2. the larger file — for one recording at one duration that is the higher
|
||||
// to the rules below
|
||||
// 3. the larger file — for one recording at one duration that is the higher
|
||||
// bitrate. The scanner does not record bitrate (tracks.bitrate is never
|
||||
// filled), so file size is the signal that actually exists
|
||||
// 3. the copy in the library longest — the one most likely to carry the play
|
||||
// 4. the copy in the library longest — the one most likely to carry the play
|
||||
// history and likes, so the merge moves the least
|
||||
// 4. the lowest track id, so the choice is stable between page loads
|
||||
// 5. the lowest track id, so the choice is stable between page loads
|
||||
func ProposeSurvivor(cands []SurvivorCandidate) (trackID, reason string) {
|
||||
if len(cands) == 0 {
|
||||
return "", ""
|
||||
@@ -77,10 +79,10 @@ func ProposeSurvivor(cands []SurvivorCandidate) (trackID, reason string) {
|
||||
// runner-up — the rule that actually decided, not every rule it passed.
|
||||
next := ranked[1]
|
||||
switch {
|
||||
case best.TagFit != next.TagFit:
|
||||
return best.TrackID, "tags fit the album"
|
||||
case isLossless(best) != isLossless(next):
|
||||
return best.TrackID, "lossless (" + strings.ToLower(best.FileFormat) + ")"
|
||||
case best.TagFit != next.TagFit:
|
||||
return best.TrackID, "tags fit the album"
|
||||
case best.FileSize != next.FileSize:
|
||||
return best.TrackID, "largest file"
|
||||
case !best.AddedAt.Equal(next.AddedAt):
|
||||
@@ -91,12 +93,12 @@ func ProposeSurvivor(cands []SurvivorCandidate) (trackID, reason string) {
|
||||
}
|
||||
|
||||
func survivorBefore(a, b SurvivorCandidate) bool {
|
||||
if a.TagFit != b.TagFit {
|
||||
return a.TagFit > b.TagFit
|
||||
}
|
||||
if isLossless(a) != isLossless(b) {
|
||||
return isLossless(a)
|
||||
}
|
||||
if a.TagFit != b.TagFit {
|
||||
return a.TagFit > b.TagFit
|
||||
}
|
||||
if a.FileSize != b.FileSize {
|
||||
return a.FileSize > b.FileSize
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user