- Delete src/audio/queue.rs (321 lines, PlayQueue never used) - Remove #![allow(dead_code)] from audio, subsonic, and mpris module roots - Remove unused MpvEvent2 enum, playlist_clear, get_volume, get_path, is_eof, observe_property from mpv.rs - Remove unused DEFAULT_DEVICE_ID, is_available, get_effective_rate from pipewire.rs (and associated dead test) - Remove unused search(), get_cover_art_url() from subsonic client - Remove unused SearchResult3Data, SearchResult3 model structs - Move parse_song_id_from_url into #[cfg(test)] block (only used by tests) - Add targeted #[allow(dead_code)] on deserialization-only fields (MpvEvent, SubsonicResponseInner.version, ArtistIndex.name) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
222 lines
5.3 KiB
Rust
222 lines
5.3 KiB
Rust
//! Subsonic API response models
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Wrapper for all Subsonic API responses
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct SubsonicResponse<T> {
|
|
#[serde(rename = "subsonic-response")]
|
|
pub subsonic_response: SubsonicResponseInner<T>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct SubsonicResponseInner<T> {
|
|
pub status: String,
|
|
#[allow(dead_code)] // Present in API response, needed for deserialization
|
|
pub version: String,
|
|
#[serde(default)]
|
|
pub error: Option<ApiError>,
|
|
#[serde(flatten)]
|
|
pub data: Option<T>,
|
|
}
|
|
|
|
/// 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<ArtistIndex>,
|
|
}
|
|
|
|
#[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>,
|
|
}
|
|
|
|
/// Artist
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Artist {
|
|
pub id: String,
|
|
pub name: String,
|
|
#[serde(default, rename = "albumCount")]
|
|
pub album_count: Option<i32>,
|
|
#[serde(default, rename = "coverArt")]
|
|
pub cover_art: Option<String>,
|
|
}
|
|
|
|
/// 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>,
|
|
}
|
|
|
|
/// Album
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Album {
|
|
pub id: String,
|
|
pub name: String,
|
|
#[serde(default)]
|
|
pub artist: Option<String>,
|
|
#[serde(default, rename = "artistId")]
|
|
pub artist_id: Option<String>,
|
|
#[serde(default, rename = "coverArt")]
|
|
pub cover_art: Option<String>,
|
|
#[serde(default, rename = "songCount")]
|
|
pub song_count: Option<i32>,
|
|
#[serde(default)]
|
|
pub duration: Option<i32>,
|
|
#[serde(default)]
|
|
pub year: Option<i32>,
|
|
#[serde(default)]
|
|
pub genre: Option<String>,
|
|
}
|
|
|
|
/// 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<String>,
|
|
#[serde(default, rename = "artistId")]
|
|
pub artist_id: Option<String>,
|
|
#[serde(default)]
|
|
pub year: Option<i32>,
|
|
#[serde(default)]
|
|
pub song: Vec<Child>,
|
|
}
|
|
|
|
/// Song/Media item (called "Child" in Subsonic API)
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Child {
|
|
pub id: String,
|
|
#[serde(default)]
|
|
pub parent: Option<String>,
|
|
#[serde(default, rename = "isDir")]
|
|
pub is_dir: bool,
|
|
pub title: String,
|
|
#[serde(default)]
|
|
pub album: Option<String>,
|
|
#[serde(default)]
|
|
pub artist: Option<String>,
|
|
#[serde(default)]
|
|
pub track: Option<i32>,
|
|
#[serde(default)]
|
|
pub year: Option<i32>,
|
|
#[serde(default)]
|
|
pub genre: Option<String>,
|
|
#[serde(default, rename = "coverArt")]
|
|
pub cover_art: Option<String>,
|
|
#[serde(default)]
|
|
pub size: Option<i64>,
|
|
#[serde(default, rename = "contentType")]
|
|
pub content_type: Option<String>,
|
|
#[serde(default)]
|
|
pub suffix: Option<String>,
|
|
#[serde(default)]
|
|
pub duration: Option<i32>,
|
|
#[serde(default, rename = "bitRate")]
|
|
pub bit_rate: Option<i32>,
|
|
#[serde(default)]
|
|
pub path: Option<String>,
|
|
#[serde(default, rename = "discNumber")]
|
|
pub disc_number: Option<i32>,
|
|
}
|
|
|
|
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>,
|
|
}
|
|
|
|
/// Playlist
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Playlist {
|
|
pub id: String,
|
|
pub name: String,
|
|
#[serde(default)]
|
|
pub owner: Option<String>,
|
|
#[serde(default, rename = "songCount")]
|
|
pub song_count: Option<i32>,
|
|
#[serde(default)]
|
|
pub duration: Option<i32>,
|
|
#[serde(default, rename = "coverArt")]
|
|
pub cover_art: Option<String>,
|
|
#[serde(default)]
|
|
pub public: Option<bool>,
|
|
#[serde(default)]
|
|
pub comment: Option<String>,
|
|
}
|
|
|
|
/// 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<String>,
|
|
#[serde(default, rename = "songCount")]
|
|
pub song_count: Option<i32>,
|
|
#[serde(default)]
|
|
pub duration: Option<i32>,
|
|
#[serde(default)]
|
|
pub entry: Vec<Child>,
|
|
}
|
|
|
|
/// Ping response (for testing connection)
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct PingData {}
|
|
|