diff --git a/src/main.rs b/src/main.rs index 8f0eb0f..b5e2e87 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,8 @@ -use std::ops::Deref; use std::process::exit; use std::sync::mpsc::{channel, Receiver}; use player::errors::PlayerError; -use ssonic::response::PlayQueue; mod audio; mod config; diff --git a/src/player/errors.rs b/src/player/errors.rs index 63317ad..c1f6145 100644 --- a/src/player/errors.rs +++ b/src/player/errors.rs @@ -1,6 +1,5 @@ #[derive(Debug)] pub enum PlayerError { - PlaylistEmpty, UserQuit, } @@ -9,7 +8,6 @@ impl std::error::Error for PlayerError {} impl std::fmt::Display for PlayerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::PlaylistEmpty => write!(f, "Playlist empty"), Self::UserQuit => write!(f, "User quit signalled"), } } diff --git a/src/player/mod.rs b/src/player/mod.rs index 9ed4f2a..862d41d 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -18,6 +18,7 @@ use crate::{ pub enum PlayerEvent { AddSongList(Vec), UserQuit, + PlayNext, } pub struct Player { diff --git a/src/player/player.rs b/src/player/player.rs index 5ba72ec..67b147c 100644 --- a/src/player/player.rs +++ b/src/player/player.rs @@ -1,4 +1,4 @@ -use std::{sync::mpsc::TryRecvError, time::Duration}; +use std::{sync::mpsc::TryRecvError, thread, time::Duration}; use crossterm::event::{poll, read, Event, KeyCode}; @@ -16,6 +16,7 @@ impl Player { fn init_playlist(&self) -> Result<(), Error> { self.api_chan .send(APIEvent::FetchRandom(self.settings.subsonic.random_limit))?; + self.player_chan_in.send(PlayerEvent::PlayNext)?; Ok(()) } @@ -37,6 +38,7 @@ impl Player { match e { PlayerEvent::UserQuit => return Err(Box::new(PlayerError::UserQuit)), PlayerEvent::AddSongList(list) => self.playlist.append(list), + PlayerEvent::PlayNext => self.play_next()?, _ => unimplemented!(), } } @@ -60,4 +62,19 @@ impl Player { fn update(&mut self) -> Result<(), Error> { Ok(()) } + + fn play_next(&mut self) -> Result<(), Error> { + let song = match self.playlist.get_next() { + None => { + // no song exists, requeue the event + self.player_chan_in.send(PlayerEvent::PlayNext)?; + return Ok(()); + } + Some(s) => s, + }; + if let Some(cover_id) = song.cover_art { + self.api_chan.send(APIEvent::FetchCoverArt(cover_id))?; + } + Ok(()) + } } diff --git a/src/player/playlist.rs b/src/player/playlist.rs index 7f8577c..f18af86 100644 --- a/src/player/playlist.rs +++ b/src/player/playlist.rs @@ -1,19 +1,14 @@ use std::{borrow::BorrowMut, collections::VecDeque}; -use crate::{ssonic::response::Song, utils::Error}; - -use super::errors::PlayerError; +use crate::ssonic::response::Song; pub struct Playlist { song_list: VecDeque, } impl Playlist { - pub fn get_next(&mut self) -> Result { - match self.song_list.pop_front() { - Some(song) => Ok(song), - None => Err(Box::new(PlayerError::PlaylistEmpty)), - } + pub fn get_next(&mut self) -> Option { + self.song_list.pop_front() } pub fn append(&mut self, list: Vec) { diff --git a/src/ssonic/client.rs b/src/ssonic/client.rs index 01088d9..0e0936c 100644 --- a/src/ssonic/client.rs +++ b/src/ssonic/client.rs @@ -1,4 +1,7 @@ -use reqwest::{blocking::Response, header::ACCEPT}; +use reqwest::{ + blocking::Response, + header::{ACCEPT, CONTENT_TYPE}, +}; use serde::Serialize; use crate::{ @@ -18,21 +21,31 @@ impl APIClient { loop { let player_resp = match self.api_requests.recv()? { super::APIEvent::FetchRandom(n) => self.get_random(n)?, + super::APIEvent::FetchCoverArt(String) => self.get_cover_art(String)?, + _ => unimplemented!(), }; self.player_chan.send(player_resp)?; } } - fn get_random(&mut self, n: i32) -> Result { - let response = self.request("/getRandomSongs", Some(&[("size", n.to_string())]))?; + fn get_cover_art(&mut self, cover_id: String) -> Result { + let response = self.request("/getCoverArt", Some(&[("id", cover_id)]))?; if !response.status().is_success() { return Err(Box::new(APIError::StatusError( response.status(), - "/getRandomSongs", + "/getCoverArt", ))); - } + }; + if response.headers()[CONTENT_TYPE].to_str()? == "application/json" { + self.validate(response, "/getCoverArt")?; + }; + unimplemented!() + } + + fn get_random(&mut self, n: i32) -> Result { + let response = self.request("/getRandomSongs", Some(&[("size", n.to_string())]))?; let song_list = self - .validate(response)? + .validate(response, "/getRandomSongs")? .subsonic_response .random_songs .unwrap() @@ -43,16 +56,17 @@ impl APIClient { fn ping(&mut self) -> Result<(), Error> { let response = self.request::<[(&str, String); 0]>("/ping.view", None)?; - self.validate(response)?; + self.validate(response, "/ping.view")?; Ok(()) } - fn validate(&mut self, response: Response) -> Result { + fn validate( + &mut self, + response: Response, + path: &'static str, + ) -> Result { if !response.status().is_success() { - return Err(Box::new(APIError::StatusError( - response.status(), - "/ping.view", - ))); + return Err(Box::new(APIError::StatusError(response.status(), path))); } let ssonic_response: response::Response = response.json()?; if ssonic_response.subsonic_response.status != "ok" { diff --git a/src/ssonic/mod.rs b/src/ssonic/mod.rs index 2b0c3b8..f98d82d 100644 --- a/src/ssonic/mod.rs +++ b/src/ssonic/mod.rs @@ -12,6 +12,8 @@ use crate::{config::Settings, player::PlayerEvent, utils::Error}; pub enum APIEvent { FetchRandom(i32), + FetchCoverArt(String), + StreamSong(String), } pub struct APIClient { diff --git a/src/ssonic/response.rs b/src/ssonic/response.rs index b6117c5..7d0e8d5 100644 --- a/src/ssonic/response.rs +++ b/src/ssonic/response.rs @@ -14,8 +14,6 @@ pub struct SSonicResponse { #[serde(skip_serializing_if = "Option::is_none")] #[serde(alias = "randomSongs")] pub random_songs: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub play_queue: Option, } #[derive(Deserialize)] @@ -35,10 +33,7 @@ pub struct Song { pub title: String, #[serde(skip_serializing_if = "Option::is_none")] pub artist: Option, - #[serde(alias = "coverArt")] #[serde(skip_serializing_if = "Option::is_none")] + #[serde(alias = "coverArt")] pub cover_art: Option, } - -#[derive(Deserialize)] -pub struct PlayQueue {}