fix: a second copy of a track counts once, joining words don't part two titles, and a newer matcher retries an older one's refusals (M502 #5569)
release / govulncheck (push) Successful in 15s
release / web (push) Successful in 1m18s
release / go (push) Successful in 1m35s
release / integration (push) Failing after 5m33s
release / android (push) Successful in 5m47s
release / Build signed APK (releases and dev) (push) Successful in 6m4s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 15s
release / web (push) Successful in 1m18s
release / go (push) Successful in 1m35s
release / integration (push) Failing after 5m33s
release / android (push) Successful in 5m47s
release / Build signed APK (releases and dev) (push) Successful in 6m4s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
The second run's refusals were mostly albums holding a track twice (a two-track single with three files), which no edition can pair, and release names that differ only in "+" against "/". Tracks are now read once per disc, number and title, and "and", "&" and "+" count for nothing when titles are compared; an edition's own title also counts as the album's name. library_album_matches records which matcher version decided each album. Refusals by an older version are matched again, even after the listing is done, so an improvement reaches them without a migration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -85,8 +85,13 @@ type albumMatch struct {
|
||||
Detail string
|
||||
}
|
||||
|
||||
// tracksToMatch reads an album's tracks, each once: a second copy of a
|
||||
// track (the same disc, number and title, as a duplicate file in another
|
||||
// format is) would otherwise count against the match as a track no edition
|
||||
// has.
|
||||
func tracksToMatch(rows []dbq.ListAlbumTracksToMatchRow) []libraryTrack {
|
||||
out := make([]libraryTrack, 0, len(rows))
|
||||
seen := make(map[string]bool, len(rows))
|
||||
for _, r := range rows {
|
||||
t := libraryTrack{Title: r.Title, LengthMs: int(r.DurationMs), Recording: deref(r.Mbid)}
|
||||
if r.DiscNumber != nil {
|
||||
@@ -95,6 +100,11 @@ func tracksToMatch(rows []dbq.ListAlbumTracksToMatchRow) []libraryTrack {
|
||||
if r.TrackNumber != nil {
|
||||
t.Number = int(*r.TrackNumber)
|
||||
}
|
||||
key := fmt.Sprintf("%d/%d/%s", max(t.Disc, 1), t.Number, strings.Join(release.Tokens(t.Title), " "))
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
seen[key] = true
|
||||
out = append(out, t)
|
||||
}
|
||||
return out
|
||||
@@ -165,9 +175,15 @@ func agrees(n, tracks int) bool {
|
||||
return tracks > 0 && n*10 >= tracks*matchAgreement
|
||||
}
|
||||
|
||||
// sameTitle: the same words, not merely one holding the other.
|
||||
// sameTitle: the same words, not merely one holding the other. "And",
|
||||
// "&" and "+" join two titles however a release spells it ("Brain Stew +
|
||||
// Jaded", "Brain Stew / Jaded"), so they count for nothing.
|
||||
func sameTitle(a, b string) bool {
|
||||
return slices.Equal(release.Tokens(a), release.Tokens(b))
|
||||
words := func(s string) []string {
|
||||
return slices.DeleteFunc(release.Tokens(s), func(w string) bool { return w == "and" })
|
||||
}
|
||||
wa, wb := words(a), words(b)
|
||||
return len(wa) > 0 && slices.Equal(wa, wb)
|
||||
}
|
||||
|
||||
// matchAlbum finds the release group a library album is. A MusicBrainz
|
||||
@@ -204,7 +220,9 @@ func (s *Service) matchAlbum(ctx context.Context, a dbq.ListAlbumsToMatchRow, tr
|
||||
continue
|
||||
}
|
||||
tried[c.Group] = true
|
||||
n, size, miss, err := s.agreementWith(ctx, c.Group, tracks, sameTitle(c.Title, a.Title))
|
||||
n, size, miss, err := s.agreementWith(ctx, c.Group, tracks, func(edition string) bool {
|
||||
return sameTitle(c.Title, a.Title) || sameTitle(edition, a.Title)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -328,7 +346,9 @@ func (s *Service) searchCandidates(ctx context.Context, a dbq.ListAlbumsToMatchR
|
||||
// agreementWith is how many of the album's tracks the group's best edition
|
||||
// agrees with, that edition's length, and how it differs. A group
|
||||
// MusicBrainz no longer has, or lists no edition of, agrees with none (-1).
|
||||
func (s *Service) agreementWith(ctx context.Context, group string, tracks []libraryTrack, titlesEnough bool) (n, size int, miss string, err error) {
|
||||
// ownName says whether an edition, by its title, is called what the album
|
||||
// is, so that its track titles are enough.
|
||||
func (s *Service) agreementWith(ctx context.Context, group string, tracks []libraryTrack, ownName func(edition string) bool) (n, size int, miss string, err error) {
|
||||
eds, err := s.cat.Releases(ctx, group)
|
||||
switch {
|
||||
case errors.Is(err, catalogue.ErrNotFound):
|
||||
@@ -343,7 +363,7 @@ func (s *Service) agreementWith(ctx context.Context, group string, tracks []libr
|
||||
edition = append(edition, editionTrack{Medium: int(t.MediumPosition), Position: int(t.Position),
|
||||
Title: t.Title, LengthMs: int(t.LengthMs), Recording: t.RecordingMbid})
|
||||
}
|
||||
if got, m := trackAgreement(tracks, edition, titlesEnough); got > n || (got == n && abs(len(edition)-len(tracks)) < abs(size-len(tracks))) {
|
||||
if got, m := trackAgreement(tracks, edition, ownName(e.Release.Title)); got > n || (got == n && abs(len(edition)-len(tracks)) < abs(size-len(tracks))) {
|
||||
n, size, miss = got, len(edition), m
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user