feat: format multi-artist credits

This commit is contained in:
2026-07-11 04:44:54 +02:00
parent d2fa098d69
commit 3413de1daa
8 changed files with 349 additions and 26 deletions
+26 -3
View File
@@ -564,7 +564,8 @@ func resultMap(resp map[string]any) map[string]any {
func normalizeTrack(raw map[string]any, fallbackID string, artistSeparator string) map[string]any {
trackID := canonicalTrackRefFromRaw(raw, fallbackID)
albumRaw := firstAlbum(raw)
artistName := joinArtists(raw, artistSeparator)
artistNames := artistNames(raw)
artistName := displayArtistNames(artistNames)
albumArtist := joinAlbumArtists(albumRaw, artistSeparator)
if albumArtist == "" {
albumArtist = artistName
@@ -589,6 +590,7 @@ func normalizeTrack(raw map[string]any, fallbackID string, artistSeparator strin
"source_track_id": jsonutil.StringFromAny(raw["realId"]),
"performer": map[string]any{"name": artistName},
"artist": map[string]any{"name": artistName, "id": firstArtistID(raw)},
"artist_names": artistNames,
"album": normalizeAlbumSummary(albumRaw, albumArtist),
"image": imageMapFromTrack(raw, albumRaw),
"cover": imageMapFromTrack(raw, albumRaw),
@@ -769,6 +771,10 @@ func firstArtistID(raw map[string]any) string {
}
func joinArtists(raw map[string]any, artistSeparator string) string {
return displayArtistNames(artistNames(raw))
}
func artistNames(raw map[string]any) []string {
artists, _ := raw["artists"].([]any)
parts := make([]string, 0, len(artists))
for _, entry := range artists {
@@ -780,13 +786,17 @@ func joinArtists(raw map[string]any, artistSeparator string) string {
parts = append(parts, name)
}
}
return strings.Join(parts, artistSeparator)
return parts
}
func joinAlbumArtists(albumRaw map[string]any, artistSeparator string) string {
if len(albumRaw) == 0 {
return ""
}
return displayArtistNames(albumArtistNames(albumRaw))
}
func albumArtistNames(albumRaw map[string]any) []string {
artists, _ := albumRaw["artists"].([]any)
parts := make([]string, 0, len(artists))
for _, entry := range artists {
@@ -798,7 +808,20 @@ func joinAlbumArtists(albumRaw map[string]any, artistSeparator string) string {
parts = append(parts, name)
}
}
return strings.Join(parts, artistSeparator)
return parts
}
func displayArtistNames(names []string) string {
switch len(names) {
case 0:
return ""
case 1:
return names[0]
case 2:
return names[0] + " & " + names[1]
default:
return strings.Join(names[:len(names)-1], ", ") + " & " + names[len(names)-1]
}
}
func (c *Client) artistSeparator() string {
+13 -2
View File
@@ -155,12 +155,23 @@ func TestGetMetadataTrackUsesModernTracksEndpoint(t *testing.T) {
if album, _ := meta["album"].(map[string]any); jsonutil.StringFromAny(album["title"]) != "OutRun" {
t.Fatalf("unexpected album: %+v", album)
}
if artist := jsonutil.NestedString(meta, "artist", "name"); artist != "Kavinsky; Lovefoxxx" {
if artist := jsonutil.NestedString(meta, "artist", "name"); artist != "Kavinsky & Lovefoxxx" {
t.Fatalf("artist = %q", artist)
}
if albumArtist := jsonutil.NestedString(meta, "album", "artist", "name"); albumArtist != "Kavinsky; Lovefoxxx" {
if albumArtist := jsonutil.NestedString(meta, "album", "artist", "name"); albumArtist != "Kavinsky & Lovefoxxx" {
t.Fatalf("album artist = %q", albumArtist)
}
artists, _ := meta["artist_names"].([]string)
if strings.Join(artists, ";") != "Kavinsky;Lovefoxxx" {
t.Fatalf("artist_names = %#v", artists)
}
}
func TestDisplayArtistNamesUsesCreditPunctuation(t *testing.T) {
got := displayArtistNames([]string{"A", "B", "C"})
if got != "A, B & C" {
t.Fatalf("displayArtistNames() = %q", got)
}
}
func TestLegacyDirectURLBuildsPlayableMP3URL(t *testing.T) {