//! Subsonic API response models use serde::{Deserialize, Serialize}; /// Wrapper for all Subsonic API responses #[derive(Debug, Deserialize)] pub struct SubsonicResponse { #[serde(rename = "subsonic-response")] pub subsonic_response: SubsonicResponseInner, } #[derive(Debug, Deserialize)] pub struct SubsonicResponseInner { pub status: String, #[allow(dead_code)] // Present in API response, needed for deserialization pub version: String, #[serde(default)] pub error: Option, #[serde(flatten)] pub data: Option, } /// API error response #[derive(Debug, Deserialize)] pub struct ApiError { pub code: i32, pub message: String, } /// Artists response wrapper #[derive(Debug, Deserialize)] pub struct ArtistsData { pub artists: ArtistsIndex, } #[derive(Debug, Deserialize)] pub struct ArtistsIndex { #[serde(default)] pub index: Vec, } #[derive(Debug, Deserialize)] pub struct ArtistIndex { #[allow(dead_code)] // Present in API response, needed for deserialization pub name: String, #[serde(default)] pub artist: Vec, } /// Artist #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Artist { pub id: String, pub name: String, #[serde(default, rename = "albumCount")] pub album_count: Option, #[serde(default, rename = "coverArt")] pub cover_art: Option, } /// Artist detail with albums #[derive(Debug, Deserialize)] pub struct ArtistData { pub artist: ArtistDetail, } #[derive(Debug, Deserialize)] pub struct ArtistDetail { pub id: String, pub name: String, #[serde(default)] pub album: Vec, } /// Album #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Album { pub id: String, pub name: String, #[serde(default)] pub artist: Option, #[serde(default, rename = "artistId")] pub artist_id: Option, #[serde(default, rename = "coverArt")] pub cover_art: Option, #[serde(default, rename = "songCount")] pub song_count: Option, #[serde(default)] pub duration: Option, #[serde(default)] pub year: Option, #[serde(default)] pub genre: Option, } /// Album detail with songs #[derive(Debug, Deserialize)] pub struct AlbumData { pub album: AlbumDetail, } #[derive(Debug, Deserialize)] pub struct AlbumDetail { pub id: String, pub name: String, #[serde(default)] pub artist: Option, #[serde(default, rename = "artistId")] pub artist_id: Option, #[serde(default)] pub year: Option, #[serde(default)] pub song: Vec, } /// Song/Media item (called "Child" in Subsonic API) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Child { pub id: String, #[serde(default)] pub parent: Option, #[serde(default, rename = "isDir")] pub is_dir: bool, pub title: String, #[serde(default)] pub album: Option, #[serde(default)] pub artist: Option, #[serde(default)] pub track: Option, #[serde(default)] pub year: Option, #[serde(default)] pub genre: Option, #[serde(default, rename = "coverArt")] pub cover_art: Option, #[serde(default)] pub size: Option, #[serde(default, rename = "contentType")] pub content_type: Option, #[serde(default)] pub suffix: Option, #[serde(default)] pub duration: Option, #[serde(default, rename = "bitRate")] pub bit_rate: Option, #[serde(default)] pub path: Option, #[serde(default, rename = "discNumber")] pub disc_number: Option, } impl Child { /// Format duration as MM:SS pub fn format_duration(&self) -> String { match self.duration { Some(d) => { let mins = d / 60; let secs = d % 60; format!("{:02}:{:02}", mins, secs) } None => "--:--".to_string(), } } } /// Playlists response #[derive(Debug, Deserialize)] pub struct PlaylistsData { pub playlists: PlaylistsInner, } #[derive(Debug, Deserialize)] pub struct PlaylistsInner { #[serde(default)] pub playlist: Vec, } /// Playlist #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Playlist { pub id: String, pub name: String, #[serde(default)] pub owner: Option, #[serde(default, rename = "songCount")] pub song_count: Option, #[serde(default)] pub duration: Option, #[serde(default, rename = "coverArt")] pub cover_art: Option, #[serde(default)] pub public: Option, #[serde(default)] pub comment: Option, } /// Playlist detail with songs #[derive(Debug, Deserialize)] pub struct PlaylistData { pub playlist: PlaylistDetail, } #[derive(Debug, Deserialize)] pub struct PlaylistDetail { pub id: String, pub name: String, #[serde(default)] pub owner: Option, #[serde(default, rename = "songCount")] pub song_count: Option, #[serde(default)] pub duration: Option, #[serde(default)] pub entry: Vec, } /// Ping response (for testing connection) #[derive(Debug, Deserialize)] pub struct PingData {}