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
+21 -12
View File
@@ -507,7 +507,8 @@ func (c *Client) normalizeRelease(raw map[string]any, tracks []any) map[string]a
func (c *Client) normalizeTrack(raw map[string]any) map[string]any {
release := mapAny(raw["release"])
artistName := c.joinArtists(raw["artists"])
trackArtistNames := artistNames(raw["artists"])
artistName := displayArtistNames(trackArtistNames)
artistID := firstArtistID(raw["artists"])
albumArtist := c.releaseArtistName(release["artists"])
if albumArtist == "" {
@@ -524,6 +525,7 @@ func (c *Client) normalizeTrack(raw map[string]any) map[string]any {
"version": jsonutil.StringFromAny(raw["mix_name"]),
"artist": map[string]any{"id": artistID, "name": artistName},
"performer": map[string]any{"id": artistID, "name": artistName},
"artist_names": trackArtistNames,
"album": map[string]any{"id": jsonutil.StringFromAny(release["id"]), "title": jsonutil.StringFromAny(release["name"]), "artist": map[string]any{"name": albumArtist}, "image": imageMap(release["image"]), "release_date_original": date},
"track_number": jsonutil.IntFromAny(raw["number"]),
"tracks_count": jsonutil.IntFromAny(release["track_count"]),
@@ -544,11 +546,12 @@ func (c *Client) normalizeTrack(raw map[string]any) map[string]any {
}
func (c *Client) normalizeTrackListItem(raw map[string]any) map[string]any {
names := artistNames(raw["artists"])
return map[string]any{
"id": jsonutil.StringFromAny(raw["id"]),
"title": jsonutil.StringFromAny(raw["name"]),
"version": jsonutil.StringFromAny(raw["mix_name"]),
"artist": map[string]any{"name": c.joinArtists(raw["artists"])},
"artist": map[string]any{"name": displayArtistNames(names)},
"album": map[string]any{"id": jsonutil.NestedString(raw, "release", "id"), "title": jsonutil.NestedString(raw, "release", "name")},
"track_number": jsonutil.IntFromAny(raw["number"]),
}
@@ -653,7 +656,7 @@ func audioProfile(quality, streamQuality string) (provider.AudioProfile, string)
}
}
func (c *Client) joinArtists(v any) string {
func artistNames(v any) []string {
items := sliceAny(v)
names := make([]string, 0, len(items))
for _, raw := range items {
@@ -665,7 +668,20 @@ func (c *Client) joinArtists(v any) string {
names = append(names, name)
}
}
return strings.Join(names, artistSeparator(c.cfg.Session.Metadata.ArtistSeparator))
return names
}
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) releaseArtistName(v any) string {
@@ -674,14 +690,7 @@ func (c *Client) releaseArtistName(v any) string {
if threshold >= 0 && len(items) > threshold {
return "Various Artists"
}
return c.joinArtists(v)
}
func artistSeparator(separator string) string {
if separator == "" {
return "; "
}
return separator
return displayArtistNames(artistNames(v))
}
func firstArtistID(v any) string {
+7 -9
View File
@@ -164,21 +164,19 @@ func TestReleaseArtistCollapseCanBeDisabled(t *testing.T) {
map[string]any{"name": "C"},
map[string]any{"name": "D"},
})
if got != "A; B; C; D" {
if got != "A, B, C & D" {
t.Fatalf("releaseArtistName() = %q", got)
}
}
func TestArtistSeparatorCanBeConfigured(t *testing.T) {
cfg := &config.Config{Session: config.DefaultConfigData(), File: config.DefaultConfigData()}
cfg.Session.Metadata.ArtistSeparator = " / "
c := New(cfg)
got := c.joinArtists([]any{
func TestDisplayArtistNamesUsesCreditPunctuation(t *testing.T) {
got := displayArtistNames(artistNames([]any{
map[string]any{"name": "A"},
map[string]any{"name": "B"},
})
if got != "A / B" {
t.Fatalf("joinArtists() = %q", got)
map[string]any{"name": "C"},
}))
if got != "A, B & C" {
t.Fatalf("displayArtistNames() = %q", got)
}
}