// Code generated by ndpgen. DO NOT EDIT. // // This file contains export wrappers for the Scrobbler capability. // It is intended for use in Navidrome plugins built with extism-pdk. use serde::{Deserialize, Serialize}; /// ScrobblerError represents an error type for scrobbling operations. pub type ScrobblerError = &'static str; /// ScrobblerErrorNotAuthorized indicates the user is not authorized. pub const SCROBBLER_ERROR_NOT_AUTHORIZED: ScrobblerError = "scrobbler(not_authorized)"; /// ScrobblerErrorRetryLater indicates the operation should be retried later. pub const SCROBBLER_ERROR_RETRY_LATER: ScrobblerError = "scrobbler(retry_later)"; /// ScrobblerErrorUnrecoverable indicates an unrecoverable error. pub const SCROBBLER_ERROR_UNRECOVERABLE: ScrobblerError = "scrobbler(unrecoverable)"; /// ArtistRef is a reference to an artist with name and optional MBID. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ArtistRef { /// ID is the internal Navidrome artist ID (if known). #[serde(default, skip_serializing_if = "String::is_empty")] pub id: String, /// Name is the artist name. #[serde(default)] pub name: String, /// MBID is the MusicBrainz ID for the artist. #[serde(default, skip_serializing_if = "String::is_empty")] pub mbid: String, } /// IsAuthorizedRequest is the request for authorization check. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct IsAuthorizedRequest { /// Username is the username of the user. #[serde(default)] pub username: String, } /// NowPlayingRequest is the request for now playing notification. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NowPlayingRequest { /// Username is the username of the user. #[serde(default)] pub username: String, /// Track is the track currently playing. #[serde(default)] pub track: TrackInfo, /// Position is the current playback position in seconds. #[serde(default)] pub position: i32, } /// ScrobbleRequest is the request for submitting a scrobble. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ScrobbleRequest { /// Username is the username of the user. #[serde(default)] pub username: String, /// Track is the track that was played. #[serde(default)] pub track: TrackInfo, /// Timestamp is the Unix timestamp when the track started playing. #[serde(default)] pub timestamp: i64, } /// TrackInfo contains track metadata for scrobbling. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TrackInfo { /// ID is the internal Navidrome track ID. #[serde(default)] pub id: String, /// Title is the track title. #[serde(default)] pub title: String, /// Album is the album name. #[serde(default)] pub album: String, /// Artist is the formatted artist name for display (e.g., "Artist1 • Artist2"). #[serde(default)] pub artist: String, /// AlbumArtist is the formatted album artist name for display. #[serde(default)] pub album_artist: String, /// Artists is the list of track artists. #[serde(default)] pub artists: Vec, /// AlbumArtists is the list of album artists. #[serde(default)] pub album_artists: Vec, /// Duration is the track duration in seconds. #[serde(default)] pub duration: f32, /// TrackNumber is the track number on the album. #[serde(default)] pub track_number: i32, /// DiscNumber is the disc number. #[serde(default)] pub disc_number: i32, /// MBZRecordingID is the MusicBrainz recording ID. #[serde(default, skip_serializing_if = "String::is_empty")] pub mbz_recording_id: String, /// MBZAlbumID is the MusicBrainz album/release ID. #[serde(default, skip_serializing_if = "String::is_empty")] pub mbz_album_id: String, /// MBZReleaseGroupID is the MusicBrainz release group ID. #[serde(default, skip_serializing_if = "String::is_empty")] pub mbz_release_group_id: String, /// MBZReleaseTrackID is the MusicBrainz release track ID. #[serde(default, skip_serializing_if = "String::is_empty")] pub mbz_release_track_id: String, } /// Error represents an error from a capability method. #[derive(Debug)] pub struct Error { pub message: String, } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.message) } } impl std::error::Error for Error {} impl Error { pub fn new(message: impl Into) -> Self { Self { message: message.into() } } } /// Scrobbler requires all methods to be implemented. /// Scrobbler provides scrobbling functionality to external services. /// This capability allows plugins to submit listening history to services like Last.fm, /// ListenBrainz, or custom scrobbling backends. /// /// All methods are required - plugins implementing this capability must provide /// 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; /// 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. fn scrobble(&self, req: ScrobbleRequest) -> Result<(), Error>; } /// Register all exports for the Scrobbler capability. /// This macro generates the WASM export functions for all trait methods. #[macro_export] macro_rules! register_scrobbler { ($plugin_type:ty) => { #[extism_pdk::plugin_fn] pub fn nd_scrobbler_is_authorized( req: extism_pdk::Json<$crate::scrobbler::IsAuthorizedRequest> ) -> extism_pdk::FnResult> { let plugin = <$plugin_type>::default(); let result = $crate::scrobbler::Scrobbler::is_authorized(&plugin, req.into_inner())?; Ok(extism_pdk::Json(result)) } #[extism_pdk::plugin_fn] pub fn nd_scrobbler_now_playing( req: extism_pdk::Json<$crate::scrobbler::NowPlayingRequest> ) -> extism_pdk::FnResult<()> { let plugin = <$plugin_type>::default(); $crate::scrobbler::Scrobbler::now_playing(&plugin, req.into_inner())?; Ok(()) } #[extism_pdk::plugin_fn] pub fn nd_scrobbler_scrobble( req: extism_pdk::Json<$crate::scrobbler::ScrobbleRequest> ) -> extism_pdk::FnResult<()> { let plugin = <$plugin_type>::default(); $crate::scrobbler::Scrobbler::scrobble(&plugin, req.into_inner())?; Ok(()) } }; }