use serde::{Deserialize, Serialize}; use chrono::{DateTime, Utc}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SpotifyConfig { pub client_id: String, pub client_secret: String, pub redirect_uri: String, pub scopes: Vec, } impl Default for SpotifyConfig { fn default() -> Self { Self { client_id: String::new(), client_secret: String::new(), redirect_uri: "https://spotify.tougie.live".to_string(), scopes: vec![ "user-read-currently-playing".to_string(), "user-read-playback-state".to_string(), ], } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TokenInfo { pub access_token: String, pub refresh_token: Option, pub expires_at: DateTime, pub token_type: String, } #[derive(Debug, Deserialize)] pub struct TokenResponse { pub access_token: String, pub token_type: String, pub scope: String, pub expires_in: u64, pub refresh_token: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Artist { pub id: String, pub name: String, pub external_urls: ExternalUrls, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Album { pub id: String, pub name: String, pub artists: Vec, pub external_urls: ExternalUrls, pub images: Vec, pub release_date: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Track { pub id: String, pub name: String, pub artists: Vec, pub album: Album, pub duration_ms: u64, pub explicit: bool, pub external_urls: ExternalUrls, pub popularity: Option, pub preview_url: Option, pub track_number: u32, pub is_local: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ExternalUrls { pub spotify: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Image { pub url: String, pub height: Option, pub width: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Context { pub external_urls: ExternalUrls, pub href: String, #[serde(rename = "type")] pub context_type: String, pub uri: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Device { pub id: Option, pub is_active: bool, pub is_private_session: bool, pub is_restricted: bool, pub name: String, #[serde(rename = "type")] pub device_type: String, pub volume_percent: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CurrentTrack { pub timestamp: u64, pub context: Option, pub progress_ms: Option, pub item: Option, pub currently_playing_type: String, pub actions: Actions, pub is_playing: bool, pub device: Option, pub repeat_state: Option, pub shuffle_state: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Actions { pub interrupting_playback: Option, pub pausing: Option, pub resuming: Option, pub seeking: Option, pub skipping_next: Option, pub skipping_prev: Option, pub toggling_repeat_context: Option, pub toggling_shuffle: Option, pub toggling_repeat_track: Option, pub transferring_playback: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SimplifiedCurrentTrack { pub track_name: String, pub artist_name: String, pub album_name: String, pub is_playing: bool, pub progress_ms: Option, pub duration_ms: u64, pub external_url: String, pub image_url: Option, pub timestamp: DateTime, } impl From for Option { fn from(current: CurrentTrack) -> Self { let track = current.item?; let artist_names: Vec = track.artists.iter().map(|a| a.name.clone()).collect(); let image_url = track.album.images.first().map(|img| img.url.clone()); Some(SimplifiedCurrentTrack { track_name: track.name, artist_name: artist_names.join(", "), album_name: track.album.name, is_playing: current.is_playing, progress_ms: current.progress_ms, duration_ms: track.duration_ms, external_url: track.external_urls.spotify, image_url, timestamp: Utc::now(), }) } }