From 32ba922fdaf693177c579227fb6e93a499f44820 Mon Sep 17 00:00:00 2001 From: gbPagano Date: Sun, 5 Jul 2026 15:36:04 -0300 Subject: [PATCH] fix: split listenbrainz spotify artist credits --- engine/handlers/lbz_submit_listen.go | 10 ++++++++-- internal/catalog/catalog.go | 27 ++++++++++++++++++++++++++- internal/catalog/catalog_test.go | 9 +++++++++ internal/importer/listenbrainz.go | 9 +++++++-- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/engine/handlers/lbz_submit_listen.go b/engine/handlers/lbz_submit_listen.go index 8ca68fa2..daeff052 100644 --- a/engine/handlers/lbz_submit_listen.go +++ b/engine/handlers/lbz_submit_listen.go @@ -69,6 +69,7 @@ type LbzAdditionalInfo struct { Duration int32 `json:"duration,omitempty"` Tags []string `json:"tags,omitempty"` AlbumArtist string `json:"albumartist,omitempty"` + SpotifyArtistIDs []string `json:"spotify_artist_ids,omitempty"` } const ( @@ -153,7 +154,7 @@ func LbzSubmitListenHandler(store submitListenHandlerStore, mbzc mbz.MusicBrainz } if len(artistMbzIDs) < 1 { l.Debug().AnErr("error", err).Msg("LbzSubmitListenHandler: Attempting to parse artist UUIDs from mbid_mapping") - utils.ParseUUIDSlice(payload.TrackMeta.MBIDMapping.ArtistMBIDs) + artistMbzIDs, err = utils.ParseUUIDSlice(payload.TrackMeta.MBIDMapping.ArtistMBIDs) if err != nil { l.Debug().AnErr("error", err).Msg("LbzSubmitListenHandler: Failed to parse one or more UUIDs") } @@ -208,9 +209,14 @@ func LbzSubmitListenHandler(store submitListenHandlerStore, mbzc mbz.MusicBrainz artistMbidMap = append(artistMbidMap, catalog.ArtistMbidMap{Artist: a.ArtistName, Mbid: mbid}) } + artistNames := payload.TrackMeta.AdditionalInfo.ArtistNames + if len(artistNames) < 1 { + artistNames = catalog.InferArtistNamesFromSourceIDs(payload.TrackMeta.ArtistName, payload.TrackMeta.AdditionalInfo.SpotifyArtistIDs) + } + opts := catalog.SubmitListenOpts{ MbzCaller: mbzc, - ArtistNames: payload.TrackMeta.AdditionalInfo.ArtistNames, + ArtistNames: artistNames, Artist: payload.TrackMeta.ArtistName, ArtistMbzIDs: artistMbzIDs, TrackTitle: payload.TrackMeta.TrackName, diff --git a/internal/catalog/catalog.go b/internal/catalog/catalog.go index 6ca8f038..38e68d8d 100644 --- a/internal/catalog/catalog.go +++ b/internal/catalog/catalog.go @@ -215,9 +215,34 @@ var ( inlineFeatPattern = regexp.MustCompile(`(?i)[fF]eat\. ([^()\[\]]+)$`) // Delimiters only used inside feat. sections - featSplitDelimiters = regexp.MustCompile(`(?i)\s*(?:,|&|and|·)\s*`) + featSplitDelimiters = regexp.MustCompile(`(?i)\s*(?:,|&|and|·)\s*`) + commaArtistDelimiter = regexp.MustCompile(`\s*,\s*`) ) +// InferArtistNamesFromSourceIDs splits artist credits when a source gives one +// external artist ID per comma-delimited name but no explicit artist_names list. +func InferArtistNamesFromSourceIDs(artist string, sourceIDs []string) []string { + if len(sourceIDs) < 2 { + return nil + } + + parts := commaArtistDelimiter.Split(artist, -1) + if len(parts) != len(sourceIDs) { + return nil + } + + artists := make([]string, 0, len(parts)) + for _, part := range parts { + part = strings.TrimSpace(part) + if part == "" { + return nil + } + artists = append(artists, part) + } + + return artists +} + // ParseArtists extracts all contributing artist names from the artist and title strings func ParseArtists(artist string, title string, addlSeparators []*regexp.Regexp) []string { seen := make(map[string]struct{}) diff --git a/internal/catalog/catalog_test.go b/internal/catalog/catalog_test.go index ed2445f0..1a87488f 100644 --- a/internal/catalog/catalog_test.go +++ b/internal/catalog/catalog_test.go @@ -379,3 +379,12 @@ func TestArtistStringParse(t *testing.T) { assert.ElementsMatch(t, out, artists) } } + +func TestInferArtistNamesFromSourceIDs(t *testing.T) { + assert.Equal(t, + []string{"Pineapple StormTv", "Sant", "Tiago Mac"}, + catalog.InferArtistNamesFromSourceIDs("Pineapple StormTv, Sant, Tiago Mac", []string{"spotify:1", "spotify:2", "spotify:3"}), + ) + assert.Nil(t, catalog.InferArtistNamesFromSourceIDs("Tyler, The Creator", []string{"spotify:1"})) + assert.Nil(t, catalog.InferArtistNamesFromSourceIDs("Earth, Wind & Fire, Foo", []string{"spotify:1", "spotify:2"})) +} diff --git a/internal/importer/listenbrainz.go b/internal/importer/listenbrainz.go index e089b34d..3d1363d5 100644 --- a/internal/importer/listenbrainz.go +++ b/internal/importer/listenbrainz.go @@ -88,7 +88,7 @@ func ImportListenBrainzFile(ctx context.Context, store importStore, mbzc mbz.Mus } if len(artistMbzIDs) < 1 { l.Debug().AnErr("error", err).Msg("ImportListenBrainzFile: Attempting to parse artist UUIDs from mbid_mapping") - utils.ParseUUIDSlice(payload.TrackMeta.MBIDMapping.ArtistMBIDs) + artistMbzIDs, err = utils.ParseUUIDSlice(payload.TrackMeta.MBIDMapping.ArtistMBIDs) if err != nil { l.Debug().AnErr("error", err).Msg("ImportListenBrainzFile: Failed to parse one or more UUIDs") } @@ -138,9 +138,14 @@ func ImportListenBrainzFile(ctx context.Context, store importStore, mbzc mbz.Mus artistMbidMap = append(artistMbidMap, catalog.ArtistMbidMap{Artist: a.ArtistName, Mbid: mbid}) } + artistNames := payload.TrackMeta.AdditionalInfo.ArtistNames + if len(artistNames) < 1 { + artistNames = catalog.InferArtistNamesFromSourceIDs(payload.TrackMeta.ArtistName, payload.TrackMeta.AdditionalInfo.SpotifyArtistIDs) + } + opts := catalog.SubmitListenOpts{ MbzCaller: mbzc, - ArtistNames: payload.TrackMeta.AdditionalInfo.ArtistNames, + ArtistNames: artistNames, Artist: payload.TrackMeta.ArtistName, ArtistMbzIDs: artistMbzIDs, TrackTitle: payload.TrackMeta.TrackName,