From d6c9bf4d3bfd2e9f1d6414a920e100f5c5238b4f Mon Sep 17 00:00:00 2001 From: Muaz Ahmad Date: Wed, 27 Nov 2024 16:01:29 +0500 Subject: [PATCH] Init player with random songs --- src/config/mod.rs | 6 ++++++ src/main.rs | 5 +++-- src/player/begin.rs | 11 ----------- src/player/mod.rs | 44 +++++++++++++++++++++++++++++++++--------- src/player/player.rs | 23 ++++++++++++++++++++++ src/ssonic/client.rs | 36 +++++++++++++++++++++++++++++----- src/ssonic/mod.rs | 6 ++++-- src/ssonic/response.rs | 16 ++++++++++++--- src/utils.rs | 4 ++++ 9 files changed, 119 insertions(+), 32 deletions(-) delete mode 100644 src/player/begin.rs create mode 100644 src/player/player.rs diff --git a/src/config/mod.rs b/src/config/mod.rs index 76d294b..45939e0 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -16,6 +16,12 @@ pub struct Subsonic { pub username: String, #[serde(default)] pub password: String, // Only optional in file, will otherwise pull from Env var + #[serde(default = "default_random_limit")] + pub random_limit: i32, +} + +fn default_random_limit() -> i32 { + 10 } pub fn init() -> Result, Error> { diff --git a/src/main.rs b/src/main.rs index 5f6fe1f..83a21ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,20 +16,20 @@ fn init() -> Result, utils::Error> { audio::init(settings.clone(), error_in.clone(), player_events_in.clone())?; let api_event_chan = ssonic::init(settings.clone(), error_in.clone(), player_events_in.clone())?; - let mut player = player::init( + player::init( settings.clone(), audio_event_chan, api_event_chan, error_in.clone(), player_events_out, )?; - player.begin()?; Ok(error_out) } fn main() { let err_chan = match init() { Err(x) => { + utils::restore(); eprintln!("{}", x); exit(1) } @@ -38,6 +38,7 @@ fn main() { match err_chan.recv() { Err(_) => exit(0), Ok(err) => { + utils::restore(); eprintln!("{}", err); exit(1) } diff --git a/src/player/begin.rs b/src/player/begin.rs deleted file mode 100644 index 632769e..0000000 --- a/src/player/begin.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::utils::Error; - -use super::Player; - -impl Player { - pub fn begin(&mut self) -> Result<(), Error> { - std::thread::sleep(std::time::Duration::from_secs(5)); - ratatui::restore(); - Ok(()) - } -} diff --git a/src/player/mod.rs b/src/player/mod.rs index 88356dd..c0c20bf 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -1,16 +1,31 @@ -use std::sync::{ - mpsc::{Receiver, Sender}, - Arc, +use std::{ + sync::{ + mpsc::{Receiver, Sender}, + Arc, + }, + thread, }; use ratatui::DefaultTerminal; -use crate::{audio::AudioEvent, config::Settings, ssonic::APIEvent, utils::Error}; +use crate::{ + audio::AudioEvent, + config::Settings, + ssonic::{response::Song, APIEvent}, + utils::Error, +}; -pub struct PlayerEvent {} +pub enum PlayerEvent { + AddSongList(Vec), +} pub struct Player { terminal: DefaultTerminal, + settings: Arc, + audio_chan: Sender, + api_chan: Sender, + error_chan: Sender, + player_chan: Receiver, } pub fn init( @@ -19,9 +34,20 @@ pub fn init( api_chan: Sender, error_chan: Sender, player_chan: Receiver, -) -> Result { - let mut terminal = ratatui::init(); - Ok(Player { terminal }) +) -> Result<(), Error> { + let terminal = ratatui::init(); + thread::spawn(move || { + let mut player = Player { + terminal, + settings, + audio_chan, + api_chan, + error_chan, + player_chan, + }; + player.begin(); + }); + Ok(()) } -mod begin; +mod player; diff --git a/src/player/player.rs b/src/player/player.rs new file mode 100644 index 0000000..82b7be3 --- /dev/null +++ b/src/player/player.rs @@ -0,0 +1,23 @@ +use crate::{ssonic::APIEvent, utils::Error}; + +use super::Player; + +impl Player { + pub fn begin(&mut self) -> Result<(), Error> { + self.init_playlist()?; + self.run()?; + Ok(()) + } + + fn init_playlist(&self) -> Result<(), Error> { + self.api_chan + .send(APIEvent::FetchRandom(self.settings.subsonic.random_limit))?; + Ok(()) + } + + fn run(&mut self) -> Result<(), Error> { + loop { + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } +} diff --git a/src/ssonic/client.rs b/src/ssonic/client.rs index 03f97bb..01088d9 100644 --- a/src/ssonic/client.rs +++ b/src/ssonic/client.rs @@ -2,6 +2,7 @@ use reqwest::{blocking::Response, header::ACCEPT}; use serde::Serialize; use crate::{ + player::PlayerEvent, ssonic::{errors::APIError, response}, utils::{generate_random_salt, Error}, }; @@ -13,15 +14,40 @@ use super::APIClient; impl APIClient { pub fn begin(&mut self) -> Result<(), Error> { - self.validate()?; + self.ping()?; loop { - unimplemented!(); + let player_resp = match self.api_requests.recv()? { + super::APIEvent::FetchRandom(n) => self.get_random(n)?, + }; + self.player_chan.send(player_resp)?; } } - fn validate(&mut self) -> Result<(), Error> { - let response = self.request::<[(&str, String); 0]>("/ping.view", None)?; + fn get_random(&mut self, n: i32) -> Result { + let response = self.request("/getRandomSongs", Some(&[("size", n.to_string())]))?; + if !response.status().is_success() { + return Err(Box::new(APIError::StatusError( + response.status(), + "/getRandomSongs", + ))); + } + let song_list = self + .validate(response)? + .subsonic_response + .random_songs + .unwrap() + .songs; + Ok(PlayerEvent::AddSongList(song_list)) + } + + fn ping(&mut self) -> Result<(), Error> { + let response = self.request::<[(&str, String); 0]>("/ping.view", None)?; + self.validate(response)?; + Ok(()) + } + + fn validate(&mut self, response: Response) -> Result { if !response.status().is_success() { return Err(Box::new(APIError::StatusError( response.status(), @@ -34,7 +60,7 @@ impl APIClient { ssonic_response.subsonic_response.error.unwrap().message, ))); } - Ok(()) + Ok(ssonic_response) } fn generate_random_token(&self) -> (String, String) { diff --git a/src/ssonic/mod.rs b/src/ssonic/mod.rs index f1d7f4e..2b0c3b8 100644 --- a/src/ssonic/mod.rs +++ b/src/ssonic/mod.rs @@ -10,7 +10,9 @@ use reqwest::blocking::Client; use crate::{config::Settings, player::PlayerEvent, utils::Error}; -pub enum APIEvent {} +pub enum APIEvent { + FetchRandom(i32), +} pub struct APIClient { settings: Arc, @@ -48,4 +50,4 @@ pub fn init( mod client; mod errors; -mod response; +pub mod response; diff --git a/src/ssonic/response.rs b/src/ssonic/response.rs index aa8ccb7..b6117c5 100644 --- a/src/ssonic/response.rs +++ b/src/ssonic/response.rs @@ -12,7 +12,8 @@ pub struct SSonicResponse { #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub random_songs: Option>, + #[serde(alias = "randomSongs")] + pub random_songs: Option, #[serde(skip_serializing_if = "Option::is_none")] pub play_queue: Option, } @@ -22,12 +23,21 @@ pub struct Error { pub message: String, } +#[derive(Deserialize)] +pub struct SongList { + #[serde(alias = "song")] + pub songs: Vec, +} + #[derive(Deserialize)] pub struct Song { pub id: String, pub title: String, - pub artist: String, - pub cover_art: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub artist: Option, + #[serde(alias = "coverArt")] + #[serde(skip_serializing_if = "Option::is_none")] + pub cover_art: Option, } #[derive(Deserialize)] diff --git a/src/utils.rs b/src/utils.rs index 6d8094a..0a47296 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -9,3 +9,7 @@ pub fn generate_random_salt() -> String { .map(|c| c as char) .collect() } + +pub fn restore() { + ratatui::restore() +}