🏡 index : quine-global/quintodrome.git

// 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};

// Helper functions for skip_serializing_if with numeric types
#[allow(dead_code)]
fn is_zero_i32(value: &i32) -> bool { *value == 0 }
#[allow(dead_code)]
fn is_zero_u32(value: &u32) -> bool { *value == 0 }
#[allow(dead_code)]
fn is_zero_i64(value: &i64) -> bool { *value == 0 }
#[allow(dead_code)]
fn is_zero_u64(value: &u64) -> bool { *value == 0 }
#[allow(dead_code)]
fn is_zero_f32(value: &f32) -> bool { *value == 0.0 }
#[allow(dead_code)]
fn is_zero_f64(value: &f64) -> bool { *value == 0.0 }

#[deprecated(note = "use nd_pdk::types::ArtistRef")]
pub type ArtistRef = nd_pdk_types::ArtistRef;
/// 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)";
/// 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,
}
/// PlaybackReportRequest is the request for playback report notifications.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PlaybackReportRequest {
    /// Username is the username of the user.
    #[serde(default)]
    pub username: String,
    /// Track is the track being played.
    #[serde(default)]
    pub track: TrackInfo,
    /// State is the current playback state (starting/playing/paused/stopped/expired).
    #[serde(default)]
    pub state: String,
    /// PositionMs is the current playback position in milliseconds.
    #[serde(default)]
    pub position_ms: i64,
    /// PlaybackRate is the playback speed (1.0 = normal).
    #[serde(default)]
    pub playback_rate: f64,
    /// PlayerId is the unique client identifier.
    #[serde(default)]
    pub player_id: String,
    /// PlayerName is the human-readable player name.
    #[serde(default)]
    pub player_name: String,
    /// Timestamp is the Unix timestamp when this report was generated.
    #[serde(default)]
    pub timestamp: i64,
}
/// 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.
#[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<nd_pdk_types::ArtistRef>,
    /// AlbumArtists is the list of album artists.
    #[serde(default)]
    pub album_artists: Vec<nd_pdk_types::ArtistRef>,
    /// 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,
    /// LibraryID is the ID of the library the track belongs to.
    /// Only included if the plugin has library permission with filesystem access for the track's library.
    #[serde(default, skip_serializing_if = "is_zero_i32")]
    pub library_id: i32,
    /// Path is the full path to the track file, relative to the library root.
    /// Only included if the plugin has library permission with filesystem access for the track's library.
    #[serde(default, skip_serializing_if = "String::is_empty")]
    pub path: 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<String>) -> 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 four functions: IsAuthorized, NowPlaying, Scrobble, and PlaybackReport.
pub trait Scrobbler {
    /// IsAuthorized - IsAuthorized checks if a user is authorized to scrobble to this service.
    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.
    fn scrobble(&self, req: ScrobbleRequest) -> Result<(), Error>;
    /// PlaybackReport - PlaybackReport sends a playback state report to the scrobbling service.
    fn playback_report(&self, req: PlaybackReportRequest) -> 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<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))
        }
        #[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(())
        }
        #[extism_pdk::plugin_fn]
        pub fn nd_scrobbler_playback_report(
            req: extism_pdk::Json<$crate::scrobbler::PlaybackReportRequest>
        ) -> extism_pdk::FnResult<()> {
            let plugin = <$plugin_type>::default();
            $crate::scrobbler::Scrobbler::playback_report(&plugin, req.into_inner())?;
            Ok(())
        }
    };
}