mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-14 05:49:02 +02:00
fix(metadata): match recording IDs across inconsistent catalogs
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package gobackend
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
@@ -254,7 +255,15 @@ func titlesMatch(expectedTitle, foundTitle string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var trackTitleAnnotationPattern = regexp.MustCompile(`(?i)[(\[]\s*(?:(?:feat\.?|ft\.?|featuring)\s+[^)\]]+|from\s+["“][^)\]]+["”]\s*)[)\]]`)
|
||||
|
||||
func normalizeTrackIdentityTitle(title string) string {
|
||||
return normalizeLooseTitle(trackTitleAnnotationPattern.ReplaceAllString(title, " "))
|
||||
}
|
||||
|
||||
func trackTitlesMatch(expectedTitle, foundTitle string) bool {
|
||||
expectedTitle = trackTitleAnnotationPattern.ReplaceAllString(expectedTitle, " ")
|
||||
foundTitle = trackTitleAnnotationPattern.ReplaceAllString(foundTitle, " ")
|
||||
expected := normalizeLooseTitle(expectedTitle)
|
||||
found := normalizeLooseTitle(foundTitle)
|
||||
if expected != "" && expected == found {
|
||||
@@ -417,7 +426,7 @@ func hasStrongTrackIdentity(req DownloadRequest, resolved resolvedTrackInfo) boo
|
||||
return false
|
||||
}
|
||||
|
||||
titleExact := exactLooseIdentityMatch(req.TrackName, resolved.Title, normalizeLooseTitle)
|
||||
titleExact := exactLooseIdentityMatch(req.TrackName, resolved.Title, normalizeTrackIdentityTitle)
|
||||
if !titleExact {
|
||||
return false
|
||||
}
|
||||
@@ -471,6 +480,17 @@ func trackMatchesRequest(req DownloadRequest, resolved resolvedTrackInfo, logPre
|
||||
diff = -diff
|
||||
}
|
||||
if diff > 10 {
|
||||
// Catalog durations can disagree even for the same recording. Require
|
||||
// both its ISRC and matching names; a preview still cannot qualify.
|
||||
if exactISRCMatch && req.TrackName != "" && resolved.Title != "" &&
|
||||
exactLooseIdentityMatch(req.TrackName, resolved.Title, normalizeTrackIdentityTitle) &&
|
||||
req.ArtistName != "" && resolved.ArtistName != "" &&
|
||||
artistsMatch(req.ArtistName, resolved.ArtistName) &&
|
||||
!(resolved.Duration <= 35 && expectedDurationSec > 45) {
|
||||
GoLog("[%s] Accepted catalog duration difference for matching ISRC and recording names: expected %ds, got %ds\n",
|
||||
logPrefix, expectedDurationSec, resolved.Duration)
|
||||
return true
|
||||
}
|
||||
GoLog("[%s] Verification failed: duration mismatch — expected %ds, got %ds\n",
|
||||
logPrefix, expectedDurationSec, resolved.Duration)
|
||||
return false
|
||||
|
||||
@@ -230,6 +230,88 @@ func TestTrackMetadataTolerancePreservesRecordingIdentity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackIdentityIgnoresCreditAndSoundtrackAnnotations(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
expected string
|
||||
found string
|
||||
want bool
|
||||
}{
|
||||
{"soundtrack", "Signal", `Signal (From "Original Soundtrack")`, true},
|
||||
{"mix credit", "Signal - Tiger Style Mix", "Signal (feat. Guest) [Tiger Style Mix]", true},
|
||||
{"mix soundtrack", "Signal - Tiger Style Mix", `Signal (Tiger Style Mix) [From "Original Soundtrack"]`, true},
|
||||
{"different mix", "Signal - Tiger Style Mix", "Signal (feat. Guest) [Club Mix]", false},
|
||||
{"original and mix", "Signal", "Signal (feat. Guest) [Tiger Style Mix]", false},
|
||||
{"unrelated title", "Signal", `Another Song (From "Signal")`, false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
req := DownloadRequest{
|
||||
TrackName: tc.expected, ArtistName: "Composer, Singer & Writer",
|
||||
AlbumName: "Original Soundtrack", DurationMS: 280000,
|
||||
}
|
||||
resolved := resolvedTrackInfo{
|
||||
Title: tc.found, ArtistName: "Singer & Composer",
|
||||
AlbumName: "Collection", Duration: 283,
|
||||
}
|
||||
if got := trackMatchesRequest(req, resolved, "test"); got != tc.want {
|
||||
t.Fatalf("trackMatchesRequest = %v, want %v", got, tc.want)
|
||||
}
|
||||
tracks := []ExtTrackMetadata{{
|
||||
Name: resolved.Title, Artists: resolved.ArtistName, AlbumName: resolved.AlbumName,
|
||||
DurationMS: resolved.Duration * 1000, ProviderID: "provider",
|
||||
}}
|
||||
if got := selectBestMetadataEnrichmentTrack(req, tracks) != nil; got != tc.want {
|
||||
t.Fatalf("metadata enrichment match = %v, want %v", got, tc.want)
|
||||
}
|
||||
resolved.Duration = 244
|
||||
if trackMatchesRequest(req, resolved, "test") {
|
||||
t.Fatal("title annotations must not bypass a duration mismatch")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackIdentityResolvesConflictingCatalogDurations(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
title string
|
||||
expected int
|
||||
found int
|
||||
}{
|
||||
{"Signal", 280000, 244},
|
||||
{"Signal (Tiger Style Mix)", 243000, 280},
|
||||
} {
|
||||
t.Run(tc.title, func(t *testing.T) {
|
||||
req := DownloadRequest{
|
||||
TrackName: tc.title, ArtistName: "Composer, Singer & Writer", AlbumName: "Soundtrack",
|
||||
ISRC: "USAAA0000001", DurationMS: tc.expected,
|
||||
}
|
||||
resolved := resolvedTrackInfo{
|
||||
Title: tc.title, ArtistName: "Singer & Composer", AlbumName: "Collection",
|
||||
ISRC: req.ISRC, Duration: tc.found,
|
||||
}
|
||||
if !trackMatchesRequest(req, resolved, "test") {
|
||||
t.Fatal("matching ISRC and recording names should resolve inconsistent catalog durations")
|
||||
}
|
||||
for _, isrc := range []string{"", "USAAA0000002"} {
|
||||
resolved.ISRC = isrc
|
||||
if trackMatchesRequest(req, resolved, "test") {
|
||||
t.Fatal("duration discrepancy requires an exact ISRC")
|
||||
}
|
||||
}
|
||||
resolved.ISRC = req.ISRC
|
||||
resolved.Duration = 30
|
||||
if trackMatchesRequest(req, resolved, "test") {
|
||||
t.Fatal("a preview must not qualify as a catalog duration discrepancy")
|
||||
}
|
||||
resolved.Duration = tc.found
|
||||
resolved.Title += " (Live)"
|
||||
if trackMatchesRequest(req, resolved, "test") {
|
||||
t.Fatal("inconsistent names and durations must not qualify on ISRC alone")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTitlesMatch_EmojiStrict(t *testing.T) {
|
||||
if titlesMatch("🪐", "Higher Power") {
|
||||
t.Fatal("expected emoji title not to match unrelated textual title")
|
||||
|
||||
Reference in New Issue
Block a user