From b53146c599adf4782bb57962ff3d7a60d7b52cd3 Mon Sep 17 00:00:00 2001 From: zarzet Date: Tue, 14 Jul 2026 07:15:47 +0700 Subject: [PATCH] fix(reenrich): keep release identity when the matched track is from another album An ISRC identifies a recording, not a release, so identifier-first and search matches could resolve to a compilation of the same song and overwrite the file's album, cover, track position, and date with the compilation's. When the file already knows its album and the candidate's album doesn't match, those release-scoped fields are now preserved; recording-level fields (title, artist, ISRC, genre) still enrich. --- go_backend/exports_reenrich.go | 37 +++++++++++++++++++++++------- go_backend/exports_test.go | 41 ++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/go_backend/exports_reenrich.go b/go_backend/exports_reenrich.go index 9b8eb7e2..8484290f 100644 --- a/go_backend/exports_reenrich.go +++ b/go_backend/exports_reenrich.go @@ -74,11 +74,30 @@ func (r *reEnrichRequest) lyricsSidecarEnabled() bool { return mode == "external" || mode == "both" } +// reEnrichSameRelease reports whether the candidate track appears to come +// from the same release as the file's existing album. An ISRC identifies a +// recording, not a release: the same song often also resolves to a +// compilation, whose album name, cover, and track positions must not +// replace the original release's. +func reEnrichSameRelease(currentAlbum, candidateAlbum string) bool { + if isPlaceholderReEnrichValue(currentAlbum) || + strings.TrimSpace(candidateAlbum) == "" { + return true + } + return titlesMatch(currentAlbum, candidateAlbum) +} + func applyReEnrichTrackMetadata(req *reEnrichRequest, track ExtTrackMetadata) { if req == nil { return } + sameRelease := reEnrichSameRelease(req.AlbumName, track.AlbumName) + if !sameRelease { + GoLog("[ReEnrich] Candidate album %q differs from file album %q; keeping release identity (album, cover, positions, date)\n", + track.AlbumName, req.AlbumName) + } + if track.SpotifyID != "" { req.SpotifyID = track.SpotifyID } else if track.DeezerID != "" { @@ -98,14 +117,16 @@ func applyReEnrichTrackMetadata(req *reEnrichRequest, track ExtTrackMetadata) { if track.Artists != "" { req.ArtistName = track.Artists } - if track.AlbumName != "" { - req.AlbumName = track.AlbumName - } - if track.AlbumArtist != "" { - req.AlbumArtist = track.AlbumArtist + if sameRelease { + if track.AlbumName != "" { + req.AlbumName = track.AlbumName + } + if track.AlbumArtist != "" { + req.AlbumArtist = track.AlbumArtist + } } } - if req.shouldUpdateField("track_info") { + if sameRelease && req.shouldUpdateField("track_info") { if track.TrackNumber > 0 { req.TrackNumber = track.TrackNumber } @@ -120,14 +141,14 @@ func applyReEnrichTrackMetadata(req *reEnrichRequest, track ExtTrackMetadata) { } } if req.shouldUpdateField("release_info") { - if track.ReleaseDate != "" { + if sameRelease && track.ReleaseDate != "" { req.ReleaseDate = track.ReleaseDate } if track.ISRC != "" { req.ISRC = track.ISRC } } - if req.shouldUpdateField("cover") { + if sameRelease && req.shouldUpdateField("cover") { if coverURL := track.ResolvedCoverURL(); coverURL != "" { req.CoverURL = coverURL } diff --git a/go_backend/exports_test.go b/go_backend/exports_test.go index dc0a8ccb..dc1e2f85 100644 --- a/go_backend/exports_test.go +++ b/go_backend/exports_test.go @@ -352,7 +352,7 @@ func TestApplyReEnrichTrackMetadataPreservesExistingReleaseDateWhenCandidateMiss } applyReEnrichTrackMetadata(&req, ExtTrackMetadata{ - AlbumName: "Resolved Album", + AlbumName: "Original Album (Deluxe)", ReleaseDate: "", ISRC: "", }) @@ -360,7 +360,7 @@ func TestApplyReEnrichTrackMetadataPreservesExistingReleaseDateWhenCandidateMiss if req.ReleaseDate != "2024-01-01" { t.Fatalf("release date = %q, want existing value preserved", req.ReleaseDate) } - if req.AlbumName != "Resolved Album" { + if req.AlbumName != "Original Album (Deluxe)" { t.Fatalf("album = %q, want updated album", req.AlbumName) } if req.ISRC != "REQ123" { @@ -368,6 +368,43 @@ func TestApplyReEnrichTrackMetadataPreservesExistingReleaseDateWhenCandidateMiss } } +func TestApplyReEnrichTrackMetadataKeepsReleaseIdentityOnAlbumMismatch(t *testing.T) { + req := reEnrichRequest{ + TrackName: "Afsana", + ArtistName: "Artist Name", + AlbumName: "Original Soundtrack", + CoverURL: "https://covers/original.jpg", + TrackNumber: 3, + ReleaseDate: "2005-01-01", + } + + applyReEnrichTrackMetadata(&req, ExtTrackMetadata{ + Name: "Afsana", + Artists: "Artist Name", + AlbumName: "The Hit Machine", + CoverURL: "https://covers/compilation.jpg", + TrackNumber: 17, + ReleaseDate: "2010-01-01", + ISRC: "NEW123", + }) + + if req.AlbumName != "Original Soundtrack" { + t.Fatalf("album = %q, want original release kept", req.AlbumName) + } + if req.CoverURL != "https://covers/original.jpg" { + t.Fatalf("cover = %q, want original release cover kept", req.CoverURL) + } + if req.TrackNumber != 3 { + t.Fatalf("track number = %d, want original position kept", req.TrackNumber) + } + if req.ReleaseDate != "2005-01-01" { + t.Fatalf("release date = %q, want original date kept", req.ReleaseDate) + } + if req.ISRC != "NEW123" { + t.Fatalf("isrc = %q, want recording-level fields still enriched", req.ISRC) + } +} + func TestSelectBestReEnrichTrackPrefersCandidateWithReleaseDate(t *testing.T) { req := reEnrichRequest{ TrackName: "Song Title",