mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-31 07:30:32 +00:00
refactor(plugins): consistent naming/types across PDK
Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
parent
547363eab7
commit
301b2c850c
@ -53,4 +53,7 @@
|
||||
// }
|
||||
//
|
||||
// func Register(impl Scrobbler) { ... }
|
||||
//
|
||||
//go:generate go run ../cmd/ndpgen -capability-only -input=. -output=../pdk -go
|
||||
//go:generate go run ../cmd/ndpgen -schemas -input=.
|
||||
package capabilities
|
||||
|
||||
@ -16,17 +16,17 @@ type Lifecycle interface {
|
||||
// The output can contain an error string if initialization failed, which will be
|
||||
// logged but will not prevent the plugin from being loaded.
|
||||
//nd:export name=nd_on_init
|
||||
OnInit(OnInitInput) (OnInitOutput, error)
|
||||
OnInit(InitRequest) (InitResponse, error)
|
||||
}
|
||||
|
||||
// OnInitInput is the input provided to the init callback.
|
||||
// InitRequest is the request provided to the init callback.
|
||||
// Currently empty, reserved for future use.
|
||||
type OnInitInput struct{}
|
||||
type InitRequest struct{}
|
||||
|
||||
// OnInitOutput is the output from the init callback.
|
||||
type OnInitOutput struct {
|
||||
// InitResponse is the response from the init callback.
|
||||
type InitResponse struct {
|
||||
// Error is the error message if initialization failed.
|
||||
// Empty or null indicates success.
|
||||
// Empty string indicates success.
|
||||
// The error is logged but does not prevent the plugin from being loaded.
|
||||
Error *string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
@ -7,27 +7,26 @@ exports:
|
||||
The output can contain an error string if initialization failed, which will be
|
||||
logged but will not prevent the plugin from being loaded.
|
||||
input:
|
||||
$ref: '#/components/schemas/OnInitInput'
|
||||
$ref: '#/components/schemas/InitRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/OnInitOutput'
|
||||
$ref: '#/components/schemas/InitResponse'
|
||||
contentType: application/json
|
||||
components:
|
||||
schemas:
|
||||
OnInitInput:
|
||||
InitRequest:
|
||||
description: |-
|
||||
OnInitInput is the input provided to the init callback.
|
||||
InitRequest is the request provided to the init callback.
|
||||
Currently empty, reserved for future use.
|
||||
type: object
|
||||
properties: {}
|
||||
OnInitOutput:
|
||||
description: OnInitOutput is the output from the init callback.
|
||||
InitResponse:
|
||||
description: InitResponse is the response from the init callback.
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: |-
|
||||
Error is the error message if initialization failed.
|
||||
Empty or null indicates success.
|
||||
Empty string indicates success.
|
||||
The error is logged but does not prevent the plugin from being loaded.
|
||||
nullable: true
|
||||
|
||||
@ -11,81 +11,81 @@ package capabilities
|
||||
type MetadataAgent interface {
|
||||
// GetArtistMBID retrieves the MusicBrainz ID for an artist.
|
||||
//nd:export name=nd_get_artist_mbid
|
||||
GetArtistMBID(ArtistMBIDInput) (ArtistMBIDOutput, error)
|
||||
GetArtistMBID(ArtistMBIDRequest) (ArtistMBIDResponse, error)
|
||||
|
||||
// GetArtistURL retrieves the external URL for an artist.
|
||||
//nd:export name=nd_get_artist_url
|
||||
GetArtistURL(ArtistInput) (ArtistURLOutput, error)
|
||||
GetArtistURL(ArtistRequest) (ArtistURLResponse, error)
|
||||
|
||||
// GetArtistBiography retrieves the biography for an artist.
|
||||
//nd:export name=nd_get_artist_biography
|
||||
GetArtistBiography(ArtistInput) (ArtistBiographyOutput, error)
|
||||
GetArtistBiography(ArtistRequest) (ArtistBiographyResponse, error)
|
||||
|
||||
// GetSimilarArtists retrieves similar artists for a given artist.
|
||||
//nd:export name=nd_get_similar_artists
|
||||
GetSimilarArtists(SimilarArtistsInput) (SimilarArtistsOutput, error)
|
||||
GetSimilarArtists(SimilarArtistsRequest) (SimilarArtistsResponse, error)
|
||||
|
||||
// GetArtistImages retrieves images for an artist.
|
||||
//nd:export name=nd_get_artist_images
|
||||
GetArtistImages(ArtistInput) (ArtistImagesOutput, error)
|
||||
GetArtistImages(ArtistRequest) (ArtistImagesResponse, error)
|
||||
|
||||
// GetArtistTopSongs retrieves top songs for an artist.
|
||||
//nd:export name=nd_get_artist_top_songs
|
||||
GetArtistTopSongs(TopSongsInput) (TopSongsOutput, error)
|
||||
GetArtistTopSongs(TopSongsRequest) (TopSongsResponse, error)
|
||||
|
||||
// GetAlbumInfo retrieves album information.
|
||||
//nd:export name=nd_get_album_info
|
||||
GetAlbumInfo(AlbumInput) (AlbumInfoOutput, error)
|
||||
GetAlbumInfo(AlbumRequest) (AlbumInfoResponse, error)
|
||||
|
||||
// GetAlbumImages retrieves images for an album.
|
||||
//nd:export name=nd_get_album_images
|
||||
GetAlbumImages(AlbumInput) (AlbumImagesOutput, error)
|
||||
GetAlbumImages(AlbumRequest) (AlbumImagesResponse, error)
|
||||
}
|
||||
|
||||
// ArtistMBIDInput is the input for GetArtistMBID.
|
||||
type ArtistMBIDInput struct {
|
||||
// ArtistMBIDRequest is the request for GetArtistMBID.
|
||||
type ArtistMBIDRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// ArtistMBIDOutput is the output for GetArtistMBID.
|
||||
type ArtistMBIDOutput struct {
|
||||
// ArtistMBIDResponse is the response for GetArtistMBID.
|
||||
type ArtistMBIDResponse struct {
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID string `json:"mbid"`
|
||||
}
|
||||
|
||||
// ArtistInput is the common input for artist-related functions.
|
||||
type ArtistInput struct {
|
||||
// ArtistRequest is the common request for artist-related functions.
|
||||
type ArtistRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// ArtistURLOutput is the output for GetArtistURL.
|
||||
type ArtistURLOutput struct {
|
||||
// ArtistURLResponse is the response for GetArtistURL.
|
||||
type ArtistURLResponse struct {
|
||||
// URL is the external URL for the artist.
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// ArtistBiographyOutput is the output for GetArtistBiography.
|
||||
type ArtistBiographyOutput struct {
|
||||
// ArtistBiographyResponse is the response for GetArtistBiography.
|
||||
type ArtistBiographyResponse struct {
|
||||
// Biography is the artist biography text.
|
||||
Biography string `json:"biography"`
|
||||
}
|
||||
|
||||
// SimilarArtistsInput is the input for GetSimilarArtists.
|
||||
type SimilarArtistsInput struct {
|
||||
// SimilarArtistsRequest is the request for GetSimilarArtists.
|
||||
type SimilarArtistsRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
// Limit is the maximum number of similar artists to return.
|
||||
Limit int32 `json:"limit"`
|
||||
}
|
||||
@ -95,11 +95,11 @@ type ArtistRef struct {
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// SimilarArtistsOutput is the output for GetSimilarArtists.
|
||||
type SimilarArtistsOutput struct {
|
||||
// SimilarArtistsResponse is the response for GetSimilarArtists.
|
||||
type SimilarArtistsResponse struct {
|
||||
// Artists is the list of similar artists.
|
||||
Artists []ArtistRef `json:"artists"`
|
||||
}
|
||||
@ -112,20 +112,20 @@ type ImageInfo struct {
|
||||
Size int32 `json:"size"`
|
||||
}
|
||||
|
||||
// ArtistImagesOutput is the output for GetArtistImages.
|
||||
type ArtistImagesOutput struct {
|
||||
// ArtistImagesResponse is the response for GetArtistImages.
|
||||
type ArtistImagesResponse struct {
|
||||
// Images is the list of artist images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
}
|
||||
|
||||
// TopSongsInput is the input for GetArtistTopSongs.
|
||||
type TopSongsInput struct {
|
||||
// TopSongsRequest is the request for GetArtistTopSongs.
|
||||
type TopSongsRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
// Count is the maximum number of top songs to return.
|
||||
Count int32 `json:"count"`
|
||||
}
|
||||
@ -135,27 +135,27 @@ type SongRef struct {
|
||||
// Name is the song name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the song.
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// TopSongsOutput is the output for GetArtistTopSongs.
|
||||
type TopSongsOutput struct {
|
||||
// TopSongsResponse is the response for GetArtistTopSongs.
|
||||
type TopSongsResponse struct {
|
||||
// Songs is the list of top songs.
|
||||
Songs []SongRef `json:"songs"`
|
||||
}
|
||||
|
||||
// AlbumInput is the common input for album-related functions.
|
||||
type AlbumInput struct {
|
||||
// AlbumRequest is the common request for album-related functions.
|
||||
type AlbumRequest struct {
|
||||
// Name is the album name.
|
||||
Name string `json:"name"`
|
||||
// Artist is the album artist name.
|
||||
Artist string `json:"artist"`
|
||||
// MBID is the MusicBrainz ID for the album (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// AlbumInfoOutput is the output for GetAlbumInfo.
|
||||
type AlbumInfoOutput struct {
|
||||
// AlbumInfoResponse is the response for GetAlbumInfo.
|
||||
type AlbumInfoResponse struct {
|
||||
// Name is the album name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the album.
|
||||
@ -166,8 +166,8 @@ type AlbumInfoOutput struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// AlbumImagesOutput is the output for GetAlbumImages.
|
||||
type AlbumImagesOutput struct {
|
||||
// AlbumImagesResponse is the response for GetAlbumImages.
|
||||
type AlbumImagesResponse struct {
|
||||
// Images is the list of album images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
}
|
||||
|
||||
@ -3,71 +3,71 @@ exports:
|
||||
nd_get_artist_mbid:
|
||||
description: GetArtistMBID retrieves the MusicBrainz ID for an artist.
|
||||
input:
|
||||
$ref: '#/components/schemas/ArtistMBIDInput'
|
||||
$ref: '#/components/schemas/ArtistMBIDRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/ArtistMBIDOutput'
|
||||
$ref: '#/components/schemas/ArtistMBIDResponse'
|
||||
contentType: application/json
|
||||
nd_get_artist_url:
|
||||
description: GetArtistURL retrieves the external URL for an artist.
|
||||
input:
|
||||
$ref: '#/components/schemas/ArtistInput'
|
||||
$ref: '#/components/schemas/ArtistRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/ArtistURLOutput'
|
||||
$ref: '#/components/schemas/ArtistURLResponse'
|
||||
contentType: application/json
|
||||
nd_get_artist_biography:
|
||||
description: GetArtistBiography retrieves the biography for an artist.
|
||||
input:
|
||||
$ref: '#/components/schemas/ArtistInput'
|
||||
$ref: '#/components/schemas/ArtistRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/ArtistBiographyOutput'
|
||||
$ref: '#/components/schemas/ArtistBiographyResponse'
|
||||
contentType: application/json
|
||||
nd_get_similar_artists:
|
||||
description: GetSimilarArtists retrieves similar artists for a given artist.
|
||||
input:
|
||||
$ref: '#/components/schemas/SimilarArtistsInput'
|
||||
$ref: '#/components/schemas/SimilarArtistsRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/SimilarArtistsOutput'
|
||||
$ref: '#/components/schemas/SimilarArtistsResponse'
|
||||
contentType: application/json
|
||||
nd_get_artist_images:
|
||||
description: GetArtistImages retrieves images for an artist.
|
||||
input:
|
||||
$ref: '#/components/schemas/ArtistInput'
|
||||
$ref: '#/components/schemas/ArtistRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/ArtistImagesOutput'
|
||||
$ref: '#/components/schemas/ArtistImagesResponse'
|
||||
contentType: application/json
|
||||
nd_get_artist_top_songs:
|
||||
description: GetArtistTopSongs retrieves top songs for an artist.
|
||||
input:
|
||||
$ref: '#/components/schemas/TopSongsInput'
|
||||
$ref: '#/components/schemas/TopSongsRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/TopSongsOutput'
|
||||
$ref: '#/components/schemas/TopSongsResponse'
|
||||
contentType: application/json
|
||||
nd_get_album_info:
|
||||
description: GetAlbumInfo retrieves album information.
|
||||
input:
|
||||
$ref: '#/components/schemas/AlbumInput'
|
||||
$ref: '#/components/schemas/AlbumRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/AlbumInfoOutput'
|
||||
$ref: '#/components/schemas/AlbumInfoResponse'
|
||||
contentType: application/json
|
||||
nd_get_album_images:
|
||||
description: GetAlbumImages retrieves images for an album.
|
||||
input:
|
||||
$ref: '#/components/schemas/AlbumInput'
|
||||
$ref: '#/components/schemas/AlbumRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/AlbumImagesOutput'
|
||||
$ref: '#/components/schemas/AlbumImagesResponse'
|
||||
contentType: application/json
|
||||
components:
|
||||
schemas:
|
||||
AlbumImagesOutput:
|
||||
description: AlbumImagesOutput is the output for GetAlbumImages.
|
||||
AlbumImagesResponse:
|
||||
description: AlbumImagesResponse is the response for GetAlbumImages.
|
||||
type: object
|
||||
properties:
|
||||
images:
|
||||
@ -77,8 +77,8 @@ components:
|
||||
$ref: '#/components/schemas/ImageInfo'
|
||||
required:
|
||||
- images
|
||||
AlbumInfoOutput:
|
||||
description: AlbumInfoOutput is the output for GetAlbumInfo.
|
||||
AlbumInfoResponse:
|
||||
description: AlbumInfoResponse is the response for GetAlbumInfo.
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
@ -98,8 +98,8 @@ components:
|
||||
- mbid
|
||||
- description
|
||||
- url
|
||||
AlbumInput:
|
||||
description: AlbumInput is the common input for album-related functions.
|
||||
AlbumRequest:
|
||||
description: AlbumRequest is the common request for album-related functions.
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
@ -111,12 +111,11 @@ components:
|
||||
mbid:
|
||||
type: string
|
||||
description: MBID is the MusicBrainz ID for the album (if known).
|
||||
nullable: true
|
||||
required:
|
||||
- name
|
||||
- artist
|
||||
ArtistBiographyOutput:
|
||||
description: ArtistBiographyOutput is the output for GetArtistBiography.
|
||||
ArtistBiographyResponse:
|
||||
description: ArtistBiographyResponse is the response for GetArtistBiography.
|
||||
type: object
|
||||
properties:
|
||||
biography:
|
||||
@ -124,8 +123,8 @@ components:
|
||||
description: Biography is the artist biography text.
|
||||
required:
|
||||
- biography
|
||||
ArtistImagesOutput:
|
||||
description: ArtistImagesOutput is the output for GetArtistImages.
|
||||
ArtistImagesResponse:
|
||||
description: ArtistImagesResponse is the response for GetArtistImages.
|
||||
type: object
|
||||
properties:
|
||||
images:
|
||||
@ -135,25 +134,8 @@ components:
|
||||
$ref: '#/components/schemas/ImageInfo'
|
||||
required:
|
||||
- images
|
||||
ArtistInput:
|
||||
description: ArtistInput is the common input for artist-related functions.
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: ID is the internal Navidrome artist ID.
|
||||
name:
|
||||
type: string
|
||||
description: Name is the artist name.
|
||||
mbid:
|
||||
type: string
|
||||
description: MBID is the MusicBrainz ID for the artist (if known).
|
||||
nullable: true
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
ArtistMBIDInput:
|
||||
description: ArtistMBIDInput is the input for GetArtistMBID.
|
||||
ArtistMBIDRequest:
|
||||
description: ArtistMBIDRequest is the request for GetArtistMBID.
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
@ -165,8 +147,8 @@ components:
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
ArtistMBIDOutput:
|
||||
description: ArtistMBIDOutput is the output for GetArtistMBID.
|
||||
ArtistMBIDResponse:
|
||||
description: ArtistMBIDResponse is the response for GetArtistMBID.
|
||||
type: object
|
||||
properties:
|
||||
mbid:
|
||||
@ -184,11 +166,26 @@ components:
|
||||
mbid:
|
||||
type: string
|
||||
description: MBID is the MusicBrainz ID for the artist.
|
||||
nullable: true
|
||||
required:
|
||||
- name
|
||||
ArtistURLOutput:
|
||||
description: ArtistURLOutput is the output for GetArtistURL.
|
||||
ArtistRequest:
|
||||
description: ArtistRequest is the common request for artist-related functions.
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: ID is the internal Navidrome artist ID.
|
||||
name:
|
||||
type: string
|
||||
description: Name is the artist name.
|
||||
mbid:
|
||||
type: string
|
||||
description: MBID is the MusicBrainz ID for the artist (if known).
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
ArtistURLResponse:
|
||||
description: ArtistURLResponse is the response for GetArtistURL.
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
@ -210,8 +207,8 @@ components:
|
||||
required:
|
||||
- url
|
||||
- size
|
||||
SimilarArtistsInput:
|
||||
description: SimilarArtistsInput is the input for GetSimilarArtists.
|
||||
SimilarArtistsRequest:
|
||||
description: SimilarArtistsRequest is the request for GetSimilarArtists.
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
@ -223,7 +220,6 @@ components:
|
||||
mbid:
|
||||
type: string
|
||||
description: MBID is the MusicBrainz ID for the artist (if known).
|
||||
nullable: true
|
||||
limit:
|
||||
type: integer
|
||||
format: int32
|
||||
@ -232,8 +228,8 @@ components:
|
||||
- id
|
||||
- name
|
||||
- limit
|
||||
SimilarArtistsOutput:
|
||||
description: SimilarArtistsOutput is the output for GetSimilarArtists.
|
||||
SimilarArtistsResponse:
|
||||
description: SimilarArtistsResponse is the response for GetSimilarArtists.
|
||||
type: object
|
||||
properties:
|
||||
artists:
|
||||
@ -253,11 +249,10 @@ components:
|
||||
mbid:
|
||||
type: string
|
||||
description: MBID is the MusicBrainz ID for the song.
|
||||
nullable: true
|
||||
required:
|
||||
- name
|
||||
TopSongsInput:
|
||||
description: TopSongsInput is the input for GetArtistTopSongs.
|
||||
TopSongsRequest:
|
||||
description: TopSongsRequest is the request for GetArtistTopSongs.
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
@ -269,7 +264,6 @@ components:
|
||||
mbid:
|
||||
type: string
|
||||
description: MBID is the MusicBrainz ID for the artist (if known).
|
||||
nullable: true
|
||||
count:
|
||||
type: integer
|
||||
format: int32
|
||||
@ -278,8 +272,8 @@ components:
|
||||
- id
|
||||
- name
|
||||
- count
|
||||
TopSongsOutput:
|
||||
description: TopSongsOutput is the output for GetArtistTopSongs.
|
||||
TopSongsResponse:
|
||||
description: TopSongsResponse is the response for GetArtistTopSongs.
|
||||
type: object
|
||||
properties:
|
||||
songs:
|
||||
|
||||
@ -9,11 +9,11 @@ package capabilities
|
||||
type SchedulerCallback interface {
|
||||
// OnSchedulerCallback is called when a scheduled task fires.
|
||||
//nd:export name=nd_scheduler_callback
|
||||
OnSchedulerCallback(SchedulerCallbackInput) (SchedulerCallbackOutput, error)
|
||||
OnSchedulerCallback(SchedulerCallbackRequest) (SchedulerCallbackResponse, error)
|
||||
}
|
||||
|
||||
// SchedulerCallbackInput is the input provided when a scheduled task fires.
|
||||
type SchedulerCallbackInput struct {
|
||||
// SchedulerCallbackRequest is the request provided when a scheduled task fires.
|
||||
type SchedulerCallbackRequest struct {
|
||||
// ScheduleID is the unique identifier for this scheduled task.
|
||||
// This is either the ID provided when scheduling, or an auto-generated UUID if none was specified.
|
||||
ScheduleID string `json:"scheduleId"`
|
||||
@ -25,10 +25,10 @@ type SchedulerCallbackInput struct {
|
||||
IsRecurring bool `json:"isRecurring"`
|
||||
}
|
||||
|
||||
// SchedulerCallbackOutput is the output from the scheduler callback.
|
||||
type SchedulerCallbackOutput struct {
|
||||
// SchedulerCallbackResponse is the response from the scheduler callback.
|
||||
type SchedulerCallbackResponse struct {
|
||||
// Error is the error message if the callback failed to process the scheduled task.
|
||||
// Empty or null indicates success. The error is logged but does not
|
||||
// Empty string indicates success. The error is logged but does not
|
||||
// affect the scheduling system.
|
||||
Error *string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
@ -3,15 +3,15 @@ exports:
|
||||
nd_scheduler_callback:
|
||||
description: OnSchedulerCallback is called when a scheduled task fires.
|
||||
input:
|
||||
$ref: '#/components/schemas/SchedulerCallbackInput'
|
||||
$ref: '#/components/schemas/SchedulerCallbackRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/SchedulerCallbackOutput'
|
||||
$ref: '#/components/schemas/SchedulerCallbackResponse'
|
||||
contentType: application/json
|
||||
components:
|
||||
schemas:
|
||||
SchedulerCallbackInput:
|
||||
description: SchedulerCallbackInput is the input provided when a scheduled task fires.
|
||||
SchedulerCallbackRequest:
|
||||
description: SchedulerCallbackRequest is the request provided when a scheduled task fires.
|
||||
type: object
|
||||
properties:
|
||||
scheduleId:
|
||||
@ -33,14 +33,13 @@ components:
|
||||
- scheduleId
|
||||
- payload
|
||||
- isRecurring
|
||||
SchedulerCallbackOutput:
|
||||
description: SchedulerCallbackOutput is the output from the scheduler callback.
|
||||
SchedulerCallbackResponse:
|
||||
description: SchedulerCallbackResponse is the response from the scheduler callback.
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: |-
|
||||
Error is the error message if the callback failed to process the scheduled task.
|
||||
Empty or null indicates success. The error is logged but does not
|
||||
Empty string indicates success. The error is logged but does not
|
||||
affect the scheduling system.
|
||||
nullable: true
|
||||
|
||||
@ -11,27 +11,27 @@ package capabilities
|
||||
type Scrobbler interface {
|
||||
// IsAuthorized checks if a user is authorized to scrobble to this service.
|
||||
//nd:export name=nd_scrobbler_is_authorized
|
||||
IsAuthorized(AuthInput) (AuthOutput, error)
|
||||
IsAuthorized(IsAuthorizedRequest) (IsAuthorizedResponse, error)
|
||||
|
||||
// NowPlaying sends a now playing notification to the scrobbling service.
|
||||
//nd:export name=nd_scrobbler_now_playing
|
||||
NowPlaying(NowPlayingInput) (ScrobblerOutput, error)
|
||||
NowPlaying(NowPlayingRequest) (ScrobblerResponse, error)
|
||||
|
||||
// Scrobble submits a completed scrobble to the scrobbling service.
|
||||
//nd:export name=nd_scrobbler_scrobble
|
||||
Scrobble(ScrobbleInput) (ScrobblerOutput, error)
|
||||
Scrobble(ScrobbleRequest) (ScrobblerResponse, error)
|
||||
}
|
||||
|
||||
// AuthInput is the input for authorization check.
|
||||
type AuthInput struct {
|
||||
// IsAuthorizedRequest is the request for authorization check.
|
||||
type IsAuthorizedRequest struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// AuthOutput is the output for authorization check.
|
||||
type AuthOutput struct {
|
||||
// IsAuthorizedResponse is the response for authorization check.
|
||||
type IsAuthorizedResponse struct {
|
||||
// Authorized indicates whether the user is authorized to scrobble.
|
||||
Authorized bool `json:"authorized"`
|
||||
}
|
||||
@ -49,27 +49,27 @@ type TrackInfo struct {
|
||||
// AlbumArtist is the album artist.
|
||||
AlbumArtist string `json:"albumArtist"`
|
||||
// Duration is the track duration in seconds.
|
||||
Duration float64 `json:"duration"`
|
||||
Duration float32 `json:"duration"`
|
||||
// TrackNumber is the track number on the album.
|
||||
TrackNumber int32 `json:"trackNumber"`
|
||||
// DiscNumber is the disc number.
|
||||
DiscNumber int32 `json:"discNumber"`
|
||||
// MBZRecordingID is the MusicBrainz recording ID.
|
||||
MBZRecordingID *string `json:"mbzRecordingId,omitempty"`
|
||||
MBZRecordingID string `json:"mbzRecordingId,omitempty"`
|
||||
// MBZAlbumID is the MusicBrainz album/release ID.
|
||||
MBZAlbumID *string `json:"mbzAlbumId,omitempty"`
|
||||
MBZAlbumID string `json:"mbzAlbumId,omitempty"`
|
||||
// MBZArtistID is the MusicBrainz artist ID.
|
||||
MBZArtistID *string `json:"mbzArtistId,omitempty"`
|
||||
MBZArtistID string `json:"mbzArtistId,omitempty"`
|
||||
// MBZReleaseGroupID is the MusicBrainz release group ID.
|
||||
MBZReleaseGroupID *string `json:"mbzReleaseGroupId,omitempty"`
|
||||
MBZReleaseGroupID string `json:"mbzReleaseGroupId,omitempty"`
|
||||
// MBZAlbumArtistID is the MusicBrainz album artist ID.
|
||||
MBZAlbumArtistID *string `json:"mbzAlbumArtistId,omitempty"`
|
||||
MBZAlbumArtistID string `json:"mbzAlbumArtistId,omitempty"`
|
||||
// MBZReleaseTrackID is the MusicBrainz release track ID.
|
||||
MBZReleaseTrackID *string `json:"mbzReleaseTrackId,omitempty"`
|
||||
MBZReleaseTrackID string `json:"mbzReleaseTrackId,omitempty"`
|
||||
}
|
||||
|
||||
// NowPlayingInput is the input for now playing notification.
|
||||
type NowPlayingInput struct {
|
||||
// NowPlayingRequest is the request for now playing notification.
|
||||
type NowPlayingRequest struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
@ -80,8 +80,8 @@ type NowPlayingInput struct {
|
||||
Position int32 `json:"position"`
|
||||
}
|
||||
|
||||
// ScrobbleInput is the input for submitting a scrobble.
|
||||
type ScrobbleInput struct {
|
||||
// ScrobbleRequest is the request for submitting a scrobble.
|
||||
type ScrobbleRequest struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
@ -106,10 +106,10 @@ const (
|
||||
ScrobblerErrorUnrecoverable ScrobblerErrorType = "unrecoverable"
|
||||
)
|
||||
|
||||
// ScrobblerOutput is the output for scrobbler operations.
|
||||
type ScrobblerOutput struct {
|
||||
// ScrobblerResponse is the response for scrobbler operations.
|
||||
type ScrobblerResponse struct {
|
||||
// Error is the error message if the operation failed.
|
||||
Error *string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
// ErrorType indicates how Navidrome should handle the error.
|
||||
ErrorType *ScrobblerErrorType `json:"errorType,omitempty"`
|
||||
ErrorType ScrobblerErrorType `json:"errorType,omitempty"`
|
||||
}
|
||||
|
||||
@ -3,31 +3,31 @@ exports:
|
||||
nd_scrobbler_is_authorized:
|
||||
description: IsAuthorized checks if a user is authorized to scrobble to this service.
|
||||
input:
|
||||
$ref: '#/components/schemas/AuthInput'
|
||||
$ref: '#/components/schemas/IsAuthorizedRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/AuthOutput'
|
||||
$ref: '#/components/schemas/IsAuthorizedResponse'
|
||||
contentType: application/json
|
||||
nd_scrobbler_now_playing:
|
||||
description: NowPlaying sends a now playing notification to the scrobbling service.
|
||||
input:
|
||||
$ref: '#/components/schemas/NowPlayingInput'
|
||||
$ref: '#/components/schemas/NowPlayingRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/ScrobblerOutput'
|
||||
$ref: '#/components/schemas/ScrobblerResponse'
|
||||
contentType: application/json
|
||||
nd_scrobbler_scrobble:
|
||||
description: Scrobble submits a completed scrobble to the scrobbling service.
|
||||
input:
|
||||
$ref: '#/components/schemas/ScrobbleInput'
|
||||
$ref: '#/components/schemas/ScrobbleRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/ScrobblerOutput'
|
||||
$ref: '#/components/schemas/ScrobblerResponse'
|
||||
contentType: application/json
|
||||
components:
|
||||
schemas:
|
||||
AuthInput:
|
||||
description: AuthInput is the input for authorization check.
|
||||
IsAuthorizedRequest:
|
||||
description: IsAuthorizedRequest is the request for authorization check.
|
||||
type: object
|
||||
properties:
|
||||
userId:
|
||||
@ -39,8 +39,8 @@ components:
|
||||
required:
|
||||
- userId
|
||||
- username
|
||||
AuthOutput:
|
||||
description: AuthOutput is the output for authorization check.
|
||||
IsAuthorizedResponse:
|
||||
description: IsAuthorizedResponse is the response for authorization check.
|
||||
type: object
|
||||
properties:
|
||||
authorized:
|
||||
@ -48,8 +48,8 @@ components:
|
||||
description: Authorized indicates whether the user is authorized to scrobble.
|
||||
required:
|
||||
- authorized
|
||||
NowPlayingInput:
|
||||
description: NowPlayingInput is the input for now playing notification.
|
||||
NowPlayingRequest:
|
||||
description: NowPlayingRequest is the request for now playing notification.
|
||||
type: object
|
||||
properties:
|
||||
userId:
|
||||
@ -70,8 +70,8 @@ components:
|
||||
- username
|
||||
- track
|
||||
- position
|
||||
ScrobbleInput:
|
||||
description: ScrobbleInput is the input for submitting a scrobble.
|
||||
ScrobbleRequest:
|
||||
description: ScrobbleRequest is the request for submitting a scrobble.
|
||||
type: object
|
||||
properties:
|
||||
userId:
|
||||
@ -92,18 +92,16 @@ components:
|
||||
- username
|
||||
- track
|
||||
- timestamp
|
||||
ScrobblerOutput:
|
||||
description: ScrobblerOutput is the output for scrobbler operations.
|
||||
ScrobblerResponse:
|
||||
description: ScrobblerResponse is the response for scrobbler operations.
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error is the error message if the operation failed.
|
||||
nullable: true
|
||||
errorType:
|
||||
$ref: '#/components/schemas/ScrobblerErrorType'
|
||||
description: ErrorType indicates how Navidrome should handle the error.
|
||||
nullable: true
|
||||
TrackInfo:
|
||||
description: TrackInfo contains track metadata for scrobbling.
|
||||
type: object
|
||||
@ -138,27 +136,21 @@ components:
|
||||
mbzRecordingId:
|
||||
type: string
|
||||
description: MBZRecordingID is the MusicBrainz recording ID.
|
||||
nullable: true
|
||||
mbzAlbumId:
|
||||
type: string
|
||||
description: MBZAlbumID is the MusicBrainz album/release ID.
|
||||
nullable: true
|
||||
mbzArtistId:
|
||||
type: string
|
||||
description: MBZArtistID is the MusicBrainz artist ID.
|
||||
nullable: true
|
||||
mbzReleaseGroupId:
|
||||
type: string
|
||||
description: MBZReleaseGroupID is the MusicBrainz release group ID.
|
||||
nullable: true
|
||||
mbzAlbumArtistId:
|
||||
type: string
|
||||
description: MBZAlbumArtistID is the MusicBrainz album artist ID.
|
||||
nullable: true
|
||||
mbzReleaseTrackId:
|
||||
type: string
|
||||
description: MBZReleaseTrackID is the MusicBrainz release track ID.
|
||||
nullable: true
|
||||
required:
|
||||
- id
|
||||
- title
|
||||
|
||||
@ -10,68 +10,68 @@ package capabilities
|
||||
type WebSocketCallback interface {
|
||||
// OnTextMessage is called when a text message is received on a WebSocket connection.
|
||||
//nd:export name=nd_websocket_on_text_message
|
||||
OnTextMessage(OnTextMessageInput) (OnTextMessageOutput, error)
|
||||
OnTextMessage(OnTextMessageRequest) (OnTextMessageResponse, error)
|
||||
|
||||
// OnBinaryMessage is called when a binary message is received on a WebSocket connection.
|
||||
//nd:export name=nd_websocket_on_binary_message
|
||||
OnBinaryMessage(OnBinaryMessageInput) (OnBinaryMessageOutput, error)
|
||||
OnBinaryMessage(OnBinaryMessageRequest) (OnBinaryMessageResponse, error)
|
||||
|
||||
// OnError is called when an error occurs on a WebSocket connection.
|
||||
//nd:export name=nd_websocket_on_error
|
||||
OnError(OnErrorInput) (OnErrorOutput, error)
|
||||
OnError(OnErrorRequest) (OnErrorResponse, error)
|
||||
|
||||
// OnClose is called when a WebSocket connection is closed.
|
||||
//nd:export name=nd_websocket_on_close
|
||||
OnClose(OnCloseInput) (OnCloseOutput, error)
|
||||
OnClose(OnCloseRequest) (OnCloseResponse, error)
|
||||
}
|
||||
|
||||
// OnTextMessageInput is the input provided when a text message is received.
|
||||
type OnTextMessageInput struct {
|
||||
// OnTextMessageRequest is the request provided when a text message is received.
|
||||
type OnTextMessageRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection that received the message.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Message is the text message content received from the WebSocket.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// OnTextMessageOutput is the output from the text message handler.
|
||||
type OnTextMessageOutput struct {
|
||||
// OnTextMessageResponse is the response from the text message handler.
|
||||
type OnTextMessageResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnBinaryMessageInput is the input provided when a binary message is received.
|
||||
type OnBinaryMessageInput struct {
|
||||
// OnBinaryMessageRequest is the request provided when a binary message is received.
|
||||
type OnBinaryMessageRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection that received the message.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Data is the binary data received from the WebSocket, encoded as base64.
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
// OnBinaryMessageOutput is the output from the binary message handler.
|
||||
type OnBinaryMessageOutput struct {
|
||||
// OnBinaryMessageResponse is the response from the binary message handler.
|
||||
type OnBinaryMessageResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnErrorInput is the input provided when an error occurs on a WebSocket connection.
|
||||
type OnErrorInput struct {
|
||||
// OnErrorRequest is the request provided when an error occurs on a WebSocket connection.
|
||||
type OnErrorRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection where the error occurred.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Error is the error message describing what went wrong.
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// OnErrorOutput is the output from the error handler.
|
||||
type OnErrorOutput struct {
|
||||
// OnErrorResponse is the response from the error handler.
|
||||
type OnErrorResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnCloseInput is the input provided when a WebSocket connection is closed.
|
||||
type OnCloseInput struct {
|
||||
// OnCloseRequest is the request provided when a WebSocket connection is closed.
|
||||
type OnCloseRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection that was closed.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Code is the WebSocket close status code (e.g., 1000 for normal closure,
|
||||
@ -81,9 +81,9 @@ type OnCloseInput struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// OnCloseOutput is the output from the close handler.
|
||||
type OnCloseOutput struct {
|
||||
// OnCloseResponse is the response from the close handler.
|
||||
type OnCloseResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
@ -3,39 +3,39 @@ exports:
|
||||
nd_websocket_on_text_message:
|
||||
description: OnTextMessage is called when a text message is received on a WebSocket connection.
|
||||
input:
|
||||
$ref: '#/components/schemas/OnTextMessageInput'
|
||||
$ref: '#/components/schemas/OnTextMessageRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/OnTextMessageOutput'
|
||||
$ref: '#/components/schemas/OnTextMessageResponse'
|
||||
contentType: application/json
|
||||
nd_websocket_on_binary_message:
|
||||
description: OnBinaryMessage is called when a binary message is received on a WebSocket connection.
|
||||
input:
|
||||
$ref: '#/components/schemas/OnBinaryMessageInput'
|
||||
$ref: '#/components/schemas/OnBinaryMessageRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/OnBinaryMessageOutput'
|
||||
$ref: '#/components/schemas/OnBinaryMessageResponse'
|
||||
contentType: application/json
|
||||
nd_websocket_on_error:
|
||||
description: OnError is called when an error occurs on a WebSocket connection.
|
||||
input:
|
||||
$ref: '#/components/schemas/OnErrorInput'
|
||||
$ref: '#/components/schemas/OnErrorRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/OnErrorOutput'
|
||||
$ref: '#/components/schemas/OnErrorResponse'
|
||||
contentType: application/json
|
||||
nd_websocket_on_close:
|
||||
description: OnClose is called when a WebSocket connection is closed.
|
||||
input:
|
||||
$ref: '#/components/schemas/OnCloseInput'
|
||||
$ref: '#/components/schemas/OnCloseRequest'
|
||||
contentType: application/json
|
||||
output:
|
||||
$ref: '#/components/schemas/OnCloseOutput'
|
||||
$ref: '#/components/schemas/OnCloseResponse'
|
||||
contentType: application/json
|
||||
components:
|
||||
schemas:
|
||||
OnBinaryMessageInput:
|
||||
description: OnBinaryMessageInput is the input provided when a binary message is received.
|
||||
OnBinaryMessageRequest:
|
||||
description: OnBinaryMessageRequest is the request provided when a binary message is received.
|
||||
type: object
|
||||
properties:
|
||||
connectionId:
|
||||
@ -47,18 +47,17 @@ components:
|
||||
required:
|
||||
- connectionId
|
||||
- data
|
||||
OnBinaryMessageOutput:
|
||||
description: OnBinaryMessageOutput is the output from the binary message handler.
|
||||
OnBinaryMessageResponse:
|
||||
description: OnBinaryMessageResponse is the response from the binary message handler.
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: |-
|
||||
Error is the error message if the callback failed.
|
||||
Empty or null indicates success.
|
||||
nullable: true
|
||||
OnCloseInput:
|
||||
description: OnCloseInput is the input provided when a WebSocket connection is closed.
|
||||
Empty string indicates success.
|
||||
OnCloseRequest:
|
||||
description: OnCloseRequest is the request provided when a WebSocket connection is closed.
|
||||
type: object
|
||||
properties:
|
||||
connectionId:
|
||||
@ -77,18 +76,17 @@ components:
|
||||
- connectionId
|
||||
- code
|
||||
- reason
|
||||
OnCloseOutput:
|
||||
description: OnCloseOutput is the output from the close handler.
|
||||
OnCloseResponse:
|
||||
description: OnCloseResponse is the response from the close handler.
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: |-
|
||||
Error is the error message if the callback failed.
|
||||
Empty or null indicates success.
|
||||
nullable: true
|
||||
OnErrorInput:
|
||||
description: OnErrorInput is the input provided when an error occurs on a WebSocket connection.
|
||||
Empty string indicates success.
|
||||
OnErrorRequest:
|
||||
description: OnErrorRequest is the request provided when an error occurs on a WebSocket connection.
|
||||
type: object
|
||||
properties:
|
||||
connectionId:
|
||||
@ -100,18 +98,17 @@ components:
|
||||
required:
|
||||
- connectionId
|
||||
- error
|
||||
OnErrorOutput:
|
||||
description: OnErrorOutput is the output from the error handler.
|
||||
OnErrorResponse:
|
||||
description: OnErrorResponse is the response from the error handler.
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: |-
|
||||
Error is the error message if the callback failed.
|
||||
Empty or null indicates success.
|
||||
nullable: true
|
||||
OnTextMessageInput:
|
||||
description: OnTextMessageInput is the input provided when a text message is received.
|
||||
Empty string indicates success.
|
||||
OnTextMessageRequest:
|
||||
description: OnTextMessageRequest is the request provided when a text message is received.
|
||||
type: object
|
||||
properties:
|
||||
connectionId:
|
||||
@ -123,13 +120,12 @@ components:
|
||||
required:
|
||||
- connectionId
|
||||
- message
|
||||
OnTextMessageOutput:
|
||||
description: OnTextMessageOutput is the output from the text message handler.
|
||||
OnTextMessageResponse:
|
||||
description: OnTextMessageResponse is the response from the text message handler.
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: |-
|
||||
Error is the error message if the callback failed.
|
||||
Empty or null indicates success.
|
||||
nullable: true
|
||||
Empty string indicates success.
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/plugins/capabilities"
|
||||
)
|
||||
|
||||
// CapabilityLifecycle indicates the plugin has lifecycle callback functions.
|
||||
@ -19,14 +20,6 @@ func init() {
|
||||
)
|
||||
}
|
||||
|
||||
// onInitInput is the input for nd_on_init (currently empty, reserved for future use)
|
||||
type onInitInput struct{}
|
||||
|
||||
// onInitOutput is the output from nd_on_init
|
||||
type onInitOutput struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// callPluginInit calls the plugin's nd_on_init function if it has the Lifecycle capability.
|
||||
// This is called after the plugin is fully loaded with all services registered.
|
||||
func callPluginInit(ctx context.Context, instance *plugin) {
|
||||
@ -36,7 +29,7 @@ func callPluginInit(ctx context.Context, instance *plugin) {
|
||||
|
||||
log.Debug(ctx, "Calling plugin init function", "plugin", instance.name)
|
||||
|
||||
result, err := callPluginFunction[onInitInput, onInitOutput](ctx, instance, FuncOnInit, onInitInput{})
|
||||
result, err := callPluginFunction[capabilities.InitRequest, capabilities.InitResponse](ctx, instance, FuncOnInit, capabilities.InitRequest{})
|
||||
if err != nil {
|
||||
log.Error(ctx, "Plugin init function failed", "plugin", instance.name, err)
|
||||
return
|
||||
|
||||
@ -71,7 +71,7 @@ var (
|
||||
|
||||
// OnInit is called when the plugin is loaded.
|
||||
// We use this to establish the initial WebSocket connection.
|
||||
func (p *cryptoTickerPlugin) OnInit(_ lifecycle.OnInitInput) (lifecycle.OnInitOutput, error) {
|
||||
func (p *cryptoTickerPlugin) OnInit(_ lifecycle.InitRequest) (lifecycle.InitResponse, error) {
|
||||
pdk.Log(pdk.LogInfo, "Crypto Ticker Plugin initializing...")
|
||||
|
||||
// Get ticker configuration
|
||||
@ -90,7 +90,7 @@ func (p *cryptoTickerPlugin) OnInit(_ lifecycle.OnInitInput) (lifecycle.OnInitOu
|
||||
// Don't fail init - let reconnect logic handle it
|
||||
}
|
||||
|
||||
return lifecycle.OnInitOutput{}, nil
|
||||
return lifecycle.InitResponse{}, nil
|
||||
}
|
||||
|
||||
// parseTickerSymbols parses a comma-separated list of ticker symbols
|
||||
@ -143,10 +143,10 @@ func connectAndSubscribe(tickers []string) error {
|
||||
}
|
||||
|
||||
// OnTextMessage is called when a text message is received
|
||||
func (p *cryptoTickerPlugin) OnTextMessage(input websocket.OnTextMessageInput) (websocket.OnTextMessageOutput, error) {
|
||||
func (p *cryptoTickerPlugin) OnTextMessage(input websocket.OnTextMessageRequest) (websocket.OnTextMessageResponse, error) {
|
||||
// Only process messages from our connection
|
||||
if input.ConnectionID != connectionID {
|
||||
return websocket.OnTextMessageOutput{}, nil
|
||||
return websocket.OnTextMessageResponse{}, nil
|
||||
}
|
||||
|
||||
// Try to parse as a ticker message
|
||||
@ -154,7 +154,7 @@ func (p *cryptoTickerPlugin) OnTextMessage(input websocket.OnTextMessageInput) (
|
||||
err := json.Unmarshal([]byte(input.Message), &ticker)
|
||||
if err != nil {
|
||||
// Not a valid JSON message, ignore
|
||||
return websocket.OnTextMessageOutput{}, nil
|
||||
return websocket.OnTextMessageResponse{}, nil
|
||||
}
|
||||
|
||||
// Only process ticker messages
|
||||
@ -163,7 +163,7 @@ func (p *cryptoTickerPlugin) OnTextMessage(input websocket.OnTextMessageInput) (
|
||||
if ticker.Type != "" {
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("Received %s message", ticker.Type))
|
||||
}
|
||||
return websocket.OnTextMessageOutput{}, nil
|
||||
return websocket.OnTextMessageResponse{}, nil
|
||||
}
|
||||
|
||||
// Calculate 24h change percentage
|
||||
@ -178,24 +178,24 @@ func (p *cryptoTickerPlugin) OnTextMessage(input websocket.OnTextMessageInput) (
|
||||
ticker.BestAsk,
|
||||
))
|
||||
|
||||
return websocket.OnTextMessageOutput{}, nil
|
||||
return websocket.OnTextMessageResponse{}, nil
|
||||
}
|
||||
|
||||
// OnBinaryMessage is called when a binary message is received
|
||||
func (p *cryptoTickerPlugin) OnBinaryMessage(input websocket.OnBinaryMessageInput) (websocket.OnBinaryMessageOutput, error) {
|
||||
func (p *cryptoTickerPlugin) OnBinaryMessage(input websocket.OnBinaryMessageRequest) (websocket.OnBinaryMessageResponse, error) {
|
||||
// Coinbase doesn't send binary messages, but we implement the handler anyway
|
||||
pdk.Log(pdk.LogWarn, fmt.Sprintf("Received unexpected binary message on connection %s", input.ConnectionID))
|
||||
return websocket.OnBinaryMessageOutput{}, nil
|
||||
return websocket.OnBinaryMessageResponse{}, nil
|
||||
}
|
||||
|
||||
// OnError is called when an error occurs on the WebSocket connection
|
||||
func (p *cryptoTickerPlugin) OnError(input websocket.OnErrorInput) (websocket.OnErrorOutput, error) {
|
||||
func (p *cryptoTickerPlugin) OnError(input websocket.OnErrorRequest) (websocket.OnErrorResponse, error) {
|
||||
pdk.Log(pdk.LogError, fmt.Sprintf("WebSocket error on connection %s: %s", input.ConnectionID, input.Error))
|
||||
return websocket.OnErrorOutput{}, nil
|
||||
return websocket.OnErrorResponse{}, nil
|
||||
}
|
||||
|
||||
// OnClose is called when the WebSocket connection is closed
|
||||
func (p *cryptoTickerPlugin) OnClose(input websocket.OnCloseInput) (websocket.OnCloseOutput, error) {
|
||||
func (p *cryptoTickerPlugin) OnClose(input websocket.OnCloseRequest) (websocket.OnCloseResponse, error) {
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("WebSocket connection %s closed (code: %d, reason: %s)",
|
||||
input.ConnectionID, input.Code, input.Reason))
|
||||
|
||||
@ -210,14 +210,14 @@ func (p *cryptoTickerPlugin) OnClose(input websocket.OnCloseInput) (websocket.On
|
||||
}
|
||||
}
|
||||
|
||||
return websocket.OnCloseOutput{}, nil
|
||||
return websocket.OnCloseResponse{}, nil
|
||||
}
|
||||
|
||||
// OnSchedulerCallback is called when a scheduled task fires
|
||||
func (p *cryptoTickerPlugin) OnSchedulerCallback(input scheduler.SchedulerCallbackInput) (scheduler.SchedulerCallbackOutput, error) {
|
||||
func (p *cryptoTickerPlugin) OnSchedulerCallback(input scheduler.SchedulerCallbackRequest) (scheduler.SchedulerCallbackResponse, error) {
|
||||
// Only handle our reconnection schedule
|
||||
if input.ScheduleID != reconnectScheduleID {
|
||||
return scheduler.SchedulerCallbackOutput{}, nil
|
||||
return scheduler.SchedulerCallbackResponse{}, nil
|
||||
}
|
||||
|
||||
pdk.Log(pdk.LogInfo, "Attempting to reconnect to Coinbase WebSocket API...")
|
||||
@ -244,7 +244,7 @@ func (p *cryptoTickerPlugin) OnSchedulerCallback(input scheduler.SchedulerCallba
|
||||
pdk.Log(pdk.LogInfo, "Successfully reconnected!")
|
||||
}
|
||||
|
||||
return scheduler.SchedulerCallbackOutput{}, nil
|
||||
return scheduler.SchedulerCallbackResponse{}, nil
|
||||
}
|
||||
|
||||
// calculatePercentChange calculates the percentage change between open and current price
|
||||
|
||||
@ -93,40 +93,42 @@ func getImageURL(trackID string) string {
|
||||
// ============================================================================
|
||||
|
||||
// IsAuthorized checks if a user is authorized for Discord Rich Presence.
|
||||
func (p *discordPlugin) IsAuthorized(input scrobbler.AuthInput) (scrobbler.AuthOutput, error) {
|
||||
func (p *discordPlugin) IsAuthorized(input scrobbler.IsAuthorizedRequest) (scrobbler.IsAuthorizedResponse, error) {
|
||||
_, users, err := getConfig()
|
||||
if err != nil {
|
||||
return scrobbler.AuthOutput{}, fmt.Errorf("failed to check user authorization: %w", err)
|
||||
return scrobbler.IsAuthorizedResponse{}, fmt.Errorf("failed to check user authorization: %w", err)
|
||||
}
|
||||
|
||||
_, authorized := users[input.Username]
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("IsAuthorized for user %s: %v", input.Username, authorized))
|
||||
return scrobbler.AuthOutput{Authorized: authorized}, nil
|
||||
return scrobbler.IsAuthorizedResponse{Authorized: authorized}, nil
|
||||
}
|
||||
|
||||
// NowPlaying sends a now playing notification to Discord.
|
||||
func (p *discordPlugin) NowPlaying(input scrobbler.NowPlayingInput) (scrobbler.ScrobblerOutput, error) {
|
||||
func (p *discordPlugin) NowPlaying(input scrobbler.NowPlayingRequest) (scrobbler.ScrobblerResponse, error) {
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("Setting presence for user %s, track: %s", input.Username, input.Track.Title))
|
||||
|
||||
// Load configuration
|
||||
clientID, users, err := getConfig()
|
||||
if err != nil {
|
||||
return scrobbler.ScrobblerOutput{}, fmt.Errorf("failed to get config: %w", err)
|
||||
return scrobbler.ScrobblerResponse{}, fmt.Errorf("failed to get config: %w", err)
|
||||
}
|
||||
|
||||
// Check authorization
|
||||
userToken, authorized := users[input.Username]
|
||||
if !authorized {
|
||||
errMsg := fmt.Sprintf("user '%s' not authorized", input.Username)
|
||||
errType := scrobbler.ScrobblerErrorNotAuthorized
|
||||
return scrobbler.ScrobblerOutput{Error: &errMsg, ErrorType: &errType}, nil
|
||||
return scrobbler.ScrobblerResponse{
|
||||
Error: fmt.Sprintf("user '%s' not authorized", input.Username),
|
||||
ErrorType: scrobbler.ScrobblerErrorNotAuthorized,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Connect to Discord
|
||||
if err := connect(input.Username, userToken); err != nil {
|
||||
errMsg := fmt.Sprintf("failed to connect to Discord: %v", err)
|
||||
errType := scrobbler.ScrobblerErrorRetryLater
|
||||
return scrobbler.ScrobblerOutput{Error: &errMsg, ErrorType: &errType}, nil
|
||||
return scrobbler.ScrobblerResponse{
|
||||
Error: fmt.Sprintf("failed to connect to Discord: %v", err),
|
||||
ErrorType: scrobbler.ScrobblerErrorRetryLater,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Cancel any existing completion schedule
|
||||
@ -153,9 +155,10 @@ func (p *discordPlugin) NowPlaying(input scrobbler.NowPlayingInput) (scrobbler.S
|
||||
LargeText: input.Track.Album,
|
||||
},
|
||||
}); err != nil {
|
||||
errMsg := fmt.Sprintf("failed to send activity: %v", err)
|
||||
errType := scrobbler.ScrobblerErrorRetryLater
|
||||
return scrobbler.ScrobblerOutput{Error: &errMsg, ErrorType: &errType}, nil
|
||||
return scrobbler.ScrobblerResponse{
|
||||
Error: fmt.Sprintf("failed to send activity: %v", err),
|
||||
ErrorType: scrobbler.ScrobblerErrorRetryLater,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Schedule a timer to clear the activity after the track completes
|
||||
@ -165,13 +168,13 @@ func (p *discordPlugin) NowPlaying(input scrobbler.NowPlayingInput) (scrobbler.S
|
||||
pdk.Log(pdk.LogWarn, fmt.Sprintf("Failed to schedule completion timer: %v", err))
|
||||
}
|
||||
|
||||
return scrobbler.ScrobblerOutput{}, nil
|
||||
return scrobbler.ScrobblerResponse{}, nil
|
||||
}
|
||||
|
||||
// Scrobble handles scrobble requests (no-op for Discord).
|
||||
func (p *discordPlugin) Scrobble(_ scrobbler.ScrobbleInput) (scrobbler.ScrobblerOutput, error) {
|
||||
func (p *discordPlugin) Scrobble(_ scrobbler.ScrobbleRequest) (scrobbler.ScrobblerResponse, error) {
|
||||
// Discord Rich Presence doesn't need scrobble events
|
||||
return scrobbler.ScrobblerOutput{}, nil
|
||||
return scrobbler.ScrobblerResponse{}, nil
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@ -179,7 +182,7 @@ func (p *discordPlugin) Scrobble(_ scrobbler.ScrobbleInput) (scrobbler.Scrobbler
|
||||
// ============================================================================
|
||||
|
||||
// OnSchedulerCallback handles scheduler callbacks.
|
||||
func (p *discordPlugin) OnSchedulerCallback(input scheduler.SchedulerCallbackInput) (scheduler.SchedulerCallbackOutput, error) {
|
||||
func (p *discordPlugin) OnSchedulerCallback(input scheduler.SchedulerCallbackRequest) (scheduler.SchedulerCallbackResponse, error) {
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("Scheduler callback: id=%s, payload=%s, recurring=%v", input.ScheduleID, input.Payload, input.IsRecurring))
|
||||
|
||||
// Route based on payload
|
||||
@ -187,23 +190,21 @@ func (p *discordPlugin) OnSchedulerCallback(input scheduler.SchedulerCallbackInp
|
||||
case payloadHeartbeat:
|
||||
// Heartbeat callback - scheduleId is the username
|
||||
if err := handleHeartbeatCallback(input.ScheduleID); err != nil {
|
||||
errMsg := err.Error()
|
||||
return scheduler.SchedulerCallbackOutput{Error: &errMsg}, nil
|
||||
return scheduler.SchedulerCallbackResponse{Error: err.Error()}, nil
|
||||
}
|
||||
|
||||
case payloadClearActivity:
|
||||
// Clear activity callback - scheduleId is "username-clear"
|
||||
username := strings.TrimSuffix(input.ScheduleID, "-clear")
|
||||
if err := handleClearActivityCallback(username); err != nil {
|
||||
errMsg := err.Error()
|
||||
return scheduler.SchedulerCallbackOutput{Error: &errMsg}, nil
|
||||
return scheduler.SchedulerCallbackResponse{Error: err.Error()}, nil
|
||||
}
|
||||
|
||||
default:
|
||||
pdk.Log(pdk.LogWarn, fmt.Sprintf("Unknown scheduler callback payload: %s", input.Payload))
|
||||
}
|
||||
|
||||
return scheduler.SchedulerCallbackOutput{}, nil
|
||||
return scheduler.SchedulerCallbackResponse{}, nil
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@ -211,30 +212,29 @@ func (p *discordPlugin) OnSchedulerCallback(input scheduler.SchedulerCallbackInp
|
||||
// ============================================================================
|
||||
|
||||
// OnTextMessage handles incoming WebSocket text messages.
|
||||
func (p *discordPlugin) OnTextMessage(input websocket.OnTextMessageInput) (websocket.OnTextMessageOutput, error) {
|
||||
func (p *discordPlugin) OnTextMessage(input websocket.OnTextMessageRequest) (websocket.OnTextMessageResponse, error) {
|
||||
if err := handleWebSocketMessage(input.ConnectionID, input.Message); err != nil {
|
||||
errMsg := err.Error()
|
||||
return websocket.OnTextMessageOutput{Error: &errMsg}, nil
|
||||
return websocket.OnTextMessageResponse{Error: err.Error()}, nil
|
||||
}
|
||||
return websocket.OnTextMessageOutput{}, nil
|
||||
return websocket.OnTextMessageResponse{}, nil
|
||||
}
|
||||
|
||||
// OnBinaryMessage handles incoming WebSocket binary messages.
|
||||
func (p *discordPlugin) OnBinaryMessage(input websocket.OnBinaryMessageInput) (websocket.OnBinaryMessageOutput, error) {
|
||||
func (p *discordPlugin) OnBinaryMessage(input websocket.OnBinaryMessageRequest) (websocket.OnBinaryMessageResponse, error) {
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("Received unexpected binary message for connection '%s'", input.ConnectionID))
|
||||
return websocket.OnBinaryMessageOutput{}, nil
|
||||
return websocket.OnBinaryMessageResponse{}, nil
|
||||
}
|
||||
|
||||
// OnError handles WebSocket errors.
|
||||
func (p *discordPlugin) OnError(input websocket.OnErrorInput) (websocket.OnErrorOutput, error) {
|
||||
func (p *discordPlugin) OnError(input websocket.OnErrorRequest) (websocket.OnErrorResponse, error) {
|
||||
pdk.Log(pdk.LogWarn, fmt.Sprintf("WebSocket error for connection '%s': %s", input.ConnectionID, input.Error))
|
||||
return websocket.OnErrorOutput{}, nil
|
||||
return websocket.OnErrorResponse{}, nil
|
||||
}
|
||||
|
||||
// OnClose handles WebSocket connection closure.
|
||||
func (p *discordPlugin) OnClose(input websocket.OnCloseInput) (websocket.OnCloseOutput, error) {
|
||||
func (p *discordPlugin) OnClose(input websocket.OnCloseRequest) (websocket.OnCloseResponse, error) {
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("WebSocket connection '%s' closed with code %d: %s", input.ConnectionID, input.Code, input.Reason))
|
||||
return websocket.OnCloseOutput{}, nil
|
||||
return websocket.OnCloseResponse{}, nil
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
||||
@ -23,8 +23,8 @@ func init() {
|
||||
var _ metadata.ArtistBiographyProvider = (*minimalPlugin)(nil)
|
||||
|
||||
// GetArtistBiography returns a placeholder biography for the artist.
|
||||
func (p *minimalPlugin) GetArtistBiography(input metadata.ArtistInput) (metadata.ArtistBiographyOutput, error) {
|
||||
return metadata.ArtistBiographyOutput{
|
||||
func (p *minimalPlugin) GetArtistBiography(input metadata.ArtistRequest) (metadata.ArtistBiographyResponse, error) {
|
||||
return metadata.ArtistBiographyResponse{
|
||||
Biography: "This is a placeholder biography for " + input.Name + ".",
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -232,23 +232,14 @@ func extractPageTitleFromURL(wikiURL string) (string, error) {
|
||||
return decodedTitle, nil
|
||||
}
|
||||
|
||||
// getMBID extracts the MBID from an optional pointer
|
||||
func getMBID(mbid *string) string {
|
||||
if mbid == nil {
|
||||
return ""
|
||||
}
|
||||
return *mbid
|
||||
}
|
||||
|
||||
// GetArtistURL returns the Wikipedia URL for an artist
|
||||
func (*wikimediaPlugin) GetArtistURL(input metadata.ArtistInput) (metadata.ArtistURLOutput, error) {
|
||||
mbid := getMBID(input.MBID)
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("GetArtistURL: name=%s, mbid=%s", input.Name, mbid))
|
||||
func (*wikimediaPlugin) GetArtistURL(input metadata.ArtistRequest) (metadata.ArtistURLResponse, error) {
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("GetArtistURL: name=%s, mbid=%s", input.Name, input.MBID))
|
||||
|
||||
// 1. Try Wikidata (MBID first, then name)
|
||||
wikiURL, err := getWikidataWikipediaURL(mbid, input.Name)
|
||||
wikiURL, err := getWikidataWikipediaURL(input.MBID, input.Name)
|
||||
if err == nil && wikiURL != "" {
|
||||
return metadata.ArtistURLOutput{URL: wikiURL}, nil
|
||||
return metadata.ArtistURLResponse{URL: wikiURL}, nil
|
||||
}
|
||||
if err != nil {
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("Wikidata URL failed: %v", err))
|
||||
@ -258,7 +249,7 @@ func (*wikimediaPlugin) GetArtistURL(input metadata.ArtistInput) (metadata.Artis
|
||||
if input.Name != "" {
|
||||
wikiURL, err = getDBpediaWikipediaURL(input.Name)
|
||||
if err == nil && wikiURL != "" {
|
||||
return metadata.ArtistURLOutput{URL: wikiURL}, nil
|
||||
return metadata.ArtistURLResponse{URL: wikiURL}, nil
|
||||
}
|
||||
if err != nil {
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("DBpedia URL failed: %v", err))
|
||||
@ -269,20 +260,19 @@ func (*wikimediaPlugin) GetArtistURL(input metadata.ArtistInput) (metadata.Artis
|
||||
if input.Name != "" {
|
||||
searchURL := fmt.Sprintf("https://en.wikipedia.org/w/index.php?search=%s", url.QueryEscape(input.Name))
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("URL not found, falling back to search URL: %s", searchURL))
|
||||
return metadata.ArtistURLOutput{URL: searchURL}, nil
|
||||
return metadata.ArtistURLResponse{URL: searchURL}, nil
|
||||
}
|
||||
|
||||
return metadata.ArtistURLOutput{}, errors.New("could not determine Wikipedia URL")
|
||||
return metadata.ArtistURLResponse{}, errors.New("could not determine Wikipedia URL")
|
||||
}
|
||||
|
||||
// GetArtistBiography returns the biography for an artist from Wikipedia
|
||||
func (*wikimediaPlugin) GetArtistBiography(input metadata.ArtistInput) (metadata.ArtistBiographyOutput, error) {
|
||||
mbid := getMBID(input.MBID)
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("GetArtistBiography: name=%s, mbid=%s", input.Name, mbid))
|
||||
func (*wikimediaPlugin) GetArtistBiography(input metadata.ArtistRequest) (metadata.ArtistBiographyResponse, error) {
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("GetArtistBiography: name=%s, mbid=%s", input.Name, input.MBID))
|
||||
|
||||
// 1. Get Wikipedia URL (using the logic from GetArtistURL)
|
||||
wikiURL := ""
|
||||
tempURL, wdErr := getWikidataWikipediaURL(mbid, input.Name)
|
||||
tempURL, wdErr := getWikidataWikipediaURL(input.MBID, input.Name)
|
||||
if wdErr == nil && tempURL != "" {
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("Found Wikidata URL: %s", tempURL))
|
||||
wikiURL = tempURL
|
||||
@ -305,7 +295,7 @@ func (*wikimediaPlugin) GetArtistBiography(input metadata.ArtistInput) (metadata
|
||||
bio, err := getWikipediaExtract(pageTitle)
|
||||
if err == nil && bio != "" {
|
||||
pdk.Log(pdk.LogDebug, "Found Wikipedia extract")
|
||||
return metadata.ArtistBiographyOutput{Biography: bio}, nil
|
||||
return metadata.ArtistBiographyResponse{Biography: bio}, nil
|
||||
}
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("Wikipedia extract failed: %v", err))
|
||||
} else {
|
||||
@ -319,43 +309,42 @@ func (*wikimediaPlugin) GetArtistBiography(input metadata.ArtistInput) (metadata
|
||||
bio, err := getDBpediaComment(input.Name)
|
||||
if err == nil && bio != "" {
|
||||
pdk.Log(pdk.LogDebug, "Found DBpedia comment")
|
||||
return metadata.ArtistBiographyOutput{Biography: bio}, nil
|
||||
return metadata.ArtistBiographyResponse{Biography: bio}, nil
|
||||
}
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("DBpedia comment failed: %v", err))
|
||||
}
|
||||
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("Biography not found for: %s (%s)", input.Name, mbid))
|
||||
return metadata.ArtistBiographyOutput{}, errors.New("biography not found")
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("Biography not found for: %s (%s)", input.Name, input.MBID))
|
||||
return metadata.ArtistBiographyResponse{}, errors.New("biography not found")
|
||||
}
|
||||
|
||||
// GetArtistImages returns artist images from Wikidata
|
||||
func (*wikimediaPlugin) GetArtistImages(input metadata.ArtistInput) (metadata.ArtistImagesOutput, error) {
|
||||
mbid := getMBID(input.MBID)
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("GetArtistImages: name=%s, mbid=%s", input.Name, mbid))
|
||||
func (*wikimediaPlugin) GetArtistImages(input metadata.ArtistRequest) (metadata.ArtistImagesResponse, error) {
|
||||
pdk.Log(pdk.LogDebug, fmt.Sprintf("GetArtistImages: name=%s, mbid=%s", input.Name, input.MBID))
|
||||
|
||||
var q string
|
||||
if mbid != "" {
|
||||
q = fmt.Sprintf(`SELECT ?img WHERE { ?artist wdt:P434 "%s"; wdt:P18 ?img } LIMIT 1`, mbid)
|
||||
if input.MBID != "" {
|
||||
q = fmt.Sprintf(`SELECT ?img WHERE { ?artist wdt:P434 "%s"; wdt:P18 ?img } LIMIT 1`, input.MBID)
|
||||
} else if input.Name != "" {
|
||||
escapedName := strings.ReplaceAll(input.Name, "\"", "\\\"")
|
||||
q = fmt.Sprintf(`SELECT ?img WHERE { ?artist rdfs:label "%s"@en; wdt:P18 ?img } LIMIT 1`, escapedName)
|
||||
} else {
|
||||
return metadata.ArtistImagesOutput{}, errors.New("MBID or Name required for Wikidata Image lookup")
|
||||
return metadata.ArtistImagesResponse{}, errors.New("MBID or Name required for Wikidata Image lookup")
|
||||
}
|
||||
|
||||
result, err := sparqlQuery(wikidataEndpoint, q)
|
||||
if err != nil {
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("Image not found for: %s (%s)", input.Name, mbid))
|
||||
return metadata.ArtistImagesOutput{}, errors.New("image not found")
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("Image not found for: %s (%s)", input.Name, input.MBID))
|
||||
return metadata.ArtistImagesResponse{}, errors.New("image not found")
|
||||
}
|
||||
if result.Results.Bindings[0].Img != nil {
|
||||
return metadata.ArtistImagesOutput{
|
||||
return metadata.ArtistImagesResponse{
|
||||
Images: []metadata.ImageInfo{{URL: result.Results.Bindings[0].Img.Value, Size: 0}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("Image not found for: %s (%s)", input.Name, mbid))
|
||||
return metadata.ArtistImagesOutput{}, errors.New("image not found")
|
||||
pdk.Log(pdk.LogInfo, fmt.Sprintf("Image not found for: %s (%s)", input.Name, input.MBID))
|
||||
return metadata.ArtistImagesResponse{}, errors.New("image not found")
|
||||
}
|
||||
|
||||
// Required main function - init() handles registration
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/model/id"
|
||||
"github.com/navidrome/navidrome/plugins/capabilities"
|
||||
"github.com/navidrome/navidrome/plugins/host"
|
||||
"github.com/navidrome/navidrome/scheduler"
|
||||
)
|
||||
@ -162,18 +163,6 @@ func (s *schedulerServiceImpl) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// schedulerCallbackInput is the input format for the nd_scheduler_callback function.
|
||||
type schedulerCallbackInput struct {
|
||||
ScheduleID string `json:"scheduleId"`
|
||||
Payload string `json:"payload"`
|
||||
IsRecurring bool `json:"isRecurring"`
|
||||
}
|
||||
|
||||
// schedulerCallbackOutput is the output format for the nd_scheduler_callback function.
|
||||
type schedulerCallbackOutput struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// invokeCallback calls the plugin's nd_scheduler_callback function.
|
||||
func (s *schedulerServiceImpl) invokeCallback(ctx context.Context, scheduleID string) {
|
||||
log.Debug(ctx, "Scheduler callback invoked", "plugin", s.pluginName, "scheduleID", scheduleID)
|
||||
@ -206,14 +195,14 @@ func (s *schedulerServiceImpl) invokeCallback(ctx context.Context, scheduleID st
|
||||
}
|
||||
|
||||
// Prepare callback input
|
||||
input := schedulerCallbackInput{
|
||||
input := capabilities.SchedulerCallbackRequest{
|
||||
ScheduleID: scheduleID,
|
||||
Payload: payload,
|
||||
IsRecurring: isRecurring,
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
result, err := callPluginFunction[schedulerCallbackInput, schedulerCallbackOutput](ctx, instance, FuncSchedulerCallback, input)
|
||||
result, err := callPluginFunction[capabilities.SchedulerCallbackRequest, capabilities.SchedulerCallbackResponse](ctx, instance, FuncSchedulerCallback, input)
|
||||
if err != nil {
|
||||
log.Error(ctx, "Scheduler callback failed", "plugin", s.pluginName, "scheduleID", scheduleID, "duration", time.Since(start), err)
|
||||
return
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/model/id"
|
||||
"github.com/navidrome/navidrome/plugins/capabilities"
|
||||
"github.com/navidrome/navidrome/plugins/host"
|
||||
)
|
||||
|
||||
@ -317,44 +318,19 @@ func (s *webSocketServiceImpl) readLoop(ctx context.Context, connectionID string
|
||||
}
|
||||
}
|
||||
|
||||
// Callback input/output types
|
||||
|
||||
type onTextMessageInput struct {
|
||||
ConnectionID string `json:"connectionId"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type onBinaryMessageInput struct {
|
||||
ConnectionID string `json:"connectionId"`
|
||||
Data string `json:"data"` // base64 encoded
|
||||
}
|
||||
|
||||
type onErrorInput struct {
|
||||
ConnectionID string `json:"connectionId"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type onCloseInput struct {
|
||||
ConnectionID string `json:"connectionId"`
|
||||
Code int32 `json:"code"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
type emptyOutput struct{}
|
||||
|
||||
func (s *webSocketServiceImpl) invokeOnTextMessage(ctx context.Context, connectionID, message string) {
|
||||
instance := s.getPluginInstance()
|
||||
if instance == nil {
|
||||
return
|
||||
}
|
||||
|
||||
input := onTextMessageInput{
|
||||
input := capabilities.OnTextMessageRequest{
|
||||
ConnectionID: connectionID,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
_, err := callPluginFunction[onTextMessageInput, emptyOutput](ctx, instance, FuncWebSocketOnTextMessage, input)
|
||||
_, err := callPluginFunction[capabilities.OnTextMessageRequest, capabilities.OnTextMessageResponse](ctx, instance, FuncWebSocketOnTextMessage, input)
|
||||
if err != nil {
|
||||
// Don't log error if function simply doesn't exist (optional callback)
|
||||
if !errors.Is(errFunctionNotFound, err) {
|
||||
@ -369,13 +345,13 @@ func (s *webSocketServiceImpl) invokeOnBinaryMessage(ctx context.Context, connec
|
||||
return
|
||||
}
|
||||
|
||||
input := onBinaryMessageInput{
|
||||
input := capabilities.OnBinaryMessageRequest{
|
||||
ConnectionID: connectionID,
|
||||
Data: base64.StdEncoding.EncodeToString(data),
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
_, err := callPluginFunction[onBinaryMessageInput, emptyOutput](ctx, instance, FuncWebSocketOnBinaryMessage, input)
|
||||
_, err := callPluginFunction[capabilities.OnBinaryMessageRequest, capabilities.OnBinaryMessageResponse](ctx, instance, FuncWebSocketOnBinaryMessage, input)
|
||||
if err != nil {
|
||||
// Don't log error if function simply doesn't exist (optional callback)
|
||||
if !errors.Is(errFunctionNotFound, err) {
|
||||
@ -390,13 +366,13 @@ func (s *webSocketServiceImpl) invokeOnError(ctx context.Context, connectionID,
|
||||
return
|
||||
}
|
||||
|
||||
input := onErrorInput{
|
||||
input := capabilities.OnErrorRequest{
|
||||
ConnectionID: connectionID,
|
||||
Error: errorMsg,
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
_, err := callPluginFunction[onErrorInput, emptyOutput](ctx, instance, FuncWebSocketOnError, input)
|
||||
_, err := callPluginFunction[capabilities.OnErrorRequest, capabilities.OnErrorResponse](ctx, instance, FuncWebSocketOnError, input)
|
||||
if err != nil {
|
||||
// Don't log error if function simply doesn't exist (optional callback)
|
||||
if !errors.Is(errFunctionNotFound, err) {
|
||||
@ -411,14 +387,14 @@ func (s *webSocketServiceImpl) invokeOnClose(ctx context.Context, connectionID s
|
||||
return
|
||||
}
|
||||
|
||||
input := onCloseInput{
|
||||
input := capabilities.OnCloseRequest{
|
||||
ConnectionID: connectionID,
|
||||
Code: code,
|
||||
Reason: reason,
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
_, err := callPluginFunction[onCloseInput, emptyOutput](ctx, instance, FuncWebSocketOnClose, input)
|
||||
_, err := callPluginFunction[capabilities.OnCloseRequest, capabilities.OnCloseResponse](ctx, instance, FuncWebSocketOnClose, input)
|
||||
if err != nil {
|
||||
// Don't log error if function simply doesn't exist (optional callback)
|
||||
if !errors.Is(errFunctionNotFound, err) {
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/navidrome/navidrome/core/agents"
|
||||
"github.com/navidrome/navidrome/plugins/capabilities"
|
||||
)
|
||||
|
||||
// CapabilityMetadataAgent indicates the plugin can provide artist/album metadata.
|
||||
@ -53,8 +54,8 @@ 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 := artistMBIDInput{ID: id, Name: name}
|
||||
result, err := callPluginFunction[artistMBIDInput, artistMBIDOutput](ctx, a.plugin, FuncGetArtistMBID, input)
|
||||
input := capabilities.ArtistMBIDRequest{ID: id, Name: name}
|
||||
result, err := callPluginFunction[capabilities.ArtistMBIDRequest, capabilities.ArtistMBIDResponse](ctx, a.plugin, FuncGetArtistMBID, input)
|
||||
if err != nil {
|
||||
return "", errors.Join(agents.ErrNotFound, err)
|
||||
}
|
||||
@ -68,8 +69,8 @@ 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 := artistInput{ID: id, Name: name, MBID: mbid}
|
||||
result, err := callPluginFunction[artistInput, artistURLOutput](ctx, a.plugin, FuncGetArtistURL, input)
|
||||
input := capabilities.ArtistRequest{ID: id, Name: name, MBID: mbid}
|
||||
result, err := callPluginFunction[capabilities.ArtistRequest, capabilities.ArtistURLResponse](ctx, a.plugin, FuncGetArtistURL, input)
|
||||
if err != nil {
|
||||
return "", errors.Join(agents.ErrNotFound, err)
|
||||
}
|
||||
@ -81,8 +82,8 @@ 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 := artistInput{ID: id, Name: name, MBID: mbid}
|
||||
result, err := callPluginFunction[artistInput, artistBiographyOutput](ctx, a.plugin, FuncGetArtistBiography, input)
|
||||
input := capabilities.ArtistRequest{ID: id, Name: name, MBID: mbid}
|
||||
result, err := callPluginFunction[capabilities.ArtistRequest, capabilities.ArtistBiographyResponse](ctx, a.plugin, FuncGetArtistBiography, input)
|
||||
if err != nil {
|
||||
return "", errors.Join(agents.ErrNotFound, err)
|
||||
}
|
||||
@ -96,8 +97,8 @@ 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 := similarArtistsInput{ID: id, Name: name, MBID: mbid, Limit: limit}
|
||||
result, err := callPluginFunction[similarArtistsInput, similarArtistsOutput](ctx, a.plugin, FuncGetSimilarArtists, input)
|
||||
input := capabilities.SimilarArtistsRequest{ID: id, Name: name, MBID: mbid, Limit: int32(limit)}
|
||||
result, err := callPluginFunction[capabilities.SimilarArtistsRequest, capabilities.SimilarArtistsResponse](ctx, a.plugin, FuncGetSimilarArtists, input)
|
||||
if err != nil {
|
||||
return nil, errors.Join(agents.ErrNotFound, err)
|
||||
}
|
||||
@ -107,8 +108,8 @@ func (a *MetadataAgent) GetSimilarArtists(ctx context.Context, id, name, mbid st
|
||||
}
|
||||
|
||||
artists := make([]agents.Artist, len(result.Artists))
|
||||
for i, a := range result.Artists {
|
||||
artists[i] = agents.Artist{Name: a.Name, MBID: a.MBID}
|
||||
for i, ar := range result.Artists {
|
||||
artists[i] = agents.Artist{Name: ar.Name, MBID: ar.MBID}
|
||||
}
|
||||
|
||||
return artists, nil
|
||||
@ -116,8 +117,8 @@ 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 := artistInput{ID: id, Name: name, MBID: mbid}
|
||||
result, err := callPluginFunction[artistInput, artistImagesOutput](ctx, a.plugin, FuncGetArtistImages, input)
|
||||
input := capabilities.ArtistRequest{ID: id, Name: name, MBID: mbid}
|
||||
result, err := callPluginFunction[capabilities.ArtistRequest, capabilities.ArtistImagesResponse](ctx, a.plugin, FuncGetArtistImages, input)
|
||||
if err != nil {
|
||||
return nil, errors.Join(agents.ErrNotFound, err)
|
||||
}
|
||||
@ -128,7 +129,7 @@ func (a *MetadataAgent) GetArtistImages(ctx context.Context, id, name, mbid stri
|
||||
|
||||
images := make([]agents.ExternalImage, len(result.Images))
|
||||
for i, img := range result.Images {
|
||||
images[i] = agents.ExternalImage{URL: img.URL, Size: img.Size}
|
||||
images[i] = agents.ExternalImage{URL: img.URL, Size: int(img.Size)}
|
||||
}
|
||||
|
||||
return images, nil
|
||||
@ -136,8 +137,8 @@ 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 := topSongsInput{ID: id, Name: artistName, MBID: mbid, Count: count}
|
||||
result, err := callPluginFunction[topSongsInput, topSongsOutput](ctx, a.plugin, FuncGetArtistTopSongs, input)
|
||||
input := capabilities.TopSongsRequest{ID: id, Name: artistName, MBID: mbid, Count: int32(count)}
|
||||
result, err := callPluginFunction[capabilities.TopSongsRequest, capabilities.TopSongsResponse](ctx, a.plugin, FuncGetArtistTopSongs, input)
|
||||
if err != nil {
|
||||
return nil, errors.Join(agents.ErrNotFound, err)
|
||||
}
|
||||
@ -156,8 +157,8 @@ 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 := albumInput{Name: name, Artist: artist, MBID: mbid}
|
||||
result, err := callPluginFunction[albumInput, albumInfoOutput](ctx, a.plugin, FuncGetAlbumInfo, input)
|
||||
input := capabilities.AlbumRequest{Name: name, Artist: artist, MBID: mbid}
|
||||
result, err := callPluginFunction[capabilities.AlbumRequest, capabilities.AlbumInfoResponse](ctx, a.plugin, FuncGetAlbumInfo, input)
|
||||
if err != nil {
|
||||
return nil, errors.Join(agents.ErrNotFound, err)
|
||||
}
|
||||
@ -172,8 +173,8 @@ 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 := albumInput{Name: name, Artist: artist, MBID: mbid}
|
||||
result, err := callPluginFunction[albumInput, albumImagesOutput](ctx, a.plugin, FuncGetAlbumImages, input)
|
||||
input := capabilities.AlbumRequest{Name: name, Artist: artist, MBID: mbid}
|
||||
result, err := callPluginFunction[capabilities.AlbumRequest, capabilities.AlbumImagesResponse](ctx, a.plugin, FuncGetAlbumImages, input)
|
||||
if err != nil {
|
||||
return nil, errors.Join(agents.ErrNotFound, err)
|
||||
}
|
||||
@ -184,7 +185,7 @@ func (a *MetadataAgent) GetAlbumImages(ctx context.Context, name, artist, mbid s
|
||||
|
||||
images := make([]agents.ExternalImage, len(result.Images))
|
||||
for i, img := range result.Images {
|
||||
images[i] = agents.ExternalImage{URL: img.URL, Size: img.Size}
|
||||
images[i] = agents.ExternalImage{URL: img.URL, Size: int(img.Size)}
|
||||
}
|
||||
|
||||
return images, nil
|
||||
|
||||
@ -1,100 +0,0 @@
|
||||
package plugins
|
||||
|
||||
// --- Input/Output JSON structures for MetadataAgent plugin calls ---
|
||||
|
||||
// artistMBIDInput is the input for GetArtistMBID
|
||||
type artistMBIDInput struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// artistMBIDOutput is the output for GetArtistMBID
|
||||
type artistMBIDOutput struct {
|
||||
MBID string `json:"mbid"`
|
||||
}
|
||||
|
||||
// artistInput is the common input for artist-related functions
|
||||
type artistInput struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// artistURLOutput is the output for GetArtistURL
|
||||
type artistURLOutput struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// artistBiographyOutput is the output for GetArtistBiography
|
||||
type artistBiographyOutput struct {
|
||||
Biography string `json:"biography"`
|
||||
}
|
||||
|
||||
// similarArtistsInput is the input for GetSimilarArtists
|
||||
type similarArtistsInput struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
// artistRef is a reference to an artist with name and optional MBID
|
||||
type artistRef struct {
|
||||
Name string `json:"name"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// similarArtistsOutput is the output for GetSimilarArtists
|
||||
type similarArtistsOutput struct {
|
||||
Artists []artistRef `json:"artists"`
|
||||
}
|
||||
|
||||
// imageInfo represents an image with URL and size
|
||||
type imageInfo struct {
|
||||
URL string `json:"url"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
// artistImagesOutput is the output for GetArtistImages
|
||||
type artistImagesOutput struct {
|
||||
Images []imageInfo `json:"images"`
|
||||
}
|
||||
|
||||
// topSongsInput is the input for GetArtistTopSongs
|
||||
type topSongsInput struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// songRef is a reference to a song with name and optional MBID
|
||||
type songRef struct {
|
||||
Name string `json:"name"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// topSongsOutput is the output for GetArtistTopSongs
|
||||
type topSongsOutput struct {
|
||||
Songs []songRef `json:"songs"`
|
||||
}
|
||||
|
||||
// albumInput is the common input for album-related functions
|
||||
type albumInput struct {
|
||||
Name string `json:"name"`
|
||||
Artist string `json:"artist"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// albumInfoOutput is the output for GetAlbumInfo
|
||||
type albumInfoOutput struct {
|
||||
Name string `json:"name"`
|
||||
MBID string `json:"mbid"`
|
||||
Description string `json:"description"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// albumImagesOutput is the output for GetAlbumImages
|
||||
type albumImagesOutput struct {
|
||||
Images []imageInfo `json:"images"`
|
||||
}
|
||||
@ -11,17 +11,17 @@ import (
|
||||
pdk "github.com/extism/go-pdk"
|
||||
)
|
||||
|
||||
// OnInitInput is the input provided to the init callback.
|
||||
// InitRequest is the request provided to the init callback.
|
||||
// Currently empty, reserved for future use.
|
||||
type OnInitInput struct {
|
||||
type InitRequest struct {
|
||||
}
|
||||
|
||||
// OnInitOutput is the output from the init callback.
|
||||
type OnInitOutput struct {
|
||||
// InitResponse is the response from the init callback.
|
||||
type InitResponse struct {
|
||||
// Error is the error message if initialization failed.
|
||||
// Empty or null indicates success.
|
||||
// Empty string indicates success.
|
||||
// The error is logged but does not prevent the plugin from being loaded.
|
||||
Error *string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Lifecycle is the marker interface for lifecycle plugins.
|
||||
@ -38,10 +38,10 @@ type Lifecycle interface{}
|
||||
|
||||
// InitProvider provides the OnInit function.
|
||||
type InitProvider interface {
|
||||
OnInit(OnInitInput) (OnInitOutput, error)
|
||||
OnInit(InitRequest) (InitResponse, error)
|
||||
} // Internal implementation holders
|
||||
var (
|
||||
initImpl func(OnInitInput) (OnInitOutput, error)
|
||||
initImpl func(InitRequest) (InitResponse, error)
|
||||
)
|
||||
|
||||
// Register registers a lifecycle implementation.
|
||||
@ -63,7 +63,7 @@ func _NdOnInit() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input OnInitInput
|
||||
var input InitRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
|
||||
@ -8,17 +8,17 @@
|
||||
|
||||
package lifecycle
|
||||
|
||||
// OnInitInput is the input provided to the init callback.
|
||||
// InitRequest is the request provided to the init callback.
|
||||
// Currently empty, reserved for future use.
|
||||
type OnInitInput struct {
|
||||
type InitRequest struct {
|
||||
}
|
||||
|
||||
// OnInitOutput is the output from the init callback.
|
||||
type OnInitOutput struct {
|
||||
// InitResponse is the response from the init callback.
|
||||
type InitResponse struct {
|
||||
// Error is the error message if initialization failed.
|
||||
// Empty or null indicates success.
|
||||
// Empty string indicates success.
|
||||
// The error is logged but does not prevent the plugin from being loaded.
|
||||
Error *string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Lifecycle is the marker interface for lifecycle plugins.
|
||||
@ -35,7 +35,7 @@ type Lifecycle interface{}
|
||||
|
||||
// InitProvider provides the OnInit function.
|
||||
type InitProvider interface {
|
||||
OnInit(OnInitInput) (OnInitOutput, error)
|
||||
OnInit(InitRequest) (InitResponse, error)
|
||||
}
|
||||
|
||||
// NotImplementedCode is the standard return code for unimplemented functions.
|
||||
|
||||
@ -11,22 +11,24 @@ import (
|
||||
pdk "github.com/extism/go-pdk"
|
||||
)
|
||||
|
||||
// ArtistMBIDInput is the input for GetArtistMBID.
|
||||
type ArtistMBIDInput struct {
|
||||
// ArtistRequest is the common request for artist-related functions.
|
||||
type ArtistRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// ArtistURLOutput is the output for GetArtistURL.
|
||||
type ArtistURLOutput struct {
|
||||
// URL is the external URL for the artist.
|
||||
URL string `json:"url"`
|
||||
// ArtistBiographyResponse is the response for GetArtistBiography.
|
||||
type ArtistBiographyResponse struct {
|
||||
// Biography is the artist biography text.
|
||||
Biography string `json:"biography"`
|
||||
}
|
||||
|
||||
// AlbumInfoOutput is the output for GetAlbumInfo.
|
||||
type AlbumInfoOutput struct {
|
||||
// AlbumInfoResponse is the response for GetAlbumInfo.
|
||||
type AlbumInfoResponse struct {
|
||||
// Name is the album name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the album.
|
||||
@ -37,10 +39,38 @@ type AlbumInfoOutput struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// AlbumImagesOutput is the output for GetAlbumImages.
|
||||
type AlbumImagesOutput struct {
|
||||
// Images is the list of album images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
// ArtistMBIDRequest is the request for GetArtistMBID.
|
||||
type ArtistMBIDRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// TopSongsResponse is the response for GetArtistTopSongs.
|
||||
type TopSongsResponse struct {
|
||||
// Songs is the list of top songs.
|
||||
Songs []SongRef `json:"songs"`
|
||||
}
|
||||
|
||||
// ArtistRef is a reference to an artist with name and optional MBID.
|
||||
type ArtistRef struct {
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// ArtistMBIDResponse is the response for GetArtistMBID.
|
||||
type ArtistMBIDResponse struct {
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID string `json:"mbid"`
|
||||
}
|
||||
|
||||
// SimilarArtistsResponse is the response for GetSimilarArtists.
|
||||
type SimilarArtistsResponse struct {
|
||||
// Artists is the list of similar artists.
|
||||
Artists []ArtistRef `json:"artists"`
|
||||
}
|
||||
|
||||
// SongRef is a reference to a song with name and optional MBID.
|
||||
@ -48,7 +78,7 @@ type SongRef struct {
|
||||
// Name is the song name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the song.
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// ImageInfo represents an image with URL and size.
|
||||
@ -59,86 +89,56 @@ type ImageInfo struct {
|
||||
Size int32 `json:"size"`
|
||||
}
|
||||
|
||||
// ArtistBiographyOutput is the output for GetArtistBiography.
|
||||
type ArtistBiographyOutput struct {
|
||||
// Biography is the artist biography text.
|
||||
Biography string `json:"biography"`
|
||||
// ArtistURLResponse is the response for GetArtistURL.
|
||||
type ArtistURLResponse struct {
|
||||
// URL is the external URL for the artist.
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// SimilarArtistsOutput is the output for GetSimilarArtists.
|
||||
type SimilarArtistsOutput struct {
|
||||
// Artists is the list of similar artists.
|
||||
Artists []ArtistRef `json:"artists"`
|
||||
}
|
||||
|
||||
// TopSongsInput is the input for GetArtistTopSongs.
|
||||
type TopSongsInput struct {
|
||||
// SimilarArtistsRequest is the request for GetSimilarArtists.
|
||||
type SimilarArtistsRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
// Count is the maximum number of top songs to return.
|
||||
Count int32 `json:"count"`
|
||||
}
|
||||
|
||||
// TopSongsOutput is the output for GetArtistTopSongs.
|
||||
type TopSongsOutput struct {
|
||||
// Songs is the list of top songs.
|
||||
Songs []SongRef `json:"songs"`
|
||||
}
|
||||
|
||||
// ArtistRef is a reference to an artist with name and optional MBID.
|
||||
type ArtistRef struct {
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// ArtistImagesOutput is the output for GetArtistImages.
|
||||
type ArtistImagesOutput struct {
|
||||
// Images is the list of artist images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
}
|
||||
|
||||
// ArtistMBIDOutput is the output for GetArtistMBID.
|
||||
type ArtistMBIDOutput struct {
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID string `json:"mbid"`
|
||||
}
|
||||
|
||||
// ArtistInput is the common input for artist-related functions.
|
||||
type ArtistInput struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// SimilarArtistsInput is the input for GetSimilarArtists.
|
||||
type SimilarArtistsInput struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
// Limit is the maximum number of similar artists to return.
|
||||
Limit int32 `json:"limit"`
|
||||
}
|
||||
|
||||
// AlbumInput is the common input for album-related functions.
|
||||
type AlbumInput struct {
|
||||
// ArtistImagesResponse is the response for GetArtistImages.
|
||||
type ArtistImagesResponse struct {
|
||||
// Images is the list of artist images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
}
|
||||
|
||||
// TopSongsRequest is the request for GetArtistTopSongs.
|
||||
type TopSongsRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
// Count is the maximum number of top songs to return.
|
||||
Count int32 `json:"count"`
|
||||
}
|
||||
|
||||
// AlbumRequest is the common request for album-related functions.
|
||||
type AlbumRequest struct {
|
||||
// Name is the album name.
|
||||
Name string `json:"name"`
|
||||
// Artist is the album artist name.
|
||||
Artist string `json:"artist"`
|
||||
// MBID is the MusicBrainz ID for the album (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// AlbumImagesResponse is the response for GetAlbumImages.
|
||||
type AlbumImagesResponse struct {
|
||||
// Images is the list of album images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
}
|
||||
|
||||
// Metadata is the marker interface for metadata plugins.
|
||||
@ -153,52 +153,52 @@ type Metadata interface{}
|
||||
|
||||
// ArtistMBIDProvider provides the GetArtistMBID function.
|
||||
type ArtistMBIDProvider interface {
|
||||
GetArtistMBID(ArtistMBIDInput) (ArtistMBIDOutput, error)
|
||||
GetArtistMBID(ArtistMBIDRequest) (ArtistMBIDResponse, error)
|
||||
}
|
||||
|
||||
// ArtistURLProvider provides the GetArtistURL function.
|
||||
type ArtistURLProvider interface {
|
||||
GetArtistURL(ArtistInput) (ArtistURLOutput, error)
|
||||
GetArtistURL(ArtistRequest) (ArtistURLResponse, error)
|
||||
}
|
||||
|
||||
// ArtistBiographyProvider provides the GetArtistBiography function.
|
||||
type ArtistBiographyProvider interface {
|
||||
GetArtistBiography(ArtistInput) (ArtistBiographyOutput, error)
|
||||
GetArtistBiography(ArtistRequest) (ArtistBiographyResponse, error)
|
||||
}
|
||||
|
||||
// SimilarArtistsProvider provides the GetSimilarArtists function.
|
||||
type SimilarArtistsProvider interface {
|
||||
GetSimilarArtists(SimilarArtistsInput) (SimilarArtistsOutput, error)
|
||||
GetSimilarArtists(SimilarArtistsRequest) (SimilarArtistsResponse, error)
|
||||
}
|
||||
|
||||
// ArtistImagesProvider provides the GetArtistImages function.
|
||||
type ArtistImagesProvider interface {
|
||||
GetArtistImages(ArtistInput) (ArtistImagesOutput, error)
|
||||
GetArtistImages(ArtistRequest) (ArtistImagesResponse, error)
|
||||
}
|
||||
|
||||
// ArtistTopSongsProvider provides the GetArtistTopSongs function.
|
||||
type ArtistTopSongsProvider interface {
|
||||
GetArtistTopSongs(TopSongsInput) (TopSongsOutput, error)
|
||||
GetArtistTopSongs(TopSongsRequest) (TopSongsResponse, error)
|
||||
}
|
||||
|
||||
// AlbumInfoProvider provides the GetAlbumInfo function.
|
||||
type AlbumInfoProvider interface {
|
||||
GetAlbumInfo(AlbumInput) (AlbumInfoOutput, error)
|
||||
GetAlbumInfo(AlbumRequest) (AlbumInfoResponse, error)
|
||||
}
|
||||
|
||||
// AlbumImagesProvider provides the GetAlbumImages function.
|
||||
type AlbumImagesProvider interface {
|
||||
GetAlbumImages(AlbumInput) (AlbumImagesOutput, error)
|
||||
GetAlbumImages(AlbumRequest) (AlbumImagesResponse, error)
|
||||
} // Internal implementation holders
|
||||
var (
|
||||
artistMBIDImpl func(ArtistMBIDInput) (ArtistMBIDOutput, error)
|
||||
artistURLImpl func(ArtistInput) (ArtistURLOutput, error)
|
||||
artistBiographyImpl func(ArtistInput) (ArtistBiographyOutput, error)
|
||||
similarArtistsImpl func(SimilarArtistsInput) (SimilarArtistsOutput, error)
|
||||
artistImagesImpl func(ArtistInput) (ArtistImagesOutput, error)
|
||||
artistTopSongsImpl func(TopSongsInput) (TopSongsOutput, error)
|
||||
albumInfoImpl func(AlbumInput) (AlbumInfoOutput, error)
|
||||
albumImagesImpl func(AlbumInput) (AlbumImagesOutput, 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.
|
||||
@ -241,7 +241,7 @@ func _NdGetArtistMbid() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input ArtistMBIDInput
|
||||
var input ArtistMBIDRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -268,7 +268,7 @@ func _NdGetArtistUrl() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input ArtistInput
|
||||
var input ArtistRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -295,7 +295,7 @@ func _NdGetArtistBiography() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input ArtistInput
|
||||
var input ArtistRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -322,7 +322,7 @@ func _NdGetSimilarArtists() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input SimilarArtistsInput
|
||||
var input SimilarArtistsRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -349,7 +349,7 @@ func _NdGetArtistImages() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input ArtistInput
|
||||
var input ArtistRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -376,7 +376,7 @@ func _NdGetArtistTopSongs() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input TopSongsInput
|
||||
var input TopSongsRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -403,7 +403,7 @@ func _NdGetAlbumInfo() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input AlbumInput
|
||||
var input AlbumRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -430,7 +430,7 @@ func _NdGetAlbumImages() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input AlbumInput
|
||||
var input AlbumRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
|
||||
@ -8,22 +8,24 @@
|
||||
|
||||
package metadata
|
||||
|
||||
// ArtistMBIDInput is the input for GetArtistMBID.
|
||||
type ArtistMBIDInput struct {
|
||||
// ArtistRequest is the common request for artist-related functions.
|
||||
type ArtistRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// ArtistURLOutput is the output for GetArtistURL.
|
||||
type ArtistURLOutput struct {
|
||||
// URL is the external URL for the artist.
|
||||
URL string `json:"url"`
|
||||
// ArtistBiographyResponse is the response for GetArtistBiography.
|
||||
type ArtistBiographyResponse struct {
|
||||
// Biography is the artist biography text.
|
||||
Biography string `json:"biography"`
|
||||
}
|
||||
|
||||
// AlbumInfoOutput is the output for GetAlbumInfo.
|
||||
type AlbumInfoOutput struct {
|
||||
// AlbumInfoResponse is the response for GetAlbumInfo.
|
||||
type AlbumInfoResponse struct {
|
||||
// Name is the album name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the album.
|
||||
@ -34,10 +36,38 @@ type AlbumInfoOutput struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// AlbumImagesOutput is the output for GetAlbumImages.
|
||||
type AlbumImagesOutput struct {
|
||||
// Images is the list of album images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
// ArtistMBIDRequest is the request for GetArtistMBID.
|
||||
type ArtistMBIDRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// TopSongsResponse is the response for GetArtistTopSongs.
|
||||
type TopSongsResponse struct {
|
||||
// Songs is the list of top songs.
|
||||
Songs []SongRef `json:"songs"`
|
||||
}
|
||||
|
||||
// ArtistRef is a reference to an artist with name and optional MBID.
|
||||
type ArtistRef struct {
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// ArtistMBIDResponse is the response for GetArtistMBID.
|
||||
type ArtistMBIDResponse struct {
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID string `json:"mbid"`
|
||||
}
|
||||
|
||||
// SimilarArtistsResponse is the response for GetSimilarArtists.
|
||||
type SimilarArtistsResponse struct {
|
||||
// Artists is the list of similar artists.
|
||||
Artists []ArtistRef `json:"artists"`
|
||||
}
|
||||
|
||||
// SongRef is a reference to a song with name and optional MBID.
|
||||
@ -45,7 +75,7 @@ type SongRef struct {
|
||||
// Name is the song name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the song.
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// ImageInfo represents an image with URL and size.
|
||||
@ -56,86 +86,56 @@ type ImageInfo struct {
|
||||
Size int32 `json:"size"`
|
||||
}
|
||||
|
||||
// ArtistBiographyOutput is the output for GetArtistBiography.
|
||||
type ArtistBiographyOutput struct {
|
||||
// Biography is the artist biography text.
|
||||
Biography string `json:"biography"`
|
||||
// ArtistURLResponse is the response for GetArtistURL.
|
||||
type ArtistURLResponse struct {
|
||||
// URL is the external URL for the artist.
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// SimilarArtistsOutput is the output for GetSimilarArtists.
|
||||
type SimilarArtistsOutput struct {
|
||||
// Artists is the list of similar artists.
|
||||
Artists []ArtistRef `json:"artists"`
|
||||
}
|
||||
|
||||
// TopSongsInput is the input for GetArtistTopSongs.
|
||||
type TopSongsInput struct {
|
||||
// SimilarArtistsRequest is the request for GetSimilarArtists.
|
||||
type SimilarArtistsRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
// Count is the maximum number of top songs to return.
|
||||
Count int32 `json:"count"`
|
||||
}
|
||||
|
||||
// TopSongsOutput is the output for GetArtistTopSongs.
|
||||
type TopSongsOutput struct {
|
||||
// Songs is the list of top songs.
|
||||
Songs []SongRef `json:"songs"`
|
||||
}
|
||||
|
||||
// ArtistRef is a reference to an artist with name and optional MBID.
|
||||
type ArtistRef struct {
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// ArtistImagesOutput is the output for GetArtistImages.
|
||||
type ArtistImagesOutput struct {
|
||||
// Images is the list of artist images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
}
|
||||
|
||||
// ArtistMBIDOutput is the output for GetArtistMBID.
|
||||
type ArtistMBIDOutput struct {
|
||||
// MBID is the MusicBrainz ID for the artist.
|
||||
MBID string `json:"mbid"`
|
||||
}
|
||||
|
||||
// ArtistInput is the common input for artist-related functions.
|
||||
type ArtistInput struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// SimilarArtistsInput is the input for GetSimilarArtists.
|
||||
type SimilarArtistsInput struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
// Limit is the maximum number of similar artists to return.
|
||||
Limit int32 `json:"limit"`
|
||||
}
|
||||
|
||||
// AlbumInput is the common input for album-related functions.
|
||||
type AlbumInput struct {
|
||||
// ArtistImagesResponse is the response for GetArtistImages.
|
||||
type ArtistImagesResponse struct {
|
||||
// Images is the list of artist images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
}
|
||||
|
||||
// TopSongsRequest is the request for GetArtistTopSongs.
|
||||
type TopSongsRequest struct {
|
||||
// ID is the internal Navidrome artist ID.
|
||||
ID string `json:"id"`
|
||||
// Name is the artist name.
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the artist (if known).
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
// Count is the maximum number of top songs to return.
|
||||
Count int32 `json:"count"`
|
||||
}
|
||||
|
||||
// AlbumRequest is the common request for album-related functions.
|
||||
type AlbumRequest struct {
|
||||
// Name is the album name.
|
||||
Name string `json:"name"`
|
||||
// Artist is the album artist name.
|
||||
Artist string `json:"artist"`
|
||||
// MBID is the MusicBrainz ID for the album (if known).
|
||||
MBID *string `json:"mbid,omitempty"`
|
||||
MBID string `json:"mbid,omitempty"`
|
||||
}
|
||||
|
||||
// AlbumImagesResponse is the response for GetAlbumImages.
|
||||
type AlbumImagesResponse struct {
|
||||
// Images is the list of album images.
|
||||
Images []ImageInfo `json:"images"`
|
||||
}
|
||||
|
||||
// Metadata is the marker interface for metadata plugins.
|
||||
@ -150,42 +150,42 @@ type Metadata interface{}
|
||||
|
||||
// ArtistMBIDProvider provides the GetArtistMBID function.
|
||||
type ArtistMBIDProvider interface {
|
||||
GetArtistMBID(ArtistMBIDInput) (ArtistMBIDOutput, error)
|
||||
GetArtistMBID(ArtistMBIDRequest) (ArtistMBIDResponse, error)
|
||||
}
|
||||
|
||||
// ArtistURLProvider provides the GetArtistURL function.
|
||||
type ArtistURLProvider interface {
|
||||
GetArtistURL(ArtistInput) (ArtistURLOutput, error)
|
||||
GetArtistURL(ArtistRequest) (ArtistURLResponse, error)
|
||||
}
|
||||
|
||||
// ArtistBiographyProvider provides the GetArtistBiography function.
|
||||
type ArtistBiographyProvider interface {
|
||||
GetArtistBiography(ArtistInput) (ArtistBiographyOutput, error)
|
||||
GetArtistBiography(ArtistRequest) (ArtistBiographyResponse, error)
|
||||
}
|
||||
|
||||
// SimilarArtistsProvider provides the GetSimilarArtists function.
|
||||
type SimilarArtistsProvider interface {
|
||||
GetSimilarArtists(SimilarArtistsInput) (SimilarArtistsOutput, error)
|
||||
GetSimilarArtists(SimilarArtistsRequest) (SimilarArtistsResponse, error)
|
||||
}
|
||||
|
||||
// ArtistImagesProvider provides the GetArtistImages function.
|
||||
type ArtistImagesProvider interface {
|
||||
GetArtistImages(ArtistInput) (ArtistImagesOutput, error)
|
||||
GetArtistImages(ArtistRequest) (ArtistImagesResponse, error)
|
||||
}
|
||||
|
||||
// ArtistTopSongsProvider provides the GetArtistTopSongs function.
|
||||
type ArtistTopSongsProvider interface {
|
||||
GetArtistTopSongs(TopSongsInput) (TopSongsOutput, error)
|
||||
GetArtistTopSongs(TopSongsRequest) (TopSongsResponse, error)
|
||||
}
|
||||
|
||||
// AlbumInfoProvider provides the GetAlbumInfo function.
|
||||
type AlbumInfoProvider interface {
|
||||
GetAlbumInfo(AlbumInput) (AlbumInfoOutput, error)
|
||||
GetAlbumInfo(AlbumRequest) (AlbumInfoResponse, error)
|
||||
}
|
||||
|
||||
// AlbumImagesProvider provides the GetAlbumImages function.
|
||||
type AlbumImagesProvider interface {
|
||||
GetAlbumImages(AlbumInput) (AlbumImagesOutput, error)
|
||||
GetAlbumImages(AlbumRequest) (AlbumImagesResponse, error)
|
||||
}
|
||||
|
||||
// NotImplementedCode is the standard return code for unimplemented functions.
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
pdk "github.com/extism/go-pdk"
|
||||
)
|
||||
|
||||
// SchedulerCallbackInput is the input provided when a scheduled task fires.
|
||||
type SchedulerCallbackInput struct {
|
||||
// SchedulerCallbackRequest is the request provided when a scheduled task fires.
|
||||
type SchedulerCallbackRequest struct {
|
||||
// ScheduleID is the unique identifier for this scheduled task.
|
||||
// This is either the ID provided when scheduling, or an auto-generated UUID if none was specified.
|
||||
ScheduleID string `json:"scheduleId"`
|
||||
@ -24,12 +24,12 @@ type SchedulerCallbackInput struct {
|
||||
IsRecurring bool `json:"isRecurring"`
|
||||
}
|
||||
|
||||
// SchedulerCallbackOutput is the output from the scheduler callback.
|
||||
type SchedulerCallbackOutput struct {
|
||||
// SchedulerCallbackResponse is the response from the scheduler callback.
|
||||
type SchedulerCallbackResponse struct {
|
||||
// Error is the error message if the callback failed to process the scheduled task.
|
||||
// Empty or null indicates success. The error is logged but does not
|
||||
// Empty string indicates success. The error is logged but does not
|
||||
// affect the scheduling system.
|
||||
Error *string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Scheduler is the marker interface for scheduler plugins.
|
||||
@ -42,10 +42,10 @@ type Scheduler interface{}
|
||||
|
||||
// SchedulerCallbackProvider provides the OnSchedulerCallback function.
|
||||
type SchedulerCallbackProvider interface {
|
||||
OnSchedulerCallback(SchedulerCallbackInput) (SchedulerCallbackOutput, error)
|
||||
OnSchedulerCallback(SchedulerCallbackRequest) (SchedulerCallbackResponse, error)
|
||||
} // Internal implementation holders
|
||||
var (
|
||||
schedulerCallbackImpl func(SchedulerCallbackInput) (SchedulerCallbackOutput, error)
|
||||
schedulerCallbackImpl func(SchedulerCallbackRequest) (SchedulerCallbackResponse, error)
|
||||
)
|
||||
|
||||
// Register registers a scheduler implementation.
|
||||
@ -67,7 +67,7 @@ func _NdSchedulerCallback() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input SchedulerCallbackInput
|
||||
var input SchedulerCallbackRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
|
||||
package scheduler
|
||||
|
||||
// SchedulerCallbackInput is the input provided when a scheduled task fires.
|
||||
type SchedulerCallbackInput struct {
|
||||
// SchedulerCallbackRequest is the request provided when a scheduled task fires.
|
||||
type SchedulerCallbackRequest struct {
|
||||
// ScheduleID is the unique identifier for this scheduled task.
|
||||
// This is either the ID provided when scheduling, or an auto-generated UUID if none was specified.
|
||||
ScheduleID string `json:"scheduleId"`
|
||||
@ -21,12 +21,12 @@ type SchedulerCallbackInput struct {
|
||||
IsRecurring bool `json:"isRecurring"`
|
||||
}
|
||||
|
||||
// SchedulerCallbackOutput is the output from the scheduler callback.
|
||||
type SchedulerCallbackOutput struct {
|
||||
// SchedulerCallbackResponse is the response from the scheduler callback.
|
||||
type SchedulerCallbackResponse struct {
|
||||
// Error is the error message if the callback failed to process the scheduled task.
|
||||
// Empty or null indicates success. The error is logged but does not
|
||||
// Empty string indicates success. The error is logged but does not
|
||||
// affect the scheduling system.
|
||||
Error *string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Scheduler is the marker interface for scheduler plugins.
|
||||
@ -39,7 +39,7 @@ type Scheduler interface{}
|
||||
|
||||
// SchedulerCallbackProvider provides the OnSchedulerCallback function.
|
||||
type SchedulerCallbackProvider interface {
|
||||
OnSchedulerCallback(SchedulerCallbackInput) (SchedulerCallbackOutput, error)
|
||||
OnSchedulerCallback(SchedulerCallbackRequest) (SchedulerCallbackResponse, error)
|
||||
}
|
||||
|
||||
// NotImplementedCode is the standard return code for unimplemented functions.
|
||||
|
||||
@ -25,8 +25,22 @@ const (
|
||||
ScrobblerErrorUnrecoverable ScrobblerErrorType = "unrecoverable"
|
||||
)
|
||||
|
||||
// NowPlayingInput is the input for now playing notification.
|
||||
type NowPlayingInput struct {
|
||||
// IsAuthorizedRequest is the request for authorization check.
|
||||
type IsAuthorizedRequest struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// IsAuthorizedResponse is the response for authorization check.
|
||||
type IsAuthorizedResponse struct {
|
||||
// Authorized indicates whether the user is authorized to scrobble.
|
||||
Authorized bool `json:"authorized"`
|
||||
}
|
||||
|
||||
// NowPlayingRequest is the request for now playing notification.
|
||||
type NowPlayingRequest struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
@ -37,16 +51,16 @@ type NowPlayingInput struct {
|
||||
Position int32 `json:"position"`
|
||||
}
|
||||
|
||||
// ScrobblerOutput is the output for scrobbler operations.
|
||||
type ScrobblerOutput struct {
|
||||
// ScrobblerResponse is the response for scrobbler operations.
|
||||
type ScrobblerResponse struct {
|
||||
// Error is the error message if the operation failed.
|
||||
Error *string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
// ErrorType indicates how Navidrome should handle the error.
|
||||
ErrorType *ScrobblerErrorType `json:"errorType,omitempty"`
|
||||
ErrorType ScrobblerErrorType `json:"errorType,omitempty"`
|
||||
}
|
||||
|
||||
// ScrobbleInput is the input for submitting a scrobble.
|
||||
type ScrobbleInput struct {
|
||||
// ScrobbleRequest is the request for submitting a scrobble.
|
||||
type ScrobbleRequest struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
@ -70,37 +84,23 @@ type TrackInfo struct {
|
||||
// AlbumArtist is the album artist.
|
||||
AlbumArtist string `json:"albumArtist"`
|
||||
// Duration is the track duration in seconds.
|
||||
Duration float64 `json:"duration"`
|
||||
Duration float32 `json:"duration"`
|
||||
// TrackNumber is the track number on the album.
|
||||
TrackNumber int32 `json:"trackNumber"`
|
||||
// DiscNumber is the disc number.
|
||||
DiscNumber int32 `json:"discNumber"`
|
||||
// MBZRecordingID is the MusicBrainz recording ID.
|
||||
MBZRecordingID *string `json:"mbzRecordingId,omitempty"`
|
||||
MBZRecordingID string `json:"mbzRecordingId,omitempty"`
|
||||
// MBZAlbumID is the MusicBrainz album/release ID.
|
||||
MBZAlbumID *string `json:"mbzAlbumId,omitempty"`
|
||||
MBZAlbumID string `json:"mbzAlbumId,omitempty"`
|
||||
// MBZArtistID is the MusicBrainz artist ID.
|
||||
MBZArtistID *string `json:"mbzArtistId,omitempty"`
|
||||
MBZArtistID string `json:"mbzArtistId,omitempty"`
|
||||
// MBZReleaseGroupID is the MusicBrainz release group ID.
|
||||
MBZReleaseGroupID *string `json:"mbzReleaseGroupId,omitempty"`
|
||||
MBZReleaseGroupID string `json:"mbzReleaseGroupId,omitempty"`
|
||||
// MBZAlbumArtistID is the MusicBrainz album artist ID.
|
||||
MBZAlbumArtistID *string `json:"mbzAlbumArtistId,omitempty"`
|
||||
MBZAlbumArtistID string `json:"mbzAlbumArtistId,omitempty"`
|
||||
// MBZReleaseTrackID is the MusicBrainz release track ID.
|
||||
MBZReleaseTrackID *string `json:"mbzReleaseTrackId,omitempty"`
|
||||
}
|
||||
|
||||
// AuthInput is the input for authorization check.
|
||||
type AuthInput struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// AuthOutput is the output for authorization check.
|
||||
type AuthOutput struct {
|
||||
// Authorized indicates whether the user is authorized to scrobble.
|
||||
Authorized bool `json:"authorized"`
|
||||
MBZReleaseTrackID string `json:"mbzReleaseTrackId,omitempty"`
|
||||
}
|
||||
|
||||
// Scrobbler requires all methods to be implemented.
|
||||
@ -112,16 +112,16 @@ type AuthOutput struct {
|
||||
// all three functions: IsAuthorized, NowPlaying, and Scrobble.
|
||||
type Scrobbler interface {
|
||||
// IsAuthorized - IsAuthorized checks if a user is authorized to scrobble to this service.
|
||||
IsAuthorized(AuthInput) (AuthOutput, error)
|
||||
IsAuthorized(IsAuthorizedRequest) (IsAuthorizedResponse, error)
|
||||
// NowPlaying - NowPlaying sends a now playing notification to the scrobbling service.
|
||||
NowPlaying(NowPlayingInput) (ScrobblerOutput, error)
|
||||
NowPlaying(NowPlayingRequest) (ScrobblerResponse, error)
|
||||
// Scrobble - Scrobble submits a completed scrobble to the scrobbling service.
|
||||
Scrobble(ScrobbleInput) (ScrobblerOutput, error)
|
||||
Scrobble(ScrobbleRequest) (ScrobblerResponse, error)
|
||||
} // Internal implementation holders
|
||||
var (
|
||||
isAuthorizedImpl func(AuthInput) (AuthOutput, error)
|
||||
nowPlayingImpl func(NowPlayingInput) (ScrobblerOutput, error)
|
||||
scrobbleImpl func(ScrobbleInput) (ScrobblerOutput, error)
|
||||
isAuthorizedImpl func(IsAuthorizedRequest) (IsAuthorizedResponse, error)
|
||||
nowPlayingImpl func(NowPlayingRequest) (ScrobblerResponse, error)
|
||||
scrobbleImpl func(ScrobbleRequest) (ScrobblerResponse, error)
|
||||
)
|
||||
|
||||
// Register registers a scrobbler implementation.
|
||||
@ -143,7 +143,7 @@ func _NdScrobblerIsAuthorized() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input AuthInput
|
||||
var input IsAuthorizedRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -170,7 +170,7 @@ func _NdScrobblerNowPlaying() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input NowPlayingInput
|
||||
var input NowPlayingRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -197,7 +197,7 @@ func _NdScrobblerScrobble() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input ScrobbleInput
|
||||
var input ScrobbleRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
|
||||
@ -22,8 +22,22 @@ const (
|
||||
ScrobblerErrorUnrecoverable ScrobblerErrorType = "unrecoverable"
|
||||
)
|
||||
|
||||
// NowPlayingInput is the input for now playing notification.
|
||||
type NowPlayingInput struct {
|
||||
// IsAuthorizedRequest is the request for authorization check.
|
||||
type IsAuthorizedRequest struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// IsAuthorizedResponse is the response for authorization check.
|
||||
type IsAuthorizedResponse struct {
|
||||
// Authorized indicates whether the user is authorized to scrobble.
|
||||
Authorized bool `json:"authorized"`
|
||||
}
|
||||
|
||||
// NowPlayingRequest is the request for now playing notification.
|
||||
type NowPlayingRequest struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
@ -34,16 +48,16 @@ type NowPlayingInput struct {
|
||||
Position int32 `json:"position"`
|
||||
}
|
||||
|
||||
// ScrobblerOutput is the output for scrobbler operations.
|
||||
type ScrobblerOutput struct {
|
||||
// ScrobblerResponse is the response for scrobbler operations.
|
||||
type ScrobblerResponse struct {
|
||||
// Error is the error message if the operation failed.
|
||||
Error *string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
// ErrorType indicates how Navidrome should handle the error.
|
||||
ErrorType *ScrobblerErrorType `json:"errorType,omitempty"`
|
||||
ErrorType ScrobblerErrorType `json:"errorType,omitempty"`
|
||||
}
|
||||
|
||||
// ScrobbleInput is the input for submitting a scrobble.
|
||||
type ScrobbleInput struct {
|
||||
// ScrobbleRequest is the request for submitting a scrobble.
|
||||
type ScrobbleRequest struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
@ -67,37 +81,23 @@ type TrackInfo struct {
|
||||
// AlbumArtist is the album artist.
|
||||
AlbumArtist string `json:"albumArtist"`
|
||||
// Duration is the track duration in seconds.
|
||||
Duration float64 `json:"duration"`
|
||||
Duration float32 `json:"duration"`
|
||||
// TrackNumber is the track number on the album.
|
||||
TrackNumber int32 `json:"trackNumber"`
|
||||
// DiscNumber is the disc number.
|
||||
DiscNumber int32 `json:"discNumber"`
|
||||
// MBZRecordingID is the MusicBrainz recording ID.
|
||||
MBZRecordingID *string `json:"mbzRecordingId,omitempty"`
|
||||
MBZRecordingID string `json:"mbzRecordingId,omitempty"`
|
||||
// MBZAlbumID is the MusicBrainz album/release ID.
|
||||
MBZAlbumID *string `json:"mbzAlbumId,omitempty"`
|
||||
MBZAlbumID string `json:"mbzAlbumId,omitempty"`
|
||||
// MBZArtistID is the MusicBrainz artist ID.
|
||||
MBZArtistID *string `json:"mbzArtistId,omitempty"`
|
||||
MBZArtistID string `json:"mbzArtistId,omitempty"`
|
||||
// MBZReleaseGroupID is the MusicBrainz release group ID.
|
||||
MBZReleaseGroupID *string `json:"mbzReleaseGroupId,omitempty"`
|
||||
MBZReleaseGroupID string `json:"mbzReleaseGroupId,omitempty"`
|
||||
// MBZAlbumArtistID is the MusicBrainz album artist ID.
|
||||
MBZAlbumArtistID *string `json:"mbzAlbumArtistId,omitempty"`
|
||||
MBZAlbumArtistID string `json:"mbzAlbumArtistId,omitempty"`
|
||||
// MBZReleaseTrackID is the MusicBrainz release track ID.
|
||||
MBZReleaseTrackID *string `json:"mbzReleaseTrackId,omitempty"`
|
||||
}
|
||||
|
||||
// AuthInput is the input for authorization check.
|
||||
type AuthInput struct {
|
||||
// UserID is the internal Navidrome user ID.
|
||||
UserID string `json:"userId"`
|
||||
// Username is the username of the user.
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// AuthOutput is the output for authorization check.
|
||||
type AuthOutput struct {
|
||||
// Authorized indicates whether the user is authorized to scrobble.
|
||||
Authorized bool `json:"authorized"`
|
||||
MBZReleaseTrackID string `json:"mbzReleaseTrackId,omitempty"`
|
||||
}
|
||||
|
||||
// Scrobbler requires all methods to be implemented.
|
||||
@ -109,11 +109,11 @@ type AuthOutput struct {
|
||||
// all three functions: IsAuthorized, NowPlaying, and Scrobble.
|
||||
type Scrobbler interface {
|
||||
// IsAuthorized - IsAuthorized checks if a user is authorized to scrobble to this service.
|
||||
IsAuthorized(AuthInput) (AuthOutput, error)
|
||||
IsAuthorized(IsAuthorizedRequest) (IsAuthorizedResponse, error)
|
||||
// NowPlaying - NowPlaying sends a now playing notification to the scrobbling service.
|
||||
NowPlaying(NowPlayingInput) (ScrobblerOutput, error)
|
||||
NowPlaying(NowPlayingRequest) (ScrobblerResponse, error)
|
||||
// Scrobble - Scrobble submits a completed scrobble to the scrobbling service.
|
||||
Scrobble(ScrobbleInput) (ScrobblerOutput, error)
|
||||
Scrobble(ScrobbleRequest) (ScrobblerResponse, error)
|
||||
}
|
||||
|
||||
// NotImplementedCode is the standard return code for unimplemented functions.
|
||||
|
||||
@ -11,23 +11,15 @@ import (
|
||||
pdk "github.com/extism/go-pdk"
|
||||
)
|
||||
|
||||
// OnErrorInput is the input provided when an error occurs on a WebSocket connection.
|
||||
type OnErrorInput struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection where the error occurred.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Error is the error message describing what went wrong.
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// OnErrorOutput is the output from the error handler.
|
||||
type OnErrorOutput struct {
|
||||
// OnErrorResponse is the response from the error handler.
|
||||
type OnErrorResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnCloseInput is the input provided when a WebSocket connection is closed.
|
||||
type OnCloseInput struct {
|
||||
// OnCloseRequest is the request provided when a WebSocket connection is closed.
|
||||
type OnCloseRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection that was closed.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Code is the WebSocket close status code (e.g., 1000 for normal closure,
|
||||
@ -37,41 +29,49 @@ type OnCloseInput struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// OnCloseOutput is the output from the close handler.
|
||||
type OnCloseOutput struct {
|
||||
// OnCloseResponse is the response from the close handler.
|
||||
type OnCloseResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnTextMessageInput is the input provided when a text message is received.
|
||||
type OnTextMessageInput struct {
|
||||
// OnTextMessageRequest is the request provided when a text message is received.
|
||||
type OnTextMessageRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection that received the message.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Message is the text message content received from the WebSocket.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// OnTextMessageOutput is the output from the text message handler.
|
||||
type OnTextMessageOutput struct {
|
||||
// OnTextMessageResponse is the response from the text message handler.
|
||||
type OnTextMessageResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnBinaryMessageInput is the input provided when a binary message is received.
|
||||
type OnBinaryMessageInput struct {
|
||||
// OnBinaryMessageRequest is the request provided when a binary message is received.
|
||||
type OnBinaryMessageRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection that received the message.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Data is the binary data received from the WebSocket, encoded as base64.
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
// OnBinaryMessageOutput is the output from the binary message handler.
|
||||
type OnBinaryMessageOutput struct {
|
||||
// OnBinaryMessageResponse is the response from the binary message handler.
|
||||
type OnBinaryMessageResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnErrorRequest is the request provided when an error occurs on a WebSocket connection.
|
||||
type OnErrorRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection where the error occurred.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Error is the error message describing what went wrong.
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// WebSocket is the marker interface for websocket plugins.
|
||||
@ -85,28 +85,28 @@ type WebSocket interface{}
|
||||
|
||||
// TextMessageProvider provides the OnTextMessage function.
|
||||
type TextMessageProvider interface {
|
||||
OnTextMessage(OnTextMessageInput) (OnTextMessageOutput, error)
|
||||
OnTextMessage(OnTextMessageRequest) (OnTextMessageResponse, error)
|
||||
}
|
||||
|
||||
// BinaryMessageProvider provides the OnBinaryMessage function.
|
||||
type BinaryMessageProvider interface {
|
||||
OnBinaryMessage(OnBinaryMessageInput) (OnBinaryMessageOutput, error)
|
||||
OnBinaryMessage(OnBinaryMessageRequest) (OnBinaryMessageResponse, error)
|
||||
}
|
||||
|
||||
// ErrorProvider provides the OnError function.
|
||||
type ErrorProvider interface {
|
||||
OnError(OnErrorInput) (OnErrorOutput, error)
|
||||
OnError(OnErrorRequest) (OnErrorResponse, error)
|
||||
}
|
||||
|
||||
// CloseProvider provides the OnClose function.
|
||||
type CloseProvider interface {
|
||||
OnClose(OnCloseInput) (OnCloseOutput, error)
|
||||
OnClose(OnCloseRequest) (OnCloseResponse, error)
|
||||
} // Internal implementation holders
|
||||
var (
|
||||
textMessageImpl func(OnTextMessageInput) (OnTextMessageOutput, error)
|
||||
binaryMessageImpl func(OnBinaryMessageInput) (OnBinaryMessageOutput, error)
|
||||
errorImpl func(OnErrorInput) (OnErrorOutput, error)
|
||||
closeImpl func(OnCloseInput) (OnCloseOutput, error)
|
||||
textMessageImpl func(OnTextMessageRequest) (OnTextMessageResponse, error)
|
||||
binaryMessageImpl func(OnBinaryMessageRequest) (OnBinaryMessageResponse, error)
|
||||
errorImpl func(OnErrorRequest) (OnErrorResponse, error)
|
||||
closeImpl func(OnCloseRequest) (OnCloseResponse, error)
|
||||
)
|
||||
|
||||
// Register registers a websocket implementation.
|
||||
@ -137,7 +137,7 @@ func _NdWebsocketOnTextMessage() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input OnTextMessageInput
|
||||
var input OnTextMessageRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -164,7 +164,7 @@ func _NdWebsocketOnBinaryMessage() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input OnBinaryMessageInput
|
||||
var input OnBinaryMessageRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -191,7 +191,7 @@ func _NdWebsocketOnError() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input OnErrorInput
|
||||
var input OnErrorRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
@ -218,7 +218,7 @@ func _NdWebsocketOnClose() int32 {
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input OnCloseInput
|
||||
var input OnCloseRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
|
||||
@ -8,23 +8,15 @@
|
||||
|
||||
package websocket
|
||||
|
||||
// OnErrorInput is the input provided when an error occurs on a WebSocket connection.
|
||||
type OnErrorInput struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection where the error occurred.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Error is the error message describing what went wrong.
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// OnErrorOutput is the output from the error handler.
|
||||
type OnErrorOutput struct {
|
||||
// OnErrorResponse is the response from the error handler.
|
||||
type OnErrorResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnCloseInput is the input provided when a WebSocket connection is closed.
|
||||
type OnCloseInput struct {
|
||||
// OnCloseRequest is the request provided when a WebSocket connection is closed.
|
||||
type OnCloseRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection that was closed.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Code is the WebSocket close status code (e.g., 1000 for normal closure,
|
||||
@ -34,41 +26,49 @@ type OnCloseInput struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// OnCloseOutput is the output from the close handler.
|
||||
type OnCloseOutput struct {
|
||||
// OnCloseResponse is the response from the close handler.
|
||||
type OnCloseResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnTextMessageInput is the input provided when a text message is received.
|
||||
type OnTextMessageInput struct {
|
||||
// OnTextMessageRequest is the request provided when a text message is received.
|
||||
type OnTextMessageRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection that received the message.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Message is the text message content received from the WebSocket.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// OnTextMessageOutput is the output from the text message handler.
|
||||
type OnTextMessageOutput struct {
|
||||
// OnTextMessageResponse is the response from the text message handler.
|
||||
type OnTextMessageResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnBinaryMessageInput is the input provided when a binary message is received.
|
||||
type OnBinaryMessageInput struct {
|
||||
// OnBinaryMessageRequest is the request provided when a binary message is received.
|
||||
type OnBinaryMessageRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection that received the message.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Data is the binary data received from the WebSocket, encoded as base64.
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
// OnBinaryMessageOutput is the output from the binary message handler.
|
||||
type OnBinaryMessageOutput struct {
|
||||
// OnBinaryMessageResponse is the response from the binary message handler.
|
||||
type OnBinaryMessageResponse struct {
|
||||
// Error is the error message if the callback failed.
|
||||
// Empty or null indicates success.
|
||||
Error *string `json:"error,omitempty"`
|
||||
// Empty string indicates success.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// OnErrorRequest is the request provided when an error occurs on a WebSocket connection.
|
||||
type OnErrorRequest struct {
|
||||
// ConnectionID is the unique identifier for the WebSocket connection where the error occurred.
|
||||
ConnectionID string `json:"connectionId"`
|
||||
// Error is the error message describing what went wrong.
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// WebSocket is the marker interface for websocket plugins.
|
||||
@ -82,22 +82,22 @@ type WebSocket interface{}
|
||||
|
||||
// TextMessageProvider provides the OnTextMessage function.
|
||||
type TextMessageProvider interface {
|
||||
OnTextMessage(OnTextMessageInput) (OnTextMessageOutput, error)
|
||||
OnTextMessage(OnTextMessageRequest) (OnTextMessageResponse, error)
|
||||
}
|
||||
|
||||
// BinaryMessageProvider provides the OnBinaryMessage function.
|
||||
type BinaryMessageProvider interface {
|
||||
OnBinaryMessage(OnBinaryMessageInput) (OnBinaryMessageOutput, error)
|
||||
OnBinaryMessage(OnBinaryMessageRequest) (OnBinaryMessageResponse, error)
|
||||
}
|
||||
|
||||
// ErrorProvider provides the OnError function.
|
||||
type ErrorProvider interface {
|
||||
OnError(OnErrorInput) (OnErrorOutput, error)
|
||||
OnError(OnErrorRequest) (OnErrorResponse, error)
|
||||
}
|
||||
|
||||
// CloseProvider provides the OnClose function.
|
||||
type CloseProvider interface {
|
||||
OnClose(OnCloseInput) (OnCloseOutput, error)
|
||||
OnClose(OnCloseRequest) (OnCloseResponse, error)
|
||||
}
|
||||
|
||||
// NotImplementedCode is the standard return code for unimplemented functions.
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"github.com/navidrome/navidrome/core/scrobbler"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/model/request"
|
||||
"github.com/navidrome/navidrome/plugins/capabilities"
|
||||
)
|
||||
|
||||
// CapabilityScrobbler indicates the plugin can receive scrobble events.
|
||||
@ -39,12 +40,12 @@ type ScrobblerPlugin struct {
|
||||
// IsAuthorized checks if the user is authorized with this scrobbler
|
||||
func (s *ScrobblerPlugin) IsAuthorized(ctx context.Context, userId string) bool {
|
||||
username := getUsernameFromContext(ctx)
|
||||
input := scrobblerAuthInput{
|
||||
input := capabilities.IsAuthorizedRequest{
|
||||
UserID: userId,
|
||||
Username: username,
|
||||
}
|
||||
|
||||
result, err := callPluginFunction[scrobblerAuthInput, scrobblerAuthOutput](ctx, s.plugin, FuncScrobblerIsAuthorized, input)
|
||||
result, err := callPluginFunction[capabilities.IsAuthorizedRequest, capabilities.IsAuthorizedResponse](ctx, s.plugin, FuncScrobblerIsAuthorized, input)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@ -55,14 +56,14 @@ func (s *ScrobblerPlugin) IsAuthorized(ctx context.Context, userId string) bool
|
||||
// NowPlaying sends a now playing notification to the scrobbler
|
||||
func (s *ScrobblerPlugin) NowPlaying(ctx context.Context, userId string, track *model.MediaFile, position int) error {
|
||||
username := getUsernameFromContext(ctx)
|
||||
input := scrobblerNowPlayingInput{
|
||||
input := capabilities.NowPlayingRequest{
|
||||
UserID: userId,
|
||||
Username: username,
|
||||
Track: mediaFileToTrackInfo(track),
|
||||
Position: position,
|
||||
Position: int32(position),
|
||||
}
|
||||
|
||||
result, err := callPluginFunction[scrobblerNowPlayingInput, scrobblerOutput](ctx, s.plugin, FuncScrobblerNowPlaying, input)
|
||||
result, err := callPluginFunction[capabilities.NowPlayingRequest, capabilities.ScrobblerResponse](ctx, s.plugin, FuncScrobblerNowPlaying, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -73,14 +74,14 @@ func (s *ScrobblerPlugin) NowPlaying(ctx context.Context, userId string, track *
|
||||
// Scrobble submits a scrobble to the scrobbler
|
||||
func (s *ScrobblerPlugin) Scrobble(ctx context.Context, userId string, sc scrobbler.Scrobble) error {
|
||||
username := getUsernameFromContext(ctx)
|
||||
input := scrobblerScrobbleInput{
|
||||
input := capabilities.ScrobbleRequest{
|
||||
UserID: userId,
|
||||
Username: username,
|
||||
Track: mediaFileToTrackInfo(&sc.MediaFile),
|
||||
Timestamp: sc.TimeStamp.Unix(),
|
||||
}
|
||||
|
||||
result, err := callPluginFunction[scrobblerScrobbleInput, scrobblerOutput](ctx, s.plugin, FuncScrobblerScrobble, input)
|
||||
result, err := callPluginFunction[capabilities.ScrobbleRequest, capabilities.ScrobblerResponse](ctx, s.plugin, FuncScrobblerScrobble, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -96,42 +97,42 @@ func getUsernameFromContext(ctx context.Context) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// mediaFileToTrackInfo converts a model.MediaFile to scrobblerTrackInfo
|
||||
func mediaFileToTrackInfo(mf *model.MediaFile) scrobblerTrackInfo {
|
||||
return scrobblerTrackInfo{
|
||||
// mediaFileToTrackInfo converts a model.MediaFile to capabilities.TrackInfo
|
||||
func mediaFileToTrackInfo(mf *model.MediaFile) capabilities.TrackInfo {
|
||||
return capabilities.TrackInfo{
|
||||
ID: mf.ID,
|
||||
Title: mf.Title,
|
||||
Album: mf.Album,
|
||||
Artist: mf.Artist,
|
||||
AlbumArtist: mf.AlbumArtist,
|
||||
Duration: mf.Duration,
|
||||
TrackNumber: mf.TrackNumber,
|
||||
DiscNumber: mf.DiscNumber,
|
||||
MbzRecordingID: mf.MbzRecordingID,
|
||||
MbzAlbumID: mf.MbzAlbumID,
|
||||
MbzArtistID: mf.MbzArtistID,
|
||||
MbzReleaseGroupID: mf.MbzReleaseGroupID,
|
||||
MbzAlbumArtistID: mf.MbzAlbumArtistID,
|
||||
MbzReleaseTrackID: mf.MbzReleaseTrackID,
|
||||
TrackNumber: int32(mf.TrackNumber),
|
||||
DiscNumber: int32(mf.DiscNumber),
|
||||
MBZRecordingID: mf.MbzRecordingID,
|
||||
MBZAlbumID: mf.MbzAlbumID,
|
||||
MBZArtistID: mf.MbzArtistID,
|
||||
MBZReleaseGroupID: mf.MbzReleaseGroupID,
|
||||
MBZAlbumArtistID: mf.MbzAlbumArtistID,
|
||||
MBZReleaseTrackID: mf.MbzReleaseTrackID,
|
||||
}
|
||||
}
|
||||
|
||||
// mapScrobblerError converts the plugin output error to a scrobbler error
|
||||
func mapScrobblerError(output scrobblerOutput) error {
|
||||
func mapScrobblerError(output capabilities.ScrobblerResponse) error {
|
||||
switch output.ErrorType {
|
||||
case scrobblerErrorNone, "":
|
||||
case capabilities.ScrobblerErrorNone, "":
|
||||
return nil
|
||||
case scrobblerErrorNotAuthorized:
|
||||
case capabilities.ScrobblerErrorNotAuthorized:
|
||||
if output.Error != "" {
|
||||
return fmt.Errorf("%w: %s", scrobbler.ErrNotAuthorized, output.Error)
|
||||
}
|
||||
return scrobbler.ErrNotAuthorized
|
||||
case scrobblerErrorRetryLater:
|
||||
case capabilities.ScrobblerErrorRetryLater:
|
||||
if output.Error != "" {
|
||||
return fmt.Errorf("%w: %s", scrobbler.ErrRetryLater, output.Error)
|
||||
}
|
||||
return scrobbler.ErrRetryLater
|
||||
case scrobblerErrorUnrecoverable:
|
||||
case capabilities.ScrobblerErrorUnrecoverable:
|
||||
if output.Error != "" {
|
||||
return fmt.Errorf("%w: %s", scrobbler.ErrUnrecoverable, output.Error)
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"github.com/navidrome/navidrome/core/scrobbler"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/model/request"
|
||||
"github.com/navidrome/navidrome/plugins/capabilities"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
@ -170,42 +171,42 @@ var _ = Describe("ScrobblerPlugin", Ordered, func() {
|
||||
|
||||
var _ = Describe("mapScrobblerError", func() {
|
||||
It("returns nil for empty error type", func() {
|
||||
output := scrobblerOutput{ErrorType: ""}
|
||||
output := capabilities.ScrobblerResponse{ErrorType: ""}
|
||||
Expect(mapScrobblerError(output)).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("returns nil for 'none' error type", func() {
|
||||
output := scrobblerOutput{ErrorType: "none"}
|
||||
output := capabilities.ScrobblerResponse{ErrorType: capabilities.ScrobblerErrorNone}
|
||||
Expect(mapScrobblerError(output)).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("returns ErrNotAuthorized for 'not_authorized' error type", func() {
|
||||
output := scrobblerOutput{ErrorType: "not_authorized"}
|
||||
output := capabilities.ScrobblerResponse{ErrorType: capabilities.ScrobblerErrorNotAuthorized}
|
||||
err := mapScrobblerError(output)
|
||||
Expect(err).To(MatchError(scrobbler.ErrNotAuthorized))
|
||||
})
|
||||
|
||||
It("returns ErrNotAuthorized with message", func() {
|
||||
output := scrobblerOutput{ErrorType: "not_authorized", Error: "user not linked"}
|
||||
output := capabilities.ScrobblerResponse{ErrorType: capabilities.ScrobblerErrorNotAuthorized, Error: "user not linked"}
|
||||
err := mapScrobblerError(output)
|
||||
Expect(err).To(MatchError(ContainSubstring("not authorized")))
|
||||
Expect(err).To(MatchError(ContainSubstring("user not linked")))
|
||||
})
|
||||
|
||||
It("returns ErrRetryLater for 'retry_later' error type", func() {
|
||||
output := scrobblerOutput{ErrorType: "retry_later"}
|
||||
output := capabilities.ScrobblerResponse{ErrorType: capabilities.ScrobblerErrorRetryLater}
|
||||
err := mapScrobblerError(output)
|
||||
Expect(err).To(MatchError(scrobbler.ErrRetryLater))
|
||||
})
|
||||
|
||||
It("returns ErrUnrecoverable for 'unrecoverable' error type", func() {
|
||||
output := scrobblerOutput{ErrorType: "unrecoverable"}
|
||||
output := capabilities.ScrobblerResponse{ErrorType: capabilities.ScrobblerErrorUnrecoverable}
|
||||
err := mapScrobblerError(output)
|
||||
Expect(err).To(MatchError(scrobbler.ErrUnrecoverable))
|
||||
})
|
||||
|
||||
It("returns error for unknown error type", func() {
|
||||
output := scrobblerOutput{ErrorType: "unknown"}
|
||||
output := capabilities.ScrobblerResponse{ErrorType: "unknown"}
|
||||
err := mapScrobblerError(output)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("unknown error type"))
|
||||
|
||||
@ -1,62 +0,0 @@
|
||||
package plugins
|
||||
|
||||
// --- Input/Output JSON structures for Scrobbler plugin calls ---
|
||||
|
||||
// scrobblerAuthInput is the input for IsAuthorized
|
||||
type scrobblerAuthInput struct {
|
||||
UserID string `json:"userId"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// scrobblerAuthOutput is the output for IsAuthorized
|
||||
type scrobblerAuthOutput struct {
|
||||
Authorized bool `json:"authorized"`
|
||||
}
|
||||
|
||||
// scrobblerTrackInfo contains track metadata for scrobbling
|
||||
type scrobblerTrackInfo struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Album string `json:"album"`
|
||||
Artist string `json:"artist"`
|
||||
AlbumArtist string `json:"albumArtist"`
|
||||
Duration float32 `json:"duration"`
|
||||
TrackNumber int `json:"trackNumber"`
|
||||
DiscNumber int `json:"discNumber"`
|
||||
MbzRecordingID string `json:"mbzRecordingId,omitempty"`
|
||||
MbzAlbumID string `json:"mbzAlbumId,omitempty"`
|
||||
MbzArtistID string `json:"mbzArtistId,omitempty"`
|
||||
MbzReleaseGroupID string `json:"mbzReleaseGroupId,omitempty"`
|
||||
MbzAlbumArtistID string `json:"mbzAlbumArtistId,omitempty"`
|
||||
MbzReleaseTrackID string `json:"mbzReleaseTrackId,omitempty"`
|
||||
}
|
||||
|
||||
// scrobblerNowPlayingInput is the input for NowPlaying
|
||||
type scrobblerNowPlayingInput struct {
|
||||
UserID string `json:"userId"`
|
||||
Username string `json:"username"`
|
||||
Track scrobblerTrackInfo `json:"track"`
|
||||
Position int `json:"position"`
|
||||
}
|
||||
|
||||
// scrobblerScrobbleInput is the input for Scrobble
|
||||
type scrobblerScrobbleInput struct {
|
||||
UserID string `json:"userId"`
|
||||
Username string `json:"username"`
|
||||
Track scrobblerTrackInfo `json:"track"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
// scrobblerOutput is the output for NowPlaying and Scrobble
|
||||
type scrobblerOutput struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
ErrorType string `json:"errorType,omitempty"` // "none", "notAuthorized", "retryLater", "unrecoverable"
|
||||
}
|
||||
|
||||
// scrobbler error type constants
|
||||
const (
|
||||
scrobblerErrorNone = "none"
|
||||
scrobblerErrorNotAuthorized = "not_authorized"
|
||||
scrobblerErrorRetryLater = "retry_later"
|
||||
scrobblerErrorUnrecoverable = "unrecoverable"
|
||||
)
|
||||
Loading…
x
Reference in New Issue
Block a user