mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-07-27 23:42:28 +02:00
feat: format multi-artist credits
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user