mirror of
https://github.com/navidrome/navidrome.git
synced 2026-02-09 14:31:11 -05:00
* feat: add ISRC support to similar songs matching and plugin interface Add ISRC (International Standard Recording Code) as a high-priority identifier in the provider matching algorithm, alongside MBID. The matching pipeline now uses four strategies in priority order: ID > MBID > ISRC > Title+Artist fuzzy match. - Add ISRC field to agents.Song struct - Add ISRC field to plugin capability SongRef (Go, Rust PDKs) - Add loadTracksByISRC using json_tree query on tags column - Integrate ISRC into matchSongsToLibrary, selectBestMatchingSongs, and buildTitleQueries https://claude.ai/code/session_01Dd4mTq1VQZag4RNjCVusiF * chore: regenerate plugin schema after ISRC addition Run `make gen` to update the generated YAML schema for the metadata agent capability with the new ISRC field on SongRef. https://claude.ai/code/session_01Dd4mTq1VQZag4RNjCVusiF * feat(mediafile): add GetAllByTags method to MediaFileRepository for tag-based retrieval Signed-off-by: Deluan <deluan@navidrome.org> * feat(provider): speed up track matching by incorporating prior matches in ISRC and MBID lookups Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org> Co-authored-by: Claude <noreply@anthropic.com>
185 lines
5.7 KiB
Go
185 lines
5.7 KiB
Go
// Test plugin for Navidrome plugin system integration tests.
|
|
// Build with: tinygo build -o ../test-metadata-agent.wasm -target wasip1 -buildmode=c-shared .
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
|
|
"github.com/navidrome/navidrome/plugins/pdk/go/metadata"
|
|
"github.com/navidrome/navidrome/plugins/pdk/go/pdk"
|
|
)
|
|
|
|
func init() {
|
|
metadata.Register(&testMetadataAgent{})
|
|
}
|
|
|
|
type testMetadataAgent struct{}
|
|
|
|
// checkConfigError checks if the plugin is configured to return an error.
|
|
// If "error" config is set, it returns an error with that message.
|
|
func checkConfigError() error {
|
|
errMsg, hasErr := pdk.GetConfig("error")
|
|
if !hasErr || errMsg == "" {
|
|
return nil
|
|
}
|
|
return errors.New(errMsg)
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetArtistMBID(input metadata.ArtistMBIDRequest) (*metadata.ArtistMBIDResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &metadata.ArtistMBIDResponse{MBID: "test-mbid-" + input.Name}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetArtistURL(input metadata.ArtistRequest) (*metadata.ArtistURLResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &metadata.ArtistURLResponse{URL: "https://test.example.com/artist/" + input.Name}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetArtistBiography(input metadata.ArtistRequest) (*metadata.ArtistBiographyResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &metadata.ArtistBiographyResponse{Biography: "Biography for " + input.Name}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetArtistImages(input metadata.ArtistRequest) (*metadata.ArtistImagesResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &metadata.ArtistImagesResponse{
|
|
Images: []metadata.ImageInfo{
|
|
{URL: "https://test.example.com/images/" + input.Name + "/large.jpg", Size: 500},
|
|
{URL: "https://test.example.com/images/" + input.Name + "/small.jpg", Size: 100},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetSimilarArtists(input metadata.SimilarArtistsRequest) (*metadata.SimilarArtistsResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
limit := int(input.Limit)
|
|
if limit == 0 {
|
|
limit = 5
|
|
}
|
|
artists := make([]metadata.ArtistRef, 0, limit)
|
|
for i := range limit {
|
|
artists = append(artists, metadata.ArtistRef{
|
|
ID: "similar-artist-id-" + strconv.Itoa(i+1),
|
|
Name: input.Name + " Similar " + string(rune('A'+i)),
|
|
MBID: "similar-mbid-" + strconv.Itoa(i+1),
|
|
})
|
|
}
|
|
return &metadata.SimilarArtistsResponse{Artists: artists}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetArtistTopSongs(input metadata.TopSongsRequest) (*metadata.TopSongsResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
count := int(input.Count)
|
|
if count == 0 {
|
|
count = 5
|
|
}
|
|
songs := make([]metadata.SongRef, 0, count)
|
|
for i := range count {
|
|
songs = append(songs, metadata.SongRef{
|
|
ID: "song-id-" + strconv.Itoa(i+1),
|
|
Name: input.Name + " Song " + strconv.Itoa(i+1),
|
|
MBID: "song-mbid-" + strconv.Itoa(i+1),
|
|
})
|
|
}
|
|
return &metadata.TopSongsResponse{Songs: songs}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetAlbumInfo(input metadata.AlbumRequest) (*metadata.AlbumInfoResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &metadata.AlbumInfoResponse{
|
|
Name: input.Name,
|
|
MBID: "test-album-mbid-" + input.Name,
|
|
Description: "Description for " + input.Name + " by " + input.Artist,
|
|
URL: "https://test.example.com/album/" + input.Name,
|
|
}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetAlbumImages(input metadata.AlbumRequest) (*metadata.AlbumImagesResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &metadata.AlbumImagesResponse{
|
|
Images: []metadata.ImageInfo{
|
|
{URL: "https://test.example.com/albums/" + input.Name + "/cover.jpg", Size: 500},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetSimilarSongsByTrack(input metadata.SimilarSongsByTrackRequest) (*metadata.SimilarSongsResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
count := int(input.Count)
|
|
if count == 0 {
|
|
count = 5
|
|
}
|
|
songs := make([]metadata.SongRef, 0, count)
|
|
for i := range count {
|
|
songs = append(songs, metadata.SongRef{
|
|
ID: "similar-track-id-" + strconv.Itoa(i+1),
|
|
Name: "Similar to " + input.Name + " #" + strconv.Itoa(i+1),
|
|
MBID: "similar-mbid-" + strconv.Itoa(i+1),
|
|
ISRC: "similar-isrc-" + strconv.Itoa(i+1),
|
|
Artist: input.Artist,
|
|
ArtistMBID: "artist-mbid-" + strconv.Itoa(i+1),
|
|
})
|
|
}
|
|
return &metadata.SimilarSongsResponse{Songs: songs}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetSimilarSongsByAlbum(input metadata.SimilarSongsByAlbumRequest) (*metadata.SimilarSongsResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
count := int(input.Count)
|
|
if count == 0 {
|
|
count = 5
|
|
}
|
|
songs := make([]metadata.SongRef, 0, count)
|
|
for i := range count {
|
|
songs = append(songs, metadata.SongRef{
|
|
ID: "album-similar-id-" + strconv.Itoa(i+1),
|
|
Name: "Album Similar #" + strconv.Itoa(i+1),
|
|
Artist: input.Artist,
|
|
Album: input.Name,
|
|
})
|
|
}
|
|
return &metadata.SimilarSongsResponse{Songs: songs}, nil
|
|
}
|
|
|
|
func (t *testMetadataAgent) GetSimilarSongsByArtist(input metadata.SimilarSongsByArtistRequest) (*metadata.SimilarSongsResponse, error) {
|
|
if err := checkConfigError(); err != nil {
|
|
return nil, err
|
|
}
|
|
count := int(input.Count)
|
|
if count == 0 {
|
|
count = 5
|
|
}
|
|
songs := make([]metadata.SongRef, 0, count)
|
|
for i := range count {
|
|
songs = append(songs, metadata.SongRef{
|
|
ID: "artist-similar-id-" + strconv.Itoa(i+1),
|
|
Name: input.Name + " Style Song #" + strconv.Itoa(i+1),
|
|
Artist: input.Name + " Similar Artist",
|
|
})
|
|
}
|
|
return &metadata.SimilarSongsResponse{Songs: songs}, nil
|
|
}
|
|
|
|
func main() {}
|