feat(plugins): add similar songs retrieval functions and improve duration consistency (#4933)
* feat: add duration filtering for similar songs matching Signed-off-by: Deluan <deluan@navidrome.org> * test: refactor expectations for similar songs in provider matching tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add functions to retrieve similar songs by track, album, and artist Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): support uint32 in ndpgen Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update duration field to use seconds as float instead of milliseconds as uint32 Signed-off-by: Deluan <deluan@navidrome.org> * fix: add helper functions for Rust's skip_serializing_if with numeric types Signed-off-by: Deluan <deluan@navidrome.org> * feat(provider): enhance track matching logic to fallback to title match when duration-filtered tracks fail --------- Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
+71
-23
@@ -14,14 +14,17 @@ const CapabilityMetadataAgent Capability = "MetadataAgent"
|
||||
|
||||
// Export function names (snake_case as per design)
|
||||
const (
|
||||
FuncGetArtistMBID = "nd_get_artist_mbid"
|
||||
FuncGetArtistURL = "nd_get_artist_url"
|
||||
FuncGetArtistBiography = "nd_get_artist_biography"
|
||||
FuncGetSimilarArtists = "nd_get_similar_artists"
|
||||
FuncGetArtistImages = "nd_get_artist_images"
|
||||
FuncGetArtistTopSongs = "nd_get_artist_top_songs"
|
||||
FuncGetAlbumInfo = "nd_get_album_info"
|
||||
FuncGetAlbumImages = "nd_get_album_images"
|
||||
FuncGetArtistMBID = "nd_get_artist_mbid"
|
||||
FuncGetArtistURL = "nd_get_artist_url"
|
||||
FuncGetArtistBiography = "nd_get_artist_biography"
|
||||
FuncGetSimilarArtists = "nd_get_similar_artists"
|
||||
FuncGetArtistImages = "nd_get_artist_images"
|
||||
FuncGetArtistTopSongs = "nd_get_artist_top_songs"
|
||||
FuncGetAlbumInfo = "nd_get_album_info"
|
||||
FuncGetAlbumImages = "nd_get_album_images"
|
||||
FuncGetSimilarSongsByTrack = "nd_get_similar_songs_by_track"
|
||||
FuncGetSimilarSongsByAlbum = "nd_get_similar_songs_by_album"
|
||||
FuncGetSimilarSongsByArtist = "nd_get_similar_songs_by_artist"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -35,6 +38,9 @@ func init() {
|
||||
FuncGetArtistTopSongs,
|
||||
FuncGetAlbumInfo,
|
||||
FuncGetAlbumImages,
|
||||
FuncGetSimilarSongsByTrack,
|
||||
FuncGetSimilarSongsByAlbum,
|
||||
FuncGetSimilarSongsByArtist,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -147,12 +153,7 @@ func (a *MetadataAgent) GetArtistTopSongs(ctx context.Context, id, artistName, m
|
||||
return nil, agents.ErrNotFound
|
||||
}
|
||||
|
||||
songs := make([]agents.Song, len(result.Songs))
|
||||
for i, s := range result.Songs {
|
||||
songs[i] = agents.Song{ID: s.ID, Name: s.Name, MBID: s.MBID}
|
||||
}
|
||||
|
||||
return songs, nil
|
||||
return songRefsToAgentSongs(result.Songs), nil
|
||||
}
|
||||
|
||||
// GetAlbumInfo retrieves album information
|
||||
@@ -195,15 +196,62 @@ func (a *MetadataAgent) GetAlbumImages(ctx context.Context, name, artist, mbid s
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func callSimilarSongsPluginFunction[T any](ctx context.Context, plugin *plugin, funcName string, input T) ([]agents.Song, error) {
|
||||
result, err := callPluginFunction[T, *capabilities.SimilarSongsResponse](ctx, plugin, funcName, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result == nil || len(result.Songs) == 0 {
|
||||
return nil, agents.ErrNotFound
|
||||
}
|
||||
return songRefsToAgentSongs(result.Songs), nil
|
||||
}
|
||||
|
||||
// GetSimilarSongsByTrack retrieves songs similar to a specific track
|
||||
func (a *MetadataAgent) GetSimilarSongsByTrack(ctx context.Context, id, name, artist, mbid string, count int) ([]agents.Song, error) {
|
||||
return callSimilarSongsPluginFunction[capabilities.SimilarSongsByTrackRequest](ctx, a.plugin, FuncGetSimilarSongsByTrack, capabilities.SimilarSongsByTrackRequest{ID: id, Name: name, Artist: artist, MBID: mbid, Count: int32(count)})
|
||||
}
|
||||
|
||||
// GetSimilarSongsByAlbum retrieves songs similar to tracks on an album
|
||||
func (a *MetadataAgent) GetSimilarSongsByAlbum(ctx context.Context, id, name, artist, mbid string, count int) ([]agents.Song, error) {
|
||||
return callSimilarSongsPluginFunction[capabilities.SimilarSongsByAlbumRequest](ctx, a.plugin, FuncGetSimilarSongsByAlbum, capabilities.SimilarSongsByAlbumRequest{ID: id, Name: name, Artist: artist, MBID: mbid, Count: int32(count)})
|
||||
}
|
||||
|
||||
// GetSimilarSongsByArtist retrieves songs similar to an artist's catalog
|
||||
func (a *MetadataAgent) GetSimilarSongsByArtist(ctx context.Context, id, name, mbid string, count int) ([]agents.Song, error) {
|
||||
return callSimilarSongsPluginFunction[capabilities.SimilarSongsByArtistRequest](ctx, a.plugin, FuncGetSimilarSongsByArtist, capabilities.SimilarSongsByArtistRequest{ID: id, Name: name, MBID: mbid, Count: int32(count)})
|
||||
}
|
||||
|
||||
// songRefsToAgentSongs converts a slice of SongRef to agents.Song
|
||||
func songRefsToAgentSongs(refs []capabilities.SongRef) []agents.Song {
|
||||
songs := make([]agents.Song, len(refs))
|
||||
for i, s := range refs {
|
||||
songs[i] = agents.Song{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
MBID: s.MBID,
|
||||
Artist: s.Artist,
|
||||
ArtistMBID: s.ArtistMBID,
|
||||
Album: s.Album,
|
||||
AlbumMBID: s.AlbumMBID,
|
||||
Duration: uint32(s.Duration * 1000),
|
||||
}
|
||||
}
|
||||
return songs
|
||||
}
|
||||
|
||||
// Verify interface implementations at compile time
|
||||
var (
|
||||
_ agents.Interface = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistMBIDRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistURLRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistBiographyRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistSimilarRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistImageRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistTopSongsRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.AlbumInfoRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.AlbumImageRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.Interface = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistMBIDRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistURLRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistBiographyRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistSimilarRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistImageRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.ArtistTopSongsRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.AlbumInfoRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.AlbumImageRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.SimilarSongsByTrackRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.SimilarSongsByAlbumRetriever = (*MetadataAgent)(nil)
|
||||
_ agents.SimilarSongsByArtistRetriever = (*MetadataAgent)(nil)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user