feat: add configurable artist separator

This commit is contained in:
2026-07-11 01:21:40 +02:00
parent 537959b6ec
commit d2fa098d69
8 changed files with 82 additions and 49 deletions
+29 -22
View File
@@ -28,13 +28,13 @@ import (
)
const (
baseURL = "https://api.music.yandex.net"
desktopClientHeader = "YandexMusicDesktopAppWindows/5.13.2"
desktopOrigin = "music-application://desktop"
requestAttempts = 3
baseURL = "https://api.music.yandex.net"
desktopClientHeader = "YandexMusicDesktopAppWindows/5.13.2"
desktopOrigin = "music-application://desktop"
requestAttempts = 3
desktopWindowsSignKey = "kzqU4XhfCaY6B6JTHODeq5"
legacyMP3SignSalt = "XGRlBW9FXlekgbPrRHuSiA"
defaultEstimatedKbps = 50000
legacyMP3SignSalt = "XGRlBW9FXlekgbPrRHuSiA"
defaultEstimatedKbps = 50000
)
var ErrMissingYandexToken = errors.New("missing yandex access_token")
@@ -183,7 +183,7 @@ func (c *Client) getTrackMetadata(ctx context.Context, item string) (map[string]
if len(items) == 0 {
return nil, errors.New("yandex track metadata missing result")
}
return normalizeTrack(items[0], trackRef), nil
return normalizeTrack(items[0], trackRef, c.artistSeparator()), nil
}
func (c *Client) getAlbumMetadata(ctx context.Context, item string) (map[string]any, error) {
@@ -198,7 +198,7 @@ func (c *Client) getAlbumMetadata(ctx context.Context, item string) (map[string]
if len(result) == 0 {
return nil, errors.New("yandex album metadata missing result")
}
return normalizeAlbum(result), nil
return normalizeAlbum(result, c.artistSeparator()), nil
}
func (c *Client) getArtistMetadata(ctx context.Context, item string) (map[string]any, error) {
@@ -273,7 +273,7 @@ func (c *Client) normalizeSearchItems(mediaType string, result map[string]any) [
if !ok {
continue
}
appendItem(normalizeTrack(itm, canonicalTrackRefFromRaw(itm, "")))
appendItem(normalizeTrack(itm, canonicalTrackRefFromRaw(itm, ""), c.artistSeparator()))
}
case "album":
for _, raw := range getResults("albums") {
@@ -281,7 +281,7 @@ func (c *Client) normalizeSearchItems(mediaType string, result map[string]any) [
if !ok {
continue
}
appendItem(normalizeAlbumSearchItem(itm))
appendItem(normalizeAlbumSearchItem(itm, c.artistSeparator()))
}
case "artist":
for _, raw := range getResults("artists") {
@@ -561,11 +561,11 @@ func resultMap(resp map[string]any) map[string]any {
return resp
}
func normalizeTrack(raw map[string]any, fallbackID string) 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)
albumArtist := joinAlbumArtists(albumRaw)
artistName := joinArtists(raw, artistSeparator)
albumArtist := joinAlbumArtists(albumRaw, artistSeparator)
if albumArtist == "" {
albumArtist = artistName
}
@@ -599,10 +599,10 @@ func normalizeTrack(raw map[string]any, fallbackID string) map[string]any {
return meta
}
func normalizeAlbum(raw map[string]any) map[string]any {
artistName := joinArtists(raw)
func normalizeAlbum(raw map[string]any, artistSeparator string) map[string]any {
artistName := joinArtists(raw, artistSeparator)
if artistName == "" {
artistName = joinAlbumArtists(raw)
artistName = joinAlbumArtists(raw, artistSeparator)
}
volumes := albumVolumes(raw)
items := make([]any, 0)
@@ -698,8 +698,8 @@ func normalizeAlbumSummary(albumRaw map[string]any, artistName string) map[strin
}
}
func normalizeAlbumSearchItem(raw map[string]any) map[string]any {
artistName := joinArtists(raw)
func normalizeAlbumSearchItem(raw map[string]any, artistSeparator string) map[string]any {
artistName := joinArtists(raw, artistSeparator)
return map[string]any{
"id": jsonutil.StringFromAny(raw["id"]),
"title": jsonutil.StringFromAny(raw["title"]),
@@ -768,7 +768,7 @@ func firstArtistID(raw map[string]any) string {
return jsonutil.StringFromAny(artist["id"])
}
func joinArtists(raw map[string]any) string {
func joinArtists(raw map[string]any, artistSeparator string) string {
artists, _ := raw["artists"].([]any)
parts := make([]string, 0, len(artists))
for _, entry := range artists {
@@ -780,10 +780,10 @@ func joinArtists(raw map[string]any) string {
parts = append(parts, name)
}
}
return strings.Join(parts, ", ")
return strings.Join(parts, artistSeparator)
}
func joinAlbumArtists(albumRaw map[string]any) string {
func joinAlbumArtists(albumRaw map[string]any, artistSeparator string) string {
if len(albumRaw) == 0 {
return ""
}
@@ -798,7 +798,14 @@ func joinAlbumArtists(albumRaw map[string]any) string {
parts = append(parts, name)
}
}
return strings.Join(parts, ", ")
return strings.Join(parts, artistSeparator)
}
func (c *Client) artistSeparator() string {
if c.cfg.Session.Metadata.ArtistSeparator == "" {
return "; "
}
return c.cfg.Session.Metadata.ArtistSeparator
}
func trackNumbers(albumRaw map[string]any) (int, int) {
+14 -8
View File
@@ -47,13 +47,13 @@ func TestGetDownloadableUsesModernGetFileInfo(t *testing.T) {
_ = json.NewEncoder(w).Encode(map[string]any{
"result": map[string]any{
"downloadInfo": map[string]any{
"trackId": "32038184",
"quality": "lossless",
"codec": "flac-mp4",
"trackId": "32038184",
"quality": "lossless",
"codec": "flac-mp4",
"transport": "encraw",
"key": "00112233445566778899aabbccddeeff",
"bitrate": 0,
"url": "https://strm.example/music-v2/crypt/x/flac-mp4",
"key": "00112233445566778899aabbccddeeff",
"bitrate": 0,
"url": "https://strm.example/music-v2/crypt/x/flac-mp4",
},
},
})
@@ -115,13 +115,13 @@ func TestGetMetadataTrackUsesModernTracksEndpoint(t *testing.T) {
"id": "9442712",
"realId": "9442712",
"title": "Nightcall",
"artists": []map[string]any{{"id": "1433871", "name": "Kavinsky"}},
"artists": []map[string]any{{"id": "1433871", "name": "Kavinsky"}, {"id": "42", "name": "Lovefoxxx"}},
"albums": []map[string]any{{
"id": "1000856",
"title": "OutRun",
"releaseDate": "2013-02-25T00:00:00+04:00",
"trackCount": 13,
"artists": []map[string]any{{"id": "1433871", "name": "Kavinsky"}},
"artists": []map[string]any{{"id": "1433871", "name": "Kavinsky"}, {"id": "42", "name": "Lovefoxxx"}},
"trackPosition": map[string]any{
"index": 0,
"volume": 1,
@@ -155,6 +155,12 @@ 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" {
t.Fatalf("artist = %q", artist)
}
if albumArtist := jsonutil.NestedString(meta, "album", "artist", "name"); albumArtist != "Kavinsky; Lovefoxxx" {
t.Fatalf("album artist = %q", albumArtist)
}
}
func TestLegacyDirectURLBuildsPlayableMP3URL(t *testing.T) {