use super::{ContextType, Cursors, EpisodeId, ExternalUrls, ItemType, Track, TrackId, TrackItem};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Device {
pub id: Option<String>,
pub is_active: bool,
pub is_private_session: bool,
pub is_restricted: bool,
pub name: String,
#[serde(rename = "type")]
pub type_: String,
pub volume_percent: Option<u8>,
pub supports_volume: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Devices {
#[serde(default)]
pub devices: Vec<Device>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum RepeatState {
Track,
Context,
Off,
}
impl std::fmt::Display for RepeatState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Track => "track",
Self::Context => "context",
Self::Off => "off",
};
write!(f, "{s}")
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Context {
#[serde(rename = "type")]
pub type_: ItemType,
pub href: Option<String>,
pub external_urls: ExternalUrls,
pub uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum CurrentlyPlayingType {
Track,
Episode,
Ad,
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PlaybackState {
pub device: Device,
pub repeat_state: RepeatState,
pub shuffle_state: bool,
pub context: Option<Context>,
pub timestamp: Option<i64>,
pub progress_ms: Option<u32>,
pub is_playing: bool,
pub item: Option<TrackItem>,
pub currently_playing_type: CurrentlyPlayingType,
pub actions: Actions,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Actions {
#[serde(skip_serializing_if = "Option::is_none")]
pub interrupting_playback: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pausing: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resuming: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seeking: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub skipping_next: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub skipping_prev: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub toggling_repeat_context: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub toggling_shuffle: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub toggling_repeat_track: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transferring_playback: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CurrentlyPlaying {
pub context: Option<Context>,
pub timestamp: Option<i64>,
pub progress_ms: Option<u32>,
pub is_playing: bool,
pub item: Option<TrackItem>,
pub currently_playing_type: CurrentlyPlayingType,
pub actions: Actions,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PlayHistory {
pub track: Track,
pub played_at: String,
pub context: Context,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RecentlyPlayedTracks {
pub href: String,
pub limit: usize,
pub next: Option<String>,
pub cursors: Option<Cursors>,
pub total: Option<usize>,
pub items: Vec<PlayHistory>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Queue {
pub currently_playing: Option<TrackItem>,
pub queue: Vec<TrackItem>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Offset {
Position(usize),
Uri(ContextType),
}
impl From<usize> for Offset {
fn from(position: usize) -> Self {
Self::Position(position)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QueryRange {
Before(i64),
After(i64),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PlaylistItem {
Track(TrackId),
Episode(EpisodeId),
}
impl From<TrackId> for PlaylistItem {
fn from(track: TrackId) -> Self {
Self::Track(track)
}
}
impl From<EpisodeId> for PlaylistItem {
fn from(episode: EpisodeId) -> Self {
Self::Episode(episode)
}
}
impl std::fmt::Display for PlaylistItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Track(track) => track.uri(),
Self::Episode(episode) => episode.uri(),
};
write!(f, "{s}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn playback_state() {
let json = r#"
{
"device": {
"id": "string",
"is_active": false,
"is_private_session": false,
"is_restricted": false,
"name": "Kitchen speaker",
"type": "computer",
"volume_percent": 59,
"supports_volume": false
},
"repeat_state": "off",
"shuffle_state": false,
"context": {
"type": "track",
"href": "string",
"external_urls": {
"spotify": "string"
},
"uri": "string"
},
"timestamp": 0,
"progress_ms": 0,
"is_playing": false,
"item": {
"album": {
"album_type": "compilation",
"total_tracks": 9,
"available_markets": ["CA", "BR", "IT"],
"external_urls": {
"spotify": "string"
},
"href": "string",
"id": "2up3OPMp9Tb4dAKM2erWXQ",
"images": [
{
"url": "https://i.scdn.co/image/ab67616d00001e02ff9ca10b55ce82ae553c8228",
"height": 300,
"width": 300
}
],
"name": "string",
"release_date": "1981-12",
"release_date_precision": "year",
"restrictions": {
"reason": "market"
},
"type": "album",
"uri": "spotify:album:2up3OPMp9Tb4dAKM2erWXQ",
"artists": [
{
"external_urls": {
"spotify": "string"
},
"href": "string",
"id": "string",
"name": "string",
"type": "artist",
"uri": "string"
}
]
},
"artists": [
{
"external_urls": {
"spotify": "string"
},
"followers": {
"href": "string",
"total": 0
},
"genres": ["Prog rock", "Grunge"],
"href": "string",
"id": "string",
"images": [
{
"url": "https://i.scdn.co/image/ab67616d00001e02ff9ca10b55ce82ae553c8228",
"height": 300,
"width": 300
}
],
"name": "string",
"popularity": 0,
"type": "artist",
"uri": "string"
}
],
"available_markets": ["US"],
"disc_number": 0,
"duration_ms": 0,
"explicit": false,
"external_ids": {
"isrc": "string",
"ean": "string",
"upc": "string"
},
"external_urls": {
"spotify": "string"
},
"href": "string",
"id": "string",
"is_playable": false,
"linked_from": {},
"restrictions": {
"reason": "string"
},
"name": "string",
"popularity": 0,
"preview_url": "string",
"track_number": 0,
"type": "track",
"uri": "string",
"is_local": false
},
"currently_playing_type": "unknown",
"actions": {
"interrupting_playback": false,
"pausing": false,
"resuming": false,
"seeking": false,
"skipping_next": false,
"skipping_prev": false,
"toggling_repeat_context": false,
"toggling_shuffle": false,
"toggling_repeat_track": false,
"transferring_playback": false
}
}
"#;
crate::test::assert_deserialized!(PlaybackState, json);
}
}