fix(plugins): update return types in metadata interfaces to use pointers

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2025-12-31 10:32:52 -05:00
parent d6b412acde
commit e6b0af63ce
6 changed files with 77 additions and 71 deletions

View File

@ -191,11 +191,11 @@ Agents = "lastfm,spotify,my-plugin"
Integrates with external scrobbling services. Export one or more of these functions:
| Function | Input | Output | Description |
|------------------------------|-----------------------|-------------------------|-----------------------------|
| `nd_scrobbler_is_authorized` | `{userId, username}` | `{authorized}` | Check if user is authorized |
| `nd_scrobbler_now_playing` | See below | `{error?, errorType?}` | Send now playing |
| `nd_scrobbler_scrobble` | See below | `{error?, errorType?}` | Submit a scrobble |
| Function | Input | Output | Description |
|------------------------------|-----------------------|----------------|-----------------------------||
| `nd_scrobbler_is_authorized` | `{userId, username}` | `{authorized}` | Check if user is authorized |
| `nd_scrobbler_now_playing` | See below | (none) | Send now playing |
| `nd_scrobbler_scrobble` | See below | (none) | Submit a scrobble |
**NowPlaying/Scrobble Input:**
@ -220,21 +220,23 @@ Integrates with external scrobbling services. Export one or more of these functi
}
```
**Error Output (on failure):**
**Error Handling:**
```json
{
"error": "error message",
"errorType": "notAuthorized|retryLater|unrecoverable"
}
On success, return `0`. On failure, use `pdk.SetError()` with one of these error types:
- `scrobbler(not_authorized)` User needs to re-authorize
- `scrobbler(retry_later)` Temporary failure, Navidrome will retry
- `scrobbler(unrecoverable)` Permanent failure, scrobble discarded
```go
import "github.com/navidrome/navidrome/plugins/pdk/go/scrobbler"
// Return error using predefined constants
return scrobbler.ScrobblerErrorNotAuthorized
return scrobbler.ScrobblerErrorRetryLater
return scrobbler.ScrobblerErrorUnrecoverable
```
- `notAuthorized` User needs to re-authorize
- `retryLater` Temporary failure, Navidrome will retry
- `unrecoverable` Permanent failure, scrobble discarded
On success, return empty JSON `{}` or omit output entirely.
### Lifecycle
Optional initialization callback. Export this function to run code when your plugin loads:
@ -718,8 +720,8 @@ import (
type myPlugin struct{}
func (p *myPlugin) GetArtistBiography(input metadata.ArtistBiographyInput) metadata.ArtistBiographyOutput {
return metadata.ArtistBiographyOutput{Biography: "Biography text..."}
func (p *myPlugin) GetArtistBiography(input metadata.ArtistRequest) (*metadata.ArtistBiographyResponse, error) {
return &metadata.ArtistBiographyResponse{Biography: "Biography text..."}, nil
}
func init() {

View File

@ -10,7 +10,7 @@
// //nd:capability name=metadata
// type MetadataAgent interface {
// //nd:export name=nd_get_artist_biography
// GetArtistBiography(ArtistInput) (ArtistBiographyOutput, error)
// GetArtistBiography(ArtistRequest) (*ArtistBiographyResponse, error)
// }
//
// Annotation Reference:
@ -35,7 +35,7 @@
//
// // Optional provider interfaces
// type ArtistBiographyProvider interface {
// GetArtistBiography(ArtistInput) (ArtistBiographyOutput, error)
// GetArtistBiography(ArtistRequest) (*ArtistBiographyResponse, error)
// }
//
// // Registration function
@ -47,9 +47,9 @@
//
// // Scrobbler requires all methods
// type Scrobbler interface {
// IsAuthorized(AuthInput) (AuthOutput, error)
// NowPlaying(NowPlayingInput) (ScrobblerOutput, error)
// Scrobble(ScrobbleInput) (ScrobblerOutput, error)
// IsAuthorized(IsAuthorizedRequest) (*IsAuthorizedResponse, error)
// NowPlaying(NowPlayingRequest) error
// Scrobble(ScrobbleRequest) error
// }
//
// func Register(impl Scrobbler) { ... }

View File

@ -11,35 +11,35 @@ package capabilities
type MetadataAgent interface {
// GetArtistMBID retrieves the MusicBrainz ID for an artist.
//nd:export name=nd_get_artist_mbid
GetArtistMBID(ArtistMBIDRequest) (ArtistMBIDResponse, error)
GetArtistMBID(ArtistMBIDRequest) (*ArtistMBIDResponse, error)
// GetArtistURL retrieves the external URL for an artist.
//nd:export name=nd_get_artist_url
GetArtistURL(ArtistRequest) (ArtistURLResponse, error)
GetArtistURL(ArtistRequest) (*ArtistURLResponse, error)
// GetArtistBiography retrieves the biography for an artist.
//nd:export name=nd_get_artist_biography
GetArtistBiography(ArtistRequest) (ArtistBiographyResponse, error)
GetArtistBiography(ArtistRequest) (*ArtistBiographyResponse, error)
// GetSimilarArtists retrieves similar artists for a given artist.
//nd:export name=nd_get_similar_artists
GetSimilarArtists(SimilarArtistsRequest) (SimilarArtistsResponse, error)
GetSimilarArtists(SimilarArtistsRequest) (*SimilarArtistsResponse, error)
// GetArtistImages retrieves images for an artist.
//nd:export name=nd_get_artist_images
GetArtistImages(ArtistRequest) (ArtistImagesResponse, error)
GetArtistImages(ArtistRequest) (*ArtistImagesResponse, error)
// GetArtistTopSongs retrieves top songs for an artist.
//nd:export name=nd_get_artist_top_songs
GetArtistTopSongs(TopSongsRequest) (TopSongsResponse, error)
GetArtistTopSongs(TopSongsRequest) (*TopSongsResponse, error)
// GetAlbumInfo retrieves album information.
//nd:export name=nd_get_album_info
GetAlbumInfo(AlbumRequest) (AlbumInfoResponse, error)
GetAlbumInfo(AlbumRequest) (*AlbumInfoResponse, error)
// GetAlbumImages retrieves images for an album.
//nd:export name=nd_get_album_images
GetAlbumImages(AlbumRequest) (AlbumImagesResponse, error)
GetAlbumImages(AlbumRequest) (*AlbumImagesResponse, error)
}
// ArtistMBIDRequest is the request for GetArtistMBID.

View File

@ -55,12 +55,12 @@ func (a *MetadataAgent) AgentName() string {
// GetArtistMBID retrieves the MusicBrainz ID for an artist
func (a *MetadataAgent) GetArtistMBID(ctx context.Context, id string, name string) (string, error) {
input := capabilities.ArtistMBIDRequest{ID: id, Name: name}
result, err := callPluginFunction[capabilities.ArtistMBIDRequest, capabilities.ArtistMBIDResponse](ctx, a.plugin, FuncGetArtistMBID, input)
result, err := callPluginFunction[capabilities.ArtistMBIDRequest, *capabilities.ArtistMBIDResponse](ctx, a.plugin, FuncGetArtistMBID, input)
if err != nil {
return "", errors.Join(agents.ErrNotFound, err)
}
if result.MBID == "" {
if result == nil || result.MBID == "" {
return "", agents.ErrNotFound
}
@ -70,11 +70,11 @@ func (a *MetadataAgent) GetArtistMBID(ctx context.Context, id string, name strin
// GetArtistURL retrieves the external URL for an artist
func (a *MetadataAgent) GetArtistURL(ctx context.Context, id, name, mbid string) (string, error) {
input := capabilities.ArtistRequest{ID: id, Name: name, MBID: mbid}
result, err := callPluginFunction[capabilities.ArtistRequest, capabilities.ArtistURLResponse](ctx, a.plugin, FuncGetArtistURL, input)
result, err := callPluginFunction[capabilities.ArtistRequest, *capabilities.ArtistURLResponse](ctx, a.plugin, FuncGetArtistURL, input)
if err != nil {
return "", errors.Join(agents.ErrNotFound, err)
}
if result.URL == "" {
if result == nil || result.URL == "" {
return "", agents.ErrNotFound
}
return result.URL, nil
@ -83,12 +83,12 @@ func (a *MetadataAgent) GetArtistURL(ctx context.Context, id, name, mbid string)
// GetArtistBiography retrieves the biography for an artist
func (a *MetadataAgent) GetArtistBiography(ctx context.Context, id, name, mbid string) (string, error) {
input := capabilities.ArtistRequest{ID: id, Name: name, MBID: mbid}
result, err := callPluginFunction[capabilities.ArtistRequest, capabilities.ArtistBiographyResponse](ctx, a.plugin, FuncGetArtistBiography, input)
result, err := callPluginFunction[capabilities.ArtistRequest, *capabilities.ArtistBiographyResponse](ctx, a.plugin, FuncGetArtistBiography, input)
if err != nil {
return "", errors.Join(agents.ErrNotFound, err)
}
if result.Biography == "" {
if result == nil || result.Biography == "" {
return "", agents.ErrNotFound
}
@ -98,12 +98,12 @@ func (a *MetadataAgent) GetArtistBiography(ctx context.Context, id, name, mbid s
// GetSimilarArtists retrieves similar artists
func (a *MetadataAgent) GetSimilarArtists(ctx context.Context, id, name, mbid string, limit int) ([]agents.Artist, error) {
input := capabilities.SimilarArtistsRequest{ID: id, Name: name, MBID: mbid, Limit: int32(limit)}
result, err := callPluginFunction[capabilities.SimilarArtistsRequest, capabilities.SimilarArtistsResponse](ctx, a.plugin, FuncGetSimilarArtists, input)
result, err := callPluginFunction[capabilities.SimilarArtistsRequest, *capabilities.SimilarArtistsResponse](ctx, a.plugin, FuncGetSimilarArtists, input)
if err != nil {
return nil, errors.Join(agents.ErrNotFound, err)
}
if len(result.Artists) == 0 {
if result == nil || len(result.Artists) == 0 {
return nil, agents.ErrNotFound
}
@ -118,12 +118,12 @@ func (a *MetadataAgent) GetSimilarArtists(ctx context.Context, id, name, mbid st
// GetArtistImages retrieves images for an artist
func (a *MetadataAgent) GetArtistImages(ctx context.Context, id, name, mbid string) ([]agents.ExternalImage, error) {
input := capabilities.ArtistRequest{ID: id, Name: name, MBID: mbid}
result, err := callPluginFunction[capabilities.ArtistRequest, capabilities.ArtistImagesResponse](ctx, a.plugin, FuncGetArtistImages, input)
result, err := callPluginFunction[capabilities.ArtistRequest, *capabilities.ArtistImagesResponse](ctx, a.plugin, FuncGetArtistImages, input)
if err != nil {
return nil, errors.Join(agents.ErrNotFound, err)
}
if len(result.Images) == 0 {
if result == nil || len(result.Images) == 0 {
return nil, agents.ErrNotFound
}
@ -138,12 +138,12 @@ func (a *MetadataAgent) GetArtistImages(ctx context.Context, id, name, mbid stri
// GetArtistTopSongs retrieves top songs for an artist
func (a *MetadataAgent) GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]agents.Song, error) {
input := capabilities.TopSongsRequest{ID: id, Name: artistName, MBID: mbid, Count: int32(count)}
result, err := callPluginFunction[capabilities.TopSongsRequest, capabilities.TopSongsResponse](ctx, a.plugin, FuncGetArtistTopSongs, input)
result, err := callPluginFunction[capabilities.TopSongsRequest, *capabilities.TopSongsResponse](ctx, a.plugin, FuncGetArtistTopSongs, input)
if err != nil {
return nil, errors.Join(agents.ErrNotFound, err)
}
if len(result.Songs) == 0 {
if result == nil || len(result.Songs) == 0 {
return nil, agents.ErrNotFound
}
@ -158,11 +158,15 @@ func (a *MetadataAgent) GetArtistTopSongs(ctx context.Context, id, artistName, m
// GetAlbumInfo retrieves album information
func (a *MetadataAgent) GetAlbumInfo(ctx context.Context, name, artist, mbid string) (*agents.AlbumInfo, error) {
input := capabilities.AlbumRequest{Name: name, Artist: artist, MBID: mbid}
result, err := callPluginFunction[capabilities.AlbumRequest, capabilities.AlbumInfoResponse](ctx, a.plugin, FuncGetAlbumInfo, input)
result, err := callPluginFunction[capabilities.AlbumRequest, *capabilities.AlbumInfoResponse](ctx, a.plugin, FuncGetAlbumInfo, input)
if err != nil {
return nil, errors.Join(agents.ErrNotFound, err)
}
if result == nil {
return nil, agents.ErrNotFound
}
return &agents.AlbumInfo{
Name: result.Name,
MBID: result.MBID,
@ -174,12 +178,12 @@ func (a *MetadataAgent) GetAlbumInfo(ctx context.Context, name, artist, mbid str
// GetAlbumImages retrieves images for an album
func (a *MetadataAgent) GetAlbumImages(ctx context.Context, name, artist, mbid string) ([]agents.ExternalImage, error) {
input := capabilities.AlbumRequest{Name: name, Artist: artist, MBID: mbid}
result, err := callPluginFunction[capabilities.AlbumRequest, capabilities.AlbumImagesResponse](ctx, a.plugin, FuncGetAlbumImages, input)
result, err := callPluginFunction[capabilities.AlbumRequest, *capabilities.AlbumImagesResponse](ctx, a.plugin, FuncGetAlbumImages, input)
if err != nil {
return nil, errors.Join(agents.ErrNotFound, err)
}
if len(result.Images) == 0 {
if result == nil || len(result.Images) == 0 {
return nil, agents.ErrNotFound
}

View File

@ -153,52 +153,52 @@ type Metadata interface{}
// ArtistMBIDProvider provides the GetArtistMBID function.
type ArtistMBIDProvider interface {
GetArtistMBID(ArtistMBIDRequest) (ArtistMBIDResponse, error)
GetArtistMBID(ArtistMBIDRequest) (*ArtistMBIDResponse, error)
}
// ArtistURLProvider provides the GetArtistURL function.
type ArtistURLProvider interface {
GetArtistURL(ArtistRequest) (ArtistURLResponse, error)
GetArtistURL(ArtistRequest) (*ArtistURLResponse, error)
}
// ArtistBiographyProvider provides the GetArtistBiography function.
type ArtistBiographyProvider interface {
GetArtistBiography(ArtistRequest) (ArtistBiographyResponse, error)
GetArtistBiography(ArtistRequest) (*ArtistBiographyResponse, error)
}
// SimilarArtistsProvider provides the GetSimilarArtists function.
type SimilarArtistsProvider interface {
GetSimilarArtists(SimilarArtistsRequest) (SimilarArtistsResponse, error)
GetSimilarArtists(SimilarArtistsRequest) (*SimilarArtistsResponse, error)
}
// ArtistImagesProvider provides the GetArtistImages function.
type ArtistImagesProvider interface {
GetArtistImages(ArtistRequest) (ArtistImagesResponse, error)
GetArtistImages(ArtistRequest) (*ArtistImagesResponse, error)
}
// ArtistTopSongsProvider provides the GetArtistTopSongs function.
type ArtistTopSongsProvider interface {
GetArtistTopSongs(TopSongsRequest) (TopSongsResponse, error)
GetArtistTopSongs(TopSongsRequest) (*TopSongsResponse, error)
}
// AlbumInfoProvider provides the GetAlbumInfo function.
type AlbumInfoProvider interface {
GetAlbumInfo(AlbumRequest) (AlbumInfoResponse, error)
GetAlbumInfo(AlbumRequest) (*AlbumInfoResponse, error)
}
// AlbumImagesProvider provides the GetAlbumImages function.
type AlbumImagesProvider interface {
GetAlbumImages(AlbumRequest) (AlbumImagesResponse, error)
GetAlbumImages(AlbumRequest) (*AlbumImagesResponse, error)
} // Internal implementation holders
var (
artistMBIDImpl func(ArtistMBIDRequest) (ArtistMBIDResponse, error)
artistURLImpl func(ArtistRequest) (ArtistURLResponse, error)
artistBiographyImpl func(ArtistRequest) (ArtistBiographyResponse, error)
similarArtistsImpl func(SimilarArtistsRequest) (SimilarArtistsResponse, error)
artistImagesImpl func(ArtistRequest) (ArtistImagesResponse, error)
artistTopSongsImpl func(TopSongsRequest) (TopSongsResponse, error)
albumInfoImpl func(AlbumRequest) (AlbumInfoResponse, error)
albumImagesImpl func(AlbumRequest) (AlbumImagesResponse, error)
artistMBIDImpl func(ArtistMBIDRequest) (*ArtistMBIDResponse, error)
artistURLImpl func(ArtistRequest) (*ArtistURLResponse, error)
artistBiographyImpl func(ArtistRequest) (*ArtistBiographyResponse, error)
similarArtistsImpl func(SimilarArtistsRequest) (*SimilarArtistsResponse, error)
artistImagesImpl func(ArtistRequest) (*ArtistImagesResponse, error)
artistTopSongsImpl func(TopSongsRequest) (*TopSongsResponse, error)
albumInfoImpl func(AlbumRequest) (*AlbumInfoResponse, error)
albumImagesImpl func(AlbumRequest) (*AlbumImagesResponse, error)
)
// Register registers a metadata implementation.

View File

@ -150,42 +150,42 @@ type Metadata interface{}
// ArtistMBIDProvider provides the GetArtistMBID function.
type ArtistMBIDProvider interface {
GetArtistMBID(ArtistMBIDRequest) (ArtistMBIDResponse, error)
GetArtistMBID(ArtistMBIDRequest) (*ArtistMBIDResponse, error)
}
// ArtistURLProvider provides the GetArtistURL function.
type ArtistURLProvider interface {
GetArtistURL(ArtistRequest) (ArtistURLResponse, error)
GetArtistURL(ArtistRequest) (*ArtistURLResponse, error)
}
// ArtistBiographyProvider provides the GetArtistBiography function.
type ArtistBiographyProvider interface {
GetArtistBiography(ArtistRequest) (ArtistBiographyResponse, error)
GetArtistBiography(ArtistRequest) (*ArtistBiographyResponse, error)
}
// SimilarArtistsProvider provides the GetSimilarArtists function.
type SimilarArtistsProvider interface {
GetSimilarArtists(SimilarArtistsRequest) (SimilarArtistsResponse, error)
GetSimilarArtists(SimilarArtistsRequest) (*SimilarArtistsResponse, error)
}
// ArtistImagesProvider provides the GetArtistImages function.
type ArtistImagesProvider interface {
GetArtistImages(ArtistRequest) (ArtistImagesResponse, error)
GetArtistImages(ArtistRequest) (*ArtistImagesResponse, error)
}
// ArtistTopSongsProvider provides the GetArtistTopSongs function.
type ArtistTopSongsProvider interface {
GetArtistTopSongs(TopSongsRequest) (TopSongsResponse, error)
GetArtistTopSongs(TopSongsRequest) (*TopSongsResponse, error)
}
// AlbumInfoProvider provides the GetAlbumInfo function.
type AlbumInfoProvider interface {
GetAlbumInfo(AlbumRequest) (AlbumInfoResponse, error)
GetAlbumInfo(AlbumRequest) (*AlbumInfoResponse, error)
}
// AlbumImagesProvider provides the GetAlbumImages function.
type AlbumImagesProvider interface {
GetAlbumImages(AlbumRequest) (AlbumImagesResponse, error)
GetAlbumImages(AlbumRequest) (*AlbumImagesResponse, error)
}
// NotImplementedCode is the standard return code for unimplemented functions.