mirror of
https://github.com/navidrome/navidrome.git
synced 2025-07-14 07:31:28 +03:00
* Add prometheus metrics to subsonic and plugins * address feedback, do not log error if operation is not supported * add missing timestamp and client to stats * remove .view from subsonic route * directly inject DataStore in Prometheus, to avoid having to pass it in every call Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org> Co-authored-by: Deluan <deluan@navidrome.org>
167 lines
5.6 KiB
Go
167 lines
5.6 KiB
Go
package plugins
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/navidrome/navidrome/core/agents"
|
|
"github.com/navidrome/navidrome/log"
|
|
"github.com/navidrome/navidrome/plugins/api"
|
|
"github.com/tetratelabs/wazero"
|
|
)
|
|
|
|
// NewWasmMediaAgent creates a new adapter for a MetadataAgent plugin
|
|
func newWasmMediaAgent(wasmPath, pluginID string, m *Manager, runtime api.WazeroNewRuntime, mc wazero.ModuleConfig) WasmPlugin {
|
|
loader, err := api.NewMetadataAgentPlugin(context.Background(), api.WazeroRuntime(runtime), api.WazeroModuleConfig(mc))
|
|
if err != nil {
|
|
log.Error("Error creating media metadata service plugin", "plugin", pluginID, "path", wasmPath, err)
|
|
return nil
|
|
}
|
|
return &wasmMediaAgent{
|
|
wasmBasePlugin: newWasmBasePlugin[api.MetadataAgent, *api.MetadataAgentPlugin](
|
|
wasmPath,
|
|
pluginID,
|
|
CapabilityMetadataAgent,
|
|
m.metrics,
|
|
loader,
|
|
func(ctx context.Context, l *api.MetadataAgentPlugin, path string) (api.MetadataAgent, error) {
|
|
return l.Load(ctx, path)
|
|
},
|
|
),
|
|
}
|
|
}
|
|
|
|
// wasmMediaAgent adapts a MetadataAgent plugin to implement the agents.Interface
|
|
type wasmMediaAgent struct {
|
|
*wasmBasePlugin[api.MetadataAgent, *api.MetadataAgentPlugin]
|
|
}
|
|
|
|
func (w *wasmMediaAgent) AgentName() string {
|
|
return w.id
|
|
}
|
|
|
|
func (w *wasmMediaAgent) mapError(err error) error {
|
|
if err != nil && (err.Error() == api.ErrNotFound.Error() || err.Error() == api.ErrNotImplemented.Error()) {
|
|
return agents.ErrNotFound
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Album-related methods
|
|
|
|
func (w *wasmMediaAgent) GetAlbumInfo(ctx context.Context, name, artist, mbid string) (*agents.AlbumInfo, error) {
|
|
return callMethod(ctx, w, "GetAlbumInfo", func(inst api.MetadataAgent) (*agents.AlbumInfo, error) {
|
|
res, err := inst.GetAlbumInfo(ctx, &api.AlbumInfoRequest{Name: name, Artist: artist, Mbid: mbid})
|
|
if err != nil {
|
|
return nil, w.mapError(err)
|
|
}
|
|
if res == nil || res.Info == nil {
|
|
return nil, agents.ErrNotFound
|
|
}
|
|
info := res.Info
|
|
return &agents.AlbumInfo{
|
|
Name: info.Name,
|
|
MBID: info.Mbid,
|
|
Description: info.Description,
|
|
URL: info.Url,
|
|
}, nil
|
|
})
|
|
}
|
|
|
|
func (w *wasmMediaAgent) GetAlbumImages(ctx context.Context, name, artist, mbid string) ([]agents.ExternalImage, error) {
|
|
return callMethod(ctx, w, "GetAlbumImages", func(inst api.MetadataAgent) ([]agents.ExternalImage, error) {
|
|
res, err := inst.GetAlbumImages(ctx, &api.AlbumImagesRequest{Name: name, Artist: artist, Mbid: mbid})
|
|
if err != nil {
|
|
return nil, w.mapError(err)
|
|
}
|
|
return convertExternalImages(res.Images), nil
|
|
})
|
|
}
|
|
|
|
// Artist-related methods
|
|
|
|
func (w *wasmMediaAgent) GetArtistMBID(ctx context.Context, id string, name string) (string, error) {
|
|
return callMethod(ctx, w, "GetArtistMBID", func(inst api.MetadataAgent) (string, error) {
|
|
res, err := inst.GetArtistMBID(ctx, &api.ArtistMBIDRequest{Id: id, Name: name})
|
|
if err != nil {
|
|
return "", w.mapError(err)
|
|
}
|
|
return res.GetMbid(), nil
|
|
})
|
|
}
|
|
|
|
func (w *wasmMediaAgent) GetArtistURL(ctx context.Context, id, name, mbid string) (string, error) {
|
|
return callMethod(ctx, w, "GetArtistURL", func(inst api.MetadataAgent) (string, error) {
|
|
res, err := inst.GetArtistURL(ctx, &api.ArtistURLRequest{Id: id, Name: name, Mbid: mbid})
|
|
if err != nil {
|
|
return "", w.mapError(err)
|
|
}
|
|
return res.GetUrl(), nil
|
|
})
|
|
}
|
|
|
|
func (w *wasmMediaAgent) GetArtistBiography(ctx context.Context, id, name, mbid string) (string, error) {
|
|
return callMethod(ctx, w, "GetArtistBiography", func(inst api.MetadataAgent) (string, error) {
|
|
res, err := inst.GetArtistBiography(ctx, &api.ArtistBiographyRequest{Id: id, Name: name, Mbid: mbid})
|
|
if err != nil {
|
|
return "", w.mapError(err)
|
|
}
|
|
return res.GetBiography(), nil
|
|
})
|
|
}
|
|
|
|
func (w *wasmMediaAgent) GetSimilarArtists(ctx context.Context, id, name, mbid string, limit int) ([]agents.Artist, error) {
|
|
return callMethod(ctx, w, "GetSimilarArtists", func(inst api.MetadataAgent) ([]agents.Artist, error) {
|
|
resp, err := inst.GetSimilarArtists(ctx, &api.ArtistSimilarRequest{Id: id, Name: name, Mbid: mbid, Limit: int32(limit)})
|
|
if err != nil {
|
|
return nil, w.mapError(err)
|
|
}
|
|
artists := make([]agents.Artist, 0, len(resp.GetArtists()))
|
|
for _, a := range resp.GetArtists() {
|
|
artists = append(artists, agents.Artist{
|
|
Name: a.GetName(),
|
|
MBID: a.GetMbid(),
|
|
})
|
|
}
|
|
return artists, nil
|
|
})
|
|
}
|
|
|
|
func (w *wasmMediaAgent) GetArtistImages(ctx context.Context, id, name, mbid string) ([]agents.ExternalImage, error) {
|
|
return callMethod(ctx, w, "GetArtistImages", func(inst api.MetadataAgent) ([]agents.ExternalImage, error) {
|
|
res, err := inst.GetArtistImages(ctx, &api.ArtistImageRequest{Id: id, Name: name, Mbid: mbid})
|
|
if err != nil {
|
|
return nil, w.mapError(err)
|
|
}
|
|
return convertExternalImages(res.Images), nil
|
|
})
|
|
}
|
|
|
|
func (w *wasmMediaAgent) GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]agents.Song, error) {
|
|
return callMethod(ctx, w, "GetArtistTopSongs", func(inst api.MetadataAgent) ([]agents.Song, error) {
|
|
resp, err := inst.GetArtistTopSongs(ctx, &api.ArtistTopSongsRequest{Id: id, ArtistName: artistName, Mbid: mbid, Count: int32(count)})
|
|
if err != nil {
|
|
return nil, w.mapError(err)
|
|
}
|
|
songs := make([]agents.Song, 0, len(resp.GetSongs()))
|
|
for _, s := range resp.GetSongs() {
|
|
songs = append(songs, agents.Song{
|
|
Name: s.GetName(),
|
|
MBID: s.GetMbid(),
|
|
})
|
|
}
|
|
return songs, nil
|
|
})
|
|
}
|
|
|
|
// Helper function to convert ExternalImage objects from the API to the agents package
|
|
func convertExternalImages(images []*api.ExternalImage) []agents.ExternalImage {
|
|
result := make([]agents.ExternalImage, 0, len(images))
|
|
for _, img := range images {
|
|
result = append(result, agents.ExternalImage{
|
|
URL: img.GetUrl(),
|
|
Size: int(img.GetSize()),
|
|
})
|
|
}
|
|
return result
|
|
}
|