Skip to content

Commit 5453b7d

Browse files
author
ShimmerGlass
committed
fix: use request track count, better mix artist similar songs
The plugin used a config value for the track count to return for GetSimilarSongsByTrack, and returned everything for GetSimilarSongsByArtist. This ignores the "count" parameter in the request, which indicates how many songs the player wants. This change makes use of this parameter and removes the plugin config value to be better match what navidrome expects, and have one less thing for users to configure / worry about. For GetSimilarSongsByTrack we simply use the parameter from the query and keep the same logic. For GetSimilarSongsByArtist, however, no limit was implemented. Before this change the plugin returned tracks in "clumps": a few tracks for artist1, a few tracks for artist2, etc. I changed the logic to instead alternate between the "base artist" (from the request) and related artists, stopping when we have the desired number of tracks. This IMHO better emulates the "radio" features of popular streaming services.
1 parent c279bc1 commit 5453b7d

File tree

2 files changed

+51
-31
lines changed

2 files changed

+51
-31
lines changed

main.go

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"net/url"
7+
"slices"
78
"strconv"
89

910
"github.com/navidrome/navidrome/plugins/pdk/go/metadata"
@@ -13,15 +14,13 @@ import (
1314
// Configuration keys (must match manifest.json)
1415
const (
1516
configAPIUrl = "apiUrl"
16-
configTrackCount = "trackCount"
1717
configEliminateDuplicates = "eliminateDuplicates"
1818
configRadiusSimilarity = "radiusSimilarity"
1919
)
2020

2121
// Default values
2222
const (
2323
defaultAPIUrl = "http://192.168.3.203:8000"
24-
defaultTrackCount = 200
2524
defaultArtistSimilarCount = 10
2625
defaultEliminateDuplicates = true
2726
defaultRadiusSimilarity = true
@@ -81,17 +80,16 @@ func (p *audioMusePlugin) GetSimilarSongsByTrack(input metadata.SimilarSongsByTr
8180

8281
// Read configuration
8382
apiBaseURL := getConfigString(configAPIUrl, defaultAPIUrl)
84-
trackCount := getConfigInt(configTrackCount, defaultTrackCount)
8583
eliminateDuplicates := getConfigBool(configEliminateDuplicates, defaultEliminateDuplicates)
8684
radiusSimilarity := getConfigBool(configRadiusSimilarity, defaultRadiusSimilarity)
8785

8886
pdk.Log(pdk.LogDebug, fmt.Sprintf("[AudioMuse] Config - API URL: %s, TrackCount: %d, EliminateDuplicates: %v, RadiusSimilarity: %v",
89-
apiBaseURL, trackCount, eliminateDuplicates, radiusSimilarity))
87+
apiBaseURL, input.Count, eliminateDuplicates, radiusSimilarity))
9088

9189
// Build the API URL with query parameters
9290
params := url.Values{}
9391
params.Set("item_id", input.ID)
94-
params.Set("n", strconv.Itoa(trackCount))
92+
params.Set("n", strconv.Itoa(int(input.Count)))
9593
params.Set("eliminate_duplicates", strconv.FormatBool(eliminateDuplicates))
9694
params.Set("radius_similarity", strconv.FormatBool(radiusSimilarity))
9795

@@ -151,37 +149,69 @@ func (p *audioMusePlugin) GetSimilarSongsByArtist(input metadata.SimilarSongsByA
151149
return nil, err
152150
}
153151

154-
// Collect unique song IDs preserving order
155-
songIDs := make([]string, 0)
156152
seen := make(map[string]bool)
157153

154+
// songSlices contains artist songs in alternating order: [baseArtist, relatedArtist1, baseArtist, relatedArtist2, ...]
155+
songSlices := [][]metadata.SongRef{}
156+
158157
for _, a := range artists {
158+
var artist1Songs, artist2Songs []metadata.SongRef
159+
159160
for _, cm := range a.ComponentMatches {
160161
for _, s := range cm.Artist1RepresentativeSongs {
162+
161163
if s.ItemID == "" {
162164
continue
163165
}
164-
if !seen[s.ItemID] {
165-
seen[s.ItemID] = true
166-
songIDs = append(songIDs, s.ItemID)
166+
if seen[s.ItemID] {
167+
continue
167168
}
169+
170+
seen[s.ItemID] = true
171+
artist1Songs = append(artist1Songs, metadata.SongRef{ID: s.ItemID, Name: s.Title})
168172
}
173+
169174
for _, s := range cm.Artist2RepresentativeSongs {
170175
if s.ItemID == "" {
171176
continue
172177
}
173-
if !seen[s.ItemID] {
174-
seen[s.ItemID] = true
175-
songIDs = append(songIDs, s.ItemID)
178+
179+
if seen[s.ItemID] {
180+
continue
176181
}
182+
183+
seen[s.ItemID] = true
184+
artist2Songs = append(artist2Songs, metadata.SongRef{ID: s.ItemID, Name: s.Title})
177185
}
178186
}
187+
188+
if len(artist1Songs) > 0 {
189+
songSlices = append(songSlices, artist1Songs)
190+
}
191+
if len(artist2Songs) > 0 {
192+
songSlices = append(songSlices, artist2Songs)
193+
}
179194
}
180195

181-
// Build SongRef list (names not always available)
182-
songs := make([]metadata.SongRef, 0, len(songIDs))
183-
for _, id := range songIDs {
184-
songs = append(songs, metadata.SongRef{ID: id})
196+
songs := make([]metadata.SongRef, 0, input.Count)
197+
198+
// get songs from our slices until we have enough or we ran out
199+
artistID := 0
200+
for len(songs) < int(input.Count) && len(songSlices) > 0 {
201+
song := songSlices[artistID][0] // take a song
202+
songs = append(songs, song)
203+
204+
songSlices[artistID] = songSlices[artistID][1:] // remove it from the pool
205+
206+
if len(songSlices[artistID]) == 0 {
207+
// this slice has no more songs, remove it
208+
songSlices = slices.Delete(songSlices, artistID, artistID+1)
209+
} else {
210+
// else, go to the next slice
211+
artistID++
212+
}
213+
214+
artistID = artistID % len(songSlices) // loop around if needed
185215
}
186216

187217
pdk.Log(pdk.LogInfo, fmt.Sprintf("[AudioMuse] Returning %d artist-related songs to Navidrome", len(songs)))

manifest.json

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,6 @@
1515
"description": "Base URL for the AudioMuse-AI server (e.g., http://192.168.3.203:8000).",
1616
"default": "http://192.168.3.203:8000"
1717
},
18-
"trackCount": {
19-
"type": "integer",
20-
"title": "Number of Similar Tracks",
21-
"description": "Maximum number of similar tracks to return. Default: 200",
22-
"default": 200,
23-
"minimum": 10,
24-
"maximum": 500
25-
},
2618
"artistSimilarCount": {
2719
"type": "integer",
2820
"title": "Number of Similar Artists",
@@ -62,10 +54,6 @@
6254
"type": "Group",
6355
"label": "Song Instant Mix",
6456
"elements": [
65-
{
66-
"type": "Control",
67-
"scope": "#/properties/trackCount"
68-
},
6957
{
7058
"type": "HorizontalLayout",
7159
"elements": [
@@ -100,7 +88,9 @@
10088
},
10189
"http": {
10290
"reason": "To call AudioMuse-AI API for similar tracks",
103-
"requiredHosts": ["*"]
91+
"requiredHosts": [
92+
"*"
93+
]
10494
}
10595
}
106-
}
96+
}

0 commit comments

Comments
 (0)