Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 50 additions & 17 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/url"
"slices"
"strconv"

"github.com/navidrome/navidrome/plugins/pdk/go/metadata"
Expand All @@ -13,15 +14,13 @@ import (
// Configuration keys (must match manifest.json)
const (
configAPIUrl = "apiUrl"
configTrackCount = "trackCount"
configEliminateDuplicates = "eliminateDuplicates"
configRadiusSimilarity = "radiusSimilarity"
)

// Default values
const (
defaultAPIUrl = "http://192.168.3.203:8000"
defaultTrackCount = 200
defaultArtistSimilarCount = 10
defaultEliminateDuplicates = true
defaultRadiusSimilarity = true
Expand Down Expand Up @@ -81,17 +80,16 @@ func (p *audioMusePlugin) GetSimilarSongsByTrack(input metadata.SimilarSongsByTr

// Read configuration
apiBaseURL := getConfigString(configAPIUrl, defaultAPIUrl)
trackCount := getConfigInt(configTrackCount, defaultTrackCount)
eliminateDuplicates := getConfigBool(configEliminateDuplicates, defaultEliminateDuplicates)
radiusSimilarity := getConfigBool(configRadiusSimilarity, defaultRadiusSimilarity)

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

// Build the API URL with query parameters
params := url.Values{}
params.Set("item_id", input.ID)
params.Set("n", strconv.Itoa(trackCount))
params.Set("n", strconv.Itoa(int(input.Count)))
params.Set("eliminate_duplicates", strconv.FormatBool(eliminateDuplicates))
params.Set("radius_similarity", strconv.FormatBool(radiusSimilarity))

Expand Down Expand Up @@ -151,37 +149,72 @@ func (p *audioMusePlugin) GetSimilarSongsByArtist(input metadata.SimilarSongsByA
return nil, err
}

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

// songSlices contains artist songs in alternating order: [baseArtist, relatedArtist1, baseArtist, relatedArtist2, ...]
songSlices := [][]metadata.SongRef{}

for _, a := range artists {
var artist1Songs, artist2Songs []metadata.SongRef

for _, cm := range a.ComponentMatches {
for _, s := range cm.Artist1RepresentativeSongs {

if s.ItemID == "" {
continue
}
if !seen[s.ItemID] {
seen[s.ItemID] = true
songIDs = append(songIDs, s.ItemID)
if seen[s.ItemID] {
continue
}

seen[s.ItemID] = true
artist1Songs = append(artist1Songs, metadata.SongRef{ID: s.ItemID, Name: s.Title})
}

for _, s := range cm.Artist2RepresentativeSongs {
if s.ItemID == "" {
continue
}
if !seen[s.ItemID] {
seen[s.ItemID] = true
songIDs = append(songIDs, s.ItemID)

if seen[s.ItemID] {
continue
}

seen[s.ItemID] = true
artist2Songs = append(artist2Songs, metadata.SongRef{ID: s.ItemID, Name: s.Title})
}
}

if len(artist1Songs) > 0 {
songSlices = append(songSlices, artist1Songs)
}
if len(artist2Songs) > 0 {
songSlices = append(songSlices, artist2Songs)
}
}

// Build SongRef list (names not always available)
songs := make([]metadata.SongRef, 0, len(songIDs))
for _, id := range songIDs {
songs = append(songs, metadata.SongRef{ID: id})
songs := make([]metadata.SongRef, 0, input.Count)

// get songs from our slices until we have enough or we ran out
artistID := 0
for len(songs) < int(input.Count) && len(songSlices) > 0 {
song := songSlices[artistID][0] // take a song
songs = append(songs, song)

songSlices[artistID] = songSlices[artistID][1:] // remove it from the pool

if len(songSlices[artistID]) == 0 {
// this slice has no more songs, remove it
songSlices = slices.Delete(songSlices, artistID, artistID+1)
if len(songSlices) == 0 {
break
}
} else {
// else, go to the next slice
artistID++
}

artistID = artistID % len(songSlices) // loop around if needed
}

pdk.Log(pdk.LogInfo, fmt.Sprintf("[AudioMuse] Returning %d artist-related songs to Navidrome", len(songs)))
Expand Down
18 changes: 4 additions & 14 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@
"description": "Base URL for the AudioMuse-AI server (e.g., http://192.168.3.203:8000).",
"default": "http://192.168.3.203:8000"
},
"trackCount": {
"type": "integer",
"title": "Number of Similar Tracks",
"description": "Maximum number of similar tracks to return. Default: 200",
"default": 200,
"minimum": 10,
"maximum": 500
},
"artistSimilarCount": {
"type": "integer",
"title": "Number of Similar Artists",
Expand Down Expand Up @@ -62,10 +54,6 @@
"type": "Group",
"label": "Song Instant Mix",
"elements": [
{
"type": "Control",
"scope": "#/properties/trackCount"
},
{
"type": "HorizontalLayout",
"elements": [
Expand Down Expand Up @@ -100,7 +88,9 @@
},
"http": {
"reason": "To call AudioMuse-AI API for similar tracks",
"requiredHosts": ["*"]
"requiredHosts": [
"*"
]
}
}
}
}