Init player with random songs
This commit is contained in:
parent
4889026749
commit
d6c9bf4d3b
9 changed files with 119 additions and 32 deletions
|
@ -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> {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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
23
src/player/player.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -9,3 +9,7 @@ pub fn generate_random_salt() -> String {
|
|||
.map(|c| c as char)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn restore() {
|
||||
ratatui::restore()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue