www.byjp.me/tools/music-normalizer/sort.go
2025-02-23 10:36:39 +00:00

125 lines
2.6 KiB
Go

package main
import (
"strings"
)
type mbResponse struct {
Recordings []mbRecording
}
type mbRecording struct {
ID string
Score int
Title string
Disambiguation string
FirstRelease string `json:"first-release-date"`
ArtistCredit []mbArtist `json:"artist-credit"`
Releases []mbRelease `json:"releases"`
}
type mbArtist struct {
Name string `json:"name"`
}
type mbRelease struct {
ID string
Score int
Title string
Country string
Status string
Quality string
ArtistCredit []mbArtist `json:"artist-credit"`
Date string `json:"first-release-date"`
}
type ByDesirability []mbRecording
func (r ByDesirability) Len() int { return len(r) }
func (r ByDesirability) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r ByDesirability) Less(i, j int) bool {
return sortInTurn(r[i], r[j],
// highScoreFirst,
preferGoodRelease,
preferNonLiveRecordings,
earliestRelease,
preferShorterName,
deterministicallyByID,
)
}
func sortInTurn[T interface{}](a, b T, comparators ...func(T, T) int) bool {
for _, comparator := range comparators {
switch comparator(a, b) {
case -1:
return true
case 1:
return false
default:
// Try the next comparator
}
}
// Don't swap if they're identical
return false
}
func ternary(first, last bool) int {
if first {
return -1
} else if last {
return 1
} else {
return 0
}
}
func highScoreFirst(a, b mbRecording) int {
as := a.Score / 20
bs := b.Score / 20
return ternary(as > bs, as < bs)
}
func preferNonLiveRecordings(a, b mbRecording) int {
al := stringIs(a.Disambiguation, "live")
bl := stringIs(b.Disambiguation, "live")
return ternary(!al && bl, al && !bl)
}
func preferGoodRelease(a, b mbRecording) int {
filter := func(r mbRelease) bool {
if len(r.ArtistCredit) > 0 && stringIs(r.ArtistCredit[0].Name, "various") {
return false
}
if !stringIs(r.Status, "official") {
return false
}
return true
}
ar := filterReleases(a, filter)
br := filterReleases(a, filter)
return ternary(!ar && br, ar && !br)
}
func preferShorterName(a, b mbRecording) int {
return ternary(len(a.Title) < len(b.Title), len(a.Title) > len(b.Title))
}
func filterReleases(r mbRecording, filter func(mbRelease) bool) bool {
for _, rel := range r.Releases {
if filter(rel) {
return true
}
}
return false
}
func earliestRelease(a, b mbRecording) int {
ad := parseReleaseDate(a.FirstRelease)
bd := parseReleaseDate(b.FirstRelease)
return ternary(ad.Before(bd), ad.After(bd))
}
func deterministicallyByID(a, b mbRecording) int {
return strings.Compare(a.ID, b.ID)
}