fix(plugins): update IsAuthorized method to return boolean instead of response object

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2025-12-31 12:27:57 -05:00
parent 6fff476e93
commit 6698e94a9c
14 changed files with 34 additions and 78 deletions

View File

@ -192,8 +192,8 @@ Agents = "lastfm,spotify,my-plugin"
Integrates with external scrobbling services. Export one or more of these functions:
| Function | Input | Output | Description |
|------------------------------|-----------------------|----------------|-----------------------------||
| `nd_scrobbler_is_authorized` | `{userId, username}` | `{authorized}` | Check if user is authorized |
|------------------------------|-----------------------|----------------|-----------------------------|
| `nd_scrobbler_is_authorized` | `{userId, username}` | `bool` | Check if user is authorized |
| `nd_scrobbler_now_playing` | See below | (none) | Send now playing |
| `nd_scrobbler_scrobble` | See below | (none) | Submit a scrobble |
@ -777,15 +777,15 @@ serde_json = "1.0"
**Implementing capabilities with traits and macros:**
```rust
use nd_pdk::scrobbler::{Scrobbler, IsAuthorizedRequest, IsAuthorizedResponse, Error};
use nd_pdk::scrobbler::{Scrobbler, IsAuthorizedRequest, Error};
use nd_pdk::register_scrobbler;
#[derive(Default)]
struct MyPlugin;
impl Scrobbler for MyPlugin {
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<IsAuthorizedResponse, Error> {
Ok(IsAuthorizedResponse { authorized: true })
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<bool, Error> {
Ok(true)
}
fn now_playing(&self, req: NowPlayingRequest) -> Result<(), Error> { Ok(()) }
fn scrobble(&self, req: ScrobbleRequest) -> Result<(), Error> { Ok(()) }

View File

@ -47,7 +47,7 @@
//
// // Scrobbler requires all methods
// type Scrobbler interface {
// IsAuthorized(IsAuthorizedRequest) (*IsAuthorizedResponse, error)
// IsAuthorized(IsAuthorizedRequest) (bool, error)
// NowPlaying(NowPlayingRequest) error
// Scrobble(ScrobbleRequest) error
// }

View File

@ -11,7 +11,7 @@ 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(IsAuthorizedRequest) (*IsAuthorizedResponse, error)
IsAuthorized(IsAuthorizedRequest) (bool, error)
// NowPlaying sends a now playing notification to the scrobbling service.
//nd:export name=nd_scrobbler_now_playing
@ -30,12 +30,6 @@ type IsAuthorizedRequest struct {
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"`
}
// TrackInfo contains track metadata for scrobbling.
type TrackInfo struct {
// ID is the internal Navidrome track ID.

View File

@ -6,7 +6,7 @@ exports:
$ref: '#/components/schemas/IsAuthorizedRequest'
contentType: application/json
output:
$ref: '#/components/schemas/IsAuthorizedResponse'
type: boolean
contentType: application/json
nd_scrobbler_now_playing:
description: NowPlaying sends a now playing notification to the scrobbling service.
@ -33,15 +33,6 @@ components:
required:
- userId
- username
IsAuthorizedResponse:
description: IsAuthorizedResponse is the response for authorization check.
type: object
properties:
authorized:
type: boolean
description: Authorized indicates whether the user is authorized to scrobble.
required:
- authorized
NowPlayingRequest:
description: NowPlayingRequest is the request for now playing notification.
type: object
@ -144,10 +135,3 @@ components:
- duration
- trackNumber
- discNumber
ScrobblerError:
description: ScrobblerError represents an error type for scrobbling operations.
type: string
enum:
- scrobbler(not_authorized)
- scrobbler(retry_later)
- scrobbler(unrecoverable)

View File

@ -20,7 +20,7 @@
use extism_pdk::*;
use nd_pdk::host::{artwork, scheduler};
use nd_pdk::scrobbler::{
Error as ScrobblerError, IsAuthorizedRequest, IsAuthorizedResponse, NowPlayingRequest,
Error as ScrobblerError, IsAuthorizedRequest, NowPlayingRequest,
ScrobbleRequest, Scrobbler, SCROBBLER_ERROR_NOT_AUTHORIZED, SCROBBLER_ERROR_RETRY_LATER,
};
use nd_pdk::scheduler::{
@ -104,18 +104,18 @@ fn get_image_url(track_id: &str) -> String {
// ============================================================================
impl Scrobbler for DiscordPlugin {
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<IsAuthorizedResponse, ScrobblerError> {
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<bool, ScrobblerError> {
let (_, users) = match get_config() {
Ok(config) => config,
Err(e) => {
error!("Failed to get config: {:?}", e);
return Ok(IsAuthorizedResponse { authorized: false });
return Ok(false);
}
};
let authorized = users.contains_key(&req.username);
info!("IsAuthorized for user {}: {}", req.username, authorized);
Ok(IsAuthorizedResponse { authorized })
Ok(authorized)
}
fn now_playing(&self, req: NowPlayingRequest) -> Result<(), ScrobblerError> {

View File

@ -93,15 +93,15 @@ func getImageURL(trackID string) string {
// ============================================================================
// IsAuthorized checks if a user is authorized for Discord Rich Presence.
func (p *discordPlugin) IsAuthorized(input scrobbler.IsAuthorizedRequest) (*scrobbler.IsAuthorizedResponse, error) {
func (p *discordPlugin) IsAuthorized(input scrobbler.IsAuthorizedRequest) (bool, error) {
_, users, err := getConfig()
if err != nil {
return nil, fmt.Errorf("failed to check user authorization: %w", err)
return false, 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.IsAuthorizedResponse{Authorized: authorized}, nil
return authorized, nil
}
// NowPlaying sends a now playing notification to Discord.

View File

@ -14,7 +14,7 @@
use extism_pdk::{config, error, http, info, warn, HttpRequest};
use nd_pdk::scrobbler::{
Error, IsAuthorizedRequest, IsAuthorizedResponse, NowPlayingRequest, ScrobbleRequest,
Error, IsAuthorizedRequest, NowPlayingRequest, ScrobbleRequest,
Scrobbler,
};
@ -31,12 +31,12 @@ struct WebhookPlugin;
impl Scrobbler for WebhookPlugin {
/// Checks if a user is authorized. This plugin authorizes all users.
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<IsAuthorizedResponse, Error> {
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<bool, Error> {
info!(
"Authorization check for user: {} ({})",
req.username, req.user_id
);
Ok(IsAuthorizedResponse { authorized: true })
Ok(true)
}
/// Handles now playing notifications. This plugin ignores them (webhooks only on scrobble).

View File

@ -34,12 +34,6 @@ type IsAuthorizedRequest struct {
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.
@ -105,14 +99,14 @@ type TrackInfo 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(IsAuthorizedRequest) (*IsAuthorizedResponse, error)
IsAuthorized(IsAuthorizedRequest) (bool, error)
// NowPlaying - NowPlaying sends a now playing notification to the scrobbling service.
NowPlaying(NowPlayingRequest) error
// Scrobble - Scrobble submits a completed scrobble to the scrobbling service.
Scrobble(ScrobbleRequest) error
} // Internal implementation holders
var (
isAuthorizedImpl func(IsAuthorizedRequest) (*IsAuthorizedResponse, error)
isAuthorizedImpl func(IsAuthorizedRequest) (bool, error)
nowPlayingImpl func(NowPlayingRequest) error
scrobbleImpl func(ScrobbleRequest) error
)

View File

@ -31,12 +31,6 @@ type IsAuthorizedRequest struct {
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.
@ -102,7 +96,7 @@ type TrackInfo 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(IsAuthorizedRequest) (*IsAuthorizedResponse, error)
IsAuthorized(IsAuthorizedRequest) (bool, error)
// NowPlaying - NowPlaying sends a now playing notification to the scrobbling service.
NowPlaying(NowPlayingRequest) error
// Scrobble - Scrobble submits a completed scrobble to the scrobbling service.

View File

@ -34,7 +34,7 @@ The Scrobbler capability requires all methods to be implemented:
```rust
use nd_pdk::scrobbler::{
Error, IsAuthorizedRequest, IsAuthorizedResponse,
Error, IsAuthorizedRequest,
NowPlayingRequest, ScrobbleRequest, Scrobbler,
};
@ -45,8 +45,8 @@ nd_pdk::register_scrobbler!(MyPlugin);
struct MyPlugin;
impl Scrobbler for MyPlugin {
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<IsAuthorizedResponse, Error> {
Ok(IsAuthorizedResponse { authorized: true })
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<bool, Error> {
Ok(true)
}
fn now_playing(&self, req: NowPlayingRequest) -> Result<(), Error> {

View File

@ -23,14 +23,6 @@ pub struct IsAuthorizedRequest {
#[serde(default)]
pub username: String,
}
/// IsAuthorizedResponse is the response for authorization check.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct IsAuthorizedResponse {
/// Authorized indicates whether the user is authorized to scrobble.
#[serde(default)]
pub authorized: bool,
}
/// NowPlayingRequest is the request for now playing notification.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -142,7 +134,7 @@ impl Error {
/// all three functions: IsAuthorized, NowPlaying, and Scrobble.
pub trait Scrobbler {
/// IsAuthorized - IsAuthorized checks if a user is authorized to scrobble to this service.
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<IsAuthorizedResponse, Error>;
fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<bool, Error>;
/// NowPlaying - NowPlaying sends a now playing notification to the scrobbling service.
fn now_playing(&self, req: NowPlayingRequest) -> Result<(), Error>;
/// Scrobble - Scrobble submits a completed scrobble to the scrobbling service.
@ -157,7 +149,7 @@ macro_rules! register_scrobbler {
#[extism_pdk::plugin_fn]
pub fn nd_scrobbler_is_authorized(
req: extism_pdk::Json<$crate::scrobbler::IsAuthorizedRequest>
) -> extism_pdk::FnResult<extism_pdk::Json<$crate::scrobbler::IsAuthorizedResponse>> {
) -> extism_pdk::FnResult<extism_pdk::Json<bool>> {
let plugin = <$plugin_type>::default();
let result = $crate::scrobbler::Scrobbler::is_authorized(&plugin, req.into_inner())?;
Ok(extism_pdk::Json(result))

View File

@ -6,7 +6,7 @@
//! # Example
//!
//! ```rust,no_run
//! use nd_pdk::scrobbler::{Scrobbler, IsAuthorizedRequest, IsAuthorizedResponse, Error};
//! use nd_pdk::scrobbler::{Scrobbler, IsAuthorizedRequest, Error};
//! use nd_pdk::register_scrobbler;
//!
//! struct MyPlugin;
@ -16,8 +16,8 @@
//! }
//!
//! impl Scrobbler for MyPlugin {
//! fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<IsAuthorizedResponse, Error> {
//! Ok(IsAuthorizedResponse { authorized: true })
//! fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<bool, Error> {
//! Ok(true)
//! }
//! // ... implement other required methods
//! }

View File

@ -45,12 +45,12 @@ func (s *ScrobblerPlugin) IsAuthorized(ctx context.Context, userId string) bool
Username: username,
}
result, err := callPluginFunction[capabilities.IsAuthorizedRequest, *capabilities.IsAuthorizedResponse](ctx, s.plugin, FuncScrobblerIsAuthorized, input)
if err != nil || result == nil {
result, err := callPluginFunction[capabilities.IsAuthorizedRequest, bool](ctx, s.plugin, FuncScrobblerIsAuthorized, input)
if err != nil {
return false
}
return result.Authorized
return result
}
// NowPlaying sends a now playing notification to the scrobbler

View File

@ -17,10 +17,8 @@ func init() {
type testScrobbler struct{}
// IsAuthorized checks if a user is authorized.
func (t *testScrobbler) IsAuthorized(scrobbler.IsAuthorizedRequest) (*scrobbler.IsAuthorizedResponse, error) {
return &scrobbler.IsAuthorizedResponse{
Authorized: checkAuthConfig(),
}, nil
func (t *testScrobbler) IsAuthorized(scrobbler.IsAuthorizedRequest) (bool, error) {
return checkAuthConfig(), nil
}
// NowPlaying sends a now playing notification.