Init player with random songs

This commit is contained in:
Muaz Ahmad 2024-11-27 16:01:29 +05:00
parent 4889026749
commit d6c9bf4d3b
9 changed files with 119 additions and 32 deletions

View file

@ -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<Arc<Settings>, Error> {

View file

@ -16,20 +16,20 @@ fn init() -> Result<Receiver<utils::Error>, 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)
}

View file

@ -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(())
}
}

View file

@ -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<Song>),
}
pub struct Player {
terminal: DefaultTerminal,
settings: Arc<Settings>,
audio_chan: Sender<AudioEvent>,
api_chan: Sender<APIEvent>,
error_chan: Sender<Error>,
player_chan: Receiver<PlayerEvent>,
}
pub fn init(
@ -19,9 +34,20 @@ pub fn init(
api_chan: Sender<APIEvent>,
error_chan: Sender<Error>,
player_chan: Receiver<PlayerEvent>,
) -> Result<Player, Error> {
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;

23
src/player/player.rs Normal file
View file

@ -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));
}
}
}

View file

@ -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<PlayerEvent, Error> {
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<response::Response, Error> {
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) {

View file

@ -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<Settings>,
@ -48,4 +50,4 @@ pub fn init(
mod client;
mod errors;
mod response;
pub mod response;

View file

@ -12,7 +12,8 @@ pub struct SSonicResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<Error>,
#[serde(skip_serializing_if = "Option::is_none")]
pub random_songs: Option<Vec<Song>>,
#[serde(alias = "randomSongs")]
pub random_songs: Option<SongList>,
#[serde(skip_serializing_if = "Option::is_none")]
pub play_queue: Option<PlayQueue>,
}
@ -22,12 +23,21 @@ pub struct Error {
pub message: String,
}
#[derive(Deserialize)]
pub struct SongList {
#[serde(alias = "song")]
pub songs: Vec<Song>,
}
#[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<String>,
#[serde(alias = "coverArt")]
#[serde(skip_serializing_if = "Option::is_none")]
pub cover_art: Option<String>,
}
#[derive(Deserialize)]

View file

@ -9,3 +9,7 @@ pub fn generate_random_salt() -> String {
.map(|c| c as char)
.collect()
}
pub fn restore() {
ratatui::restore()
}