From 56040bfd28fcff01cfdb8a1f4d0a076d5af22da6 Mon Sep 17 00:00:00 2001 From: MrDulfin Date: Sun, 19 May 2024 18:48:29 -0400 Subject: [PATCH] Updated Controller and added Player Trait --- src/lib.rs | 7 +- src/music_controller/connections.rs | 158 +++++----- src/music_controller/controller.rs | 281 +++--------------- src/music_controller/queue.rs | 224 ++------------ .../gstreamer.rs} | 44 +-- src/music_player/kira.rs | 0 src/music_player/player.rs | 57 ++++ 7 files changed, 226 insertions(+), 545 deletions(-) rename src/{music_player.rs => music_player/gstreamer.rs} (94%) create mode 100644 src/music_player/kira.rs create mode 100644 src/music_player/player.rs diff --git a/src/lib.rs b/src/lib.rs index 387a488..9f6bb0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,13 +8,16 @@ pub mod music_storage { pub mod db_reader; } -pub mod music_controller{ +pub mod music_controller { pub mod controller; pub mod connections; pub mod queue; } -pub mod music_player; +pub mod music_player { + pub mod gstreamer; + pub mod player; +} #[allow(clippy::module_inception)] pub mod config { pub mod config; diff --git a/src/music_controller/connections.rs b/src/music_controller/connections.rs index bf5e688..b2ceb85 100644 --- a/src/music_controller/connections.rs +++ b/src/music_controller/connections.rs @@ -1,103 +1,103 @@ -use std::{ - sync::{Arc, RwLock}, - error::Error, -}; +// use std::{ +// sync::{Arc, RwLock}, +// error::Error, +// }; -use discord_rpc_client::Client; -use listenbrainz::ListenBrainz; -use uuid::Uuid; +// use discord_rpc_client::Client; +// use listenbrainz::ListenBrainz; +// use uuid::Uuid; -use crate::{ - config::config::Config, music_controller::controller::{Controller, QueueCmd, QueueResponse}, music_storage::library::{MusicLibrary, Song, Tag} -}; +// use crate::{ +// config::config::Config, music_controller::controller::{Controller, QueueCmd, QueueResponse}, music_storage::library::{MusicLibrary, Song, Tag} +// }; -use super::controller::DatabaseResponse; +// use super::controller::DatabaseResponse; -impl Controller { - pub fn listenbrainz_authenticate(&mut self) -> Result> { - let config = &self.config.read().unwrap(); - let mut client = ListenBrainz::new(); +// impl Controller { +// pub fn listenbrainz_authenticate(&mut self) -> Result> { +// let config = &self.config.read().unwrap(); +// let mut client = ListenBrainz::new(); - let lbz_token = match &config.connections.listenbrainz_token { - Some(token) => token, - None => todo!("No ListenBrainz token in config") - }; +// let lbz_token = match &config.connections.listenbrainz_token { +// Some(token) => token, +// None => todo!("No ListenBrainz token in config") +// }; - if !client.is_authenticated() { - client.authenticate(lbz_token)?; - } +// if !client.is_authenticated() { +// client.authenticate(lbz_token)?; +// } - Ok(client) - } - pub fn lbz_scrobble(&self, client: ListenBrainz, uuid: Uuid) -> Result<(), Box> { - let config = &self.config.read().unwrap(); +// Ok(client) +// } +// pub fn lbz_scrobble(&self, client: ListenBrainz, uuid: Uuid) -> Result<(), Box> { +// let config = &self.config.read().unwrap(); - &self.db_mail.send(super::controller::DatabaseCmd::QueryUuid(uuid)); - let res = &self.db_mail.recv()?; - let song = match res { - DatabaseResponse::Song(song) => song, - _ => todo!() - }; - let unknown = &"unknown".to_string(); - let artist = song.get_tag(&Tag::Artist).unwrap_or(unknown); - let track = song.get_tag(&Tag::Title).unwrap_or(unknown); - let release = song.get_tag(&Tag::Album).map(|rel| rel.as_str()); +// &self.db_mail.send(super::controller::DatabaseCmd::QueryUuid(uuid)); +// let res = &self.db_mail.recv()?; +// let song = match res { +// DatabaseResponse::Song(song) => song, +// _ => todo!() +// }; +// let unknown = &"unknown".to_string(); +// let artist = song.get_tag(&Tag::Artist).unwrap_or(unknown); +// let track = song.get_tag(&Tag::Title).unwrap_or(unknown); +// let release = song.get_tag(&Tag::Album).map(|rel| rel.as_str()); - client.listen(artist, track, release)?; - Ok(()) - } +// client.listen(artist, track, release)?; +// Ok(()) +// } - pub fn lbz_now_playing(&self, client: ListenBrainz, uuid: Uuid) -> Result<(), Box> { - let config = &self.config.read().unwrap(); +// pub fn lbz_now_playing(&self, client: ListenBrainz, uuid: Uuid) -> Result<(), Box> { +// let config = &self.config.read().unwrap(); - &self.db_mail.send(super::controller::DatabaseCmd::QueryUuid(uuid)); - let res = &self.db_mail.recv()?; - let song = match res { - DatabaseResponse::Song(song) => song, - _ => todo!() - }; - let unknown = &"unknown".to_string(); - let artist = song.get_tag(&Tag::Artist).unwrap_or(unknown); - let track = song.get_tag(&Tag::Title).unwrap_or(unknown); - let release = song.get_tag(&Tag::Album).map(|rel| rel.as_str()); +// &self.db_mail.send(super::controller::DatabaseCmd::QueryUuid(uuid)); +// let res = &self.db_mail.recv()?; +// let song = match res { +// DatabaseResponse::Song(song) => song, +// _ => todo!() +// }; +// let unknown = &"unknown".to_string(); +// let artist = song.get_tag(&Tag::Artist).unwrap_or(unknown); +// let track = song.get_tag(&Tag::Title).unwrap_or(unknown); +// let release = song.get_tag(&Tag::Album).map(|rel| rel.as_str()); - client.listen(artist, track, release)?; - Ok(()) - } +// client.listen(artist, track, release)?; +// Ok(()) +// } - pub fn discord_song_change(client: &mut Client,song: Song) { - client.set_activity(|a| { - a.state(format!("Listening to {}", song.get_tag(&Tag::Title).unwrap())) - .into() - }); - } -} +// pub fn discord_song_change(client: &mut Client,song: Song) { +// client.set_activity(|a| { +// a.state(format!("Listening to {}", song.get_tag(&Tag::Title).unwrap())) +// .into() +// }); +// } +// } -#[cfg(test)] -mod test_super { - use std::{thread::sleep, time::Duration}; +// #[cfg(test)] +// mod test_super { +// use std::{thread::sleep, time::Duration}; - use super::*; - use crate::config::config::tests::read_config_lib; +// use super::*; +// use crate::config::config::tests::read_config_lib; - #[test] - fn listenbrainz() { - let mut c = Controller::start(".\\test-config\\config_test.json").unwrap(); +// #[test] +// fn listenbrainz() { +// let mut c = Controller::start(".\\test-config\\config_test.json").unwrap(); - let client = c.listenbrainz_authenticate().unwrap(); +// let client = c.listenbrainz_authenticate().unwrap(); - c.q_new().unwrap(); - c.queue_mail[0].send(QueueCmd::SetVolume(0.04)).unwrap(); +// c.q_new().unwrap(); +// c.queue_mail[0].send(QueueCmd::SetVolume(0.04)).unwrap(); - let songs = c.lib_get_songs(); +// let songs = c.lib_get_songs(); - c.q_enqueue(0, songs[1].location.to_owned()).unwrap(); - c.q_play(0).unwrap(); +// c.q_enqueue(0, songs[1].location.to_owned()).unwrap(); +// c.q_play(0).unwrap(); - sleep(Duration::from_secs(100)); - c.lbz_scrobble(client, songs[1].uuid).unwrap(); - } -} +// sleep(Duration::from_secs(100)); +// c.lbz_scrobble(client, songs[1].uuid).unwrap(); +// } +// } diff --git a/src/music_controller/controller.rs b/src/music_controller/controller.rs index d13a62f..8efb630 100644 --- a/src/music_controller/controller.rs +++ b/src/music_controller/controller.rs @@ -13,7 +13,8 @@ use std::error::Error; use crossbeam_channel::unbounded; use uuid::Uuid; -use crate::music_controller::queue::{QueueItem, QueueItemType}; +use crate::music_controller::queue::QueueItem; +use crate::music_player::gstreamer::GStreamer; use crate::music_storage::library::{Tag, URI}; use crate::{ music_storage::library::{MusicLibrary, Song}, @@ -22,63 +23,10 @@ use crate::{ }; pub struct Controller { - // queues: Vec, + pub queue: Queue, pub config: Arc>, - // library: MusicLibrary, - pub(super) controller_mail: MailMan, - pub(super) db_mail: MailMan, - pub(super) queue_mail: Vec>, -} -#[derive(Debug)] -pub(super) enum ControllerCmd { - Default, - Test -} - -#[derive(Debug)] -pub(super) enum ControllerResponse { - Empty, - QueueMailMan(MailMan), - -} - -#[derive(Debug)] -pub(super) enum DatabaseCmd { - Default, - Test, - SaveLibrary, - GetSongs, - QueryUuid(Uuid), - QueryUuids(Vec), - ReadFolder(String), -} - -#[derive(Debug)] -pub(super) enum DatabaseResponse { - Empty, - Song(Song), - Songs(Vec), - Library(MusicLibrary), -} - -#[derive(Debug)] -pub(super) enum QueueCmd { - Default, - Test, - Play, - Pause, - // SetSongs(Vec>), - // SetLocation(URI), - Enqueue(URI), - SetVolume(f64), -} - -#[derive(Debug)] -pub(super) enum QueueResponse { - Default, - Test, - Index(i32), - Uuid(Uuid), + pub library: MusicLibrary, + player_mail: MailMan } #[derive(Debug)] @@ -115,227 +63,72 @@ impl MailMan { } } +enum PlayerCmd { + Test(URI) +} + +enum PlayerRes { + Test +} + #[allow(unused_variables)] impl Controller { pub fn start

(config_path: P) -> Result> where std::path::PathBuf: std::convert::From

{ let config_path = PathBuf::from(config_path); + let config = Config::read_file(config_path)?; let uuid = config.libraries.get_default()?.uuid; let config_ = Arc::new(RwLock::from(config)); - let mut lib = MusicLibrary::init(config_.clone(), uuid)?; + let library = MusicLibrary::init(config_.clone(), uuid)?; - let config = config_.clone(); - let (out_thread_controller, in_thread) = MailMan::double(); - let monitor_thread = spawn(move || { - use ControllerCmd::*; - loop { - let command = in_thread.recv().unwrap(); + let (player_mail, in_thread) = MailMan::::double(); - match command { - Default => (), - Test => { - in_thread.send(ControllerResponse::Empty).unwrap(); - }, - } - } - }); + spawn(move || { + let mut player = GStreamer::new().unwrap(); - let config = config_.clone(); - let (out_thread_db, in_thread) = MailMan::double(); - let db_monitor = spawn(move || { - use DatabaseCmd::*; - loop { - let command = in_thread.recv().unwrap(); - - match command { - Default => {}, - Test => { - in_thread.send(DatabaseResponse::Empty).unwrap(); - }, - GetSongs => { - let songs = lib.query_tracks(&String::from(""), &(vec![Tag::Title]), &(vec![Tag::Title])).unwrap().iter().cloned().cloned().collect(); - in_thread.send(DatabaseResponse::Songs(songs)).unwrap(); - }, - SaveLibrary => { - //TODO: make this send lib ref to the function to save instead - lib.save(config.read().unwrap().to_owned()).unwrap(); - }, - QueryUuid(uuid) => { - match lib.query_uuid(&uuid) { - Some(song) => in_thread.send(DatabaseResponse::Song(song.0.clone())).unwrap(), - None => in_thread.send(DatabaseResponse::Empty).unwrap(), - } - }, - QueryUuids(uuids) => { - let mut vec = Vec::new(); - for uuid in uuids { - match lib.query_uuid(&uuid) { - Some(song) => vec.push(song.0.clone()), - None => unimplemented!() - } - } - in_thread.send(DatabaseResponse::Songs(vec)).unwrap(); - }, - ReadFolder(folder) => { - lib.scan_folder(&folder).unwrap(); - in_thread.send(DatabaseResponse::Empty).unwrap(); + while true { + match in_thread.recv().unwrap() { + PlayerCmd::Test(uri) => { + &player.set_volume(0.04); + _ = &player.enqueue_next(&uri).unwrap(); + _ = &player.play(); + in_thread.send(PlayerRes::Test).unwrap(); } - } } + }); Ok( Controller { - // queues: Vec::new(), + queue: Queue::new(), config: config_.clone(), - controller_mail: out_thread_controller, - db_mail: out_thread_db, - queue_mail: Vec::new(), + library, + player_mail } ) } - pub fn lib_get_songs(&self) -> Vec { - self.db_mail.send(DatabaseCmd::GetSongs); - match self.db_mail.recv().unwrap() { - DatabaseResponse::Songs(songs) => songs, - _ => Vec::new() - } + pub fn q_add(&self, item: Uuid, source:super::queue::PlayerLocation , by_human: bool) { + self.queue.add_item(item, source, by_human) } - pub fn lib_scan_folder(&self, folder: String) -> Result<(), Box> { - let mail = &self.db_mail; - mail.send(DatabaseCmd::ReadFolder(folder))?; - dbg!(mail.recv()?); - Ok(()) - } - - pub fn lib_save(&self) -> Result<(), Box> { - self.db_mail.send(DatabaseCmd::SaveLibrary); - Ok(()) - } - - pub fn q_new(&mut self) -> Result> { - let (out_thread_queue, in_thread) = MailMan::::double(); - let queues_monitor = spawn(move || { - use QueueCmd::*; - let mut queue = Queue::new().unwrap(); - loop { - let command = in_thread.recv().unwrap(); - match command { - Default => {}, - Test => { in_thread.send(QueueResponse::Test).unwrap() }, - Play => { - match queue.player.play() { - Ok(_) => in_thread.send(QueueResponse::Default).unwrap(), - Err(_) => todo!() - }; - - }, - Pause => { - match queue.player.pause() { - Ok(_) => in_thread.send(QueueResponse::Default).unwrap(), - Err(_) => todo!() - } - }, - // SetSongs(songs) => { - // queue.set_tracks(songs); - // in_thread.send(QueueResponse::Default).unwrap(); - // }, - Enqueue(uri) => { - queue.player.enqueue_next(&uri).unwrap(); - - // in_thread.send(QueueResponse::Default).unwrap(); - }, - SetVolume(vol) => { - queue.player.set_volume(vol); - } - } - } - }); - self.queue_mail.push(out_thread_queue); - Ok(self.queue_mail.len() - 1) - } - - pub fn q_play(&self, index: usize) -> Result<(), Box> { - let mail = &self.queue_mail[index]; - mail.send(QueueCmd::Play)?; - dbg!(mail.recv()?); - Ok(()) - } - - pub fn q_pause(&self, index: usize) -> Result<(), Box> { - let mail = &self.queue_mail[index]; - mail.send(QueueCmd::Pause)?; - dbg!(mail.recv()?); - Ok(()) - } - - pub fn q_set_volume(&self, index: usize, volume: f64) -> Result<(), Box> { - let mail = &self.queue_mail[index]; - mail.send(QueueCmd::SetVolume(volume))?; - Ok(()) - } - - // fn q_set_songs(&self, index: usize, songs: Vec>) -> Result<(), Box> { - // let mail = &self.queue_mail[index]; - // mail.send(QueueCmd::SetSongs(songs))?; - // dbg!(mail.recv()?); - // Ok(()) - // } - - pub fn q_enqueue(&self, index: usize, uri: URI) -> Result<(), Box> { - let mail = &self.queue_mail[index]; - mail.send(QueueCmd::Enqueue(uri))?; - // dbg!(mail.recv()?); - Ok(()) - } - - } #[cfg(test)] -mod tests { +mod test_super { use std::{thread::sleep, time::Duration}; - use super::Controller; - - #[test] - fn play_test() { - let mut a = match Controller::start("test-config/config_test.json".to_string()) { - Ok(c) => c, - Err(e) => panic!("{e}") - }; - sleep(Duration::from_millis(500)); - - let i = a.q_new().unwrap(); - a.q_set_volume(i, 0.04); - // a.new_queue(); - let songs = a.lib_get_songs(); - a.q_enqueue(i, songs[2].location.clone()); - // a.enqueue(1, songs[2].location.clone()); - a.q_play(i).unwrap(); - // a.play(1).unwrap(); - - sleep(Duration::from_secs(10)); - a.q_pause(i); - sleep(Duration::from_secs(10)); - a.q_play(i); - sleep(Duration::from_secs(1000)); - } + use super::*; #[test] fn test_() { - let a = match Controller::start("test-config/config_test.json".to_string()) { - Ok(c) => c, - Err(e) => panic!("{e}") - }; - a.lib_scan_folder("F:/Music/Mp3".to_string()); - a.lib_save(); + let c = Controller::start("F:\\Dangoware\\Dango Music Player\\dmp-core\\test-config\\config_test.json").unwrap(); + + sleep(Duration::from_secs(60)); } -} +} \ No newline at end of file diff --git a/src/music_controller/queue.rs b/src/music_controller/queue.rs index 0559696..39736e6 100644 --- a/src/music_controller/queue.rs +++ b/src/music_controller/queue.rs @@ -1,8 +1,5 @@ use uuid::Uuid; -use crate::{ - music_player::{Player, PlayerError}, - music_storage::library::{Album, MusicLibrary, URI} -}; +use crate::music_storage::library::{MusicLibrary, Song, URI}; use std::{ error::Error, sync::{Arc, RwLock} @@ -27,54 +24,6 @@ pub enum QueueState { NoState, } -#[derive(Debug, Clone, PartialEq)] -#[non_exhaustive] -pub enum QueueItemType<'a> { - Song(Uuid), - ExternalSong(URI), - Album{ - album: Album<'a>, - shuffled: bool, - order: Option>, - // disc #, track # - current: (i32, i32) - }, - Playlist { - uuid: Uuid, - shuffled: bool, - order: Option>, - current: Uuid - }, - None, - Test -} - -impl QueueItemType<'_> { - fn get_uri(&self, lib: Arc>) -> Option { - use QueueItemType::*; - - let lib = lib.read().unwrap(); - match self { - Song(uuid) => { - if let Some((song, _)) = lib.query_uuid(uuid) { - Some(song.location.clone()) - }else { - Option::None - } - }, - Album{album, shuffled, current: (disc, index), ..} => { - if !shuffled { - Some(album.track(*disc as usize, *index as usize).unwrap().location.clone()) - }else { - todo!() //what to do for non shuffled album - } - }, - ExternalSong(uri) => { Some(uri.clone()) }, - _ => { Option::None } - } - } -} - // TODO: move this to a different location to be used elsewhere #[derive(Debug, Clone, PartialEq)] #[non_exhaustive] @@ -88,16 +37,16 @@ pub enum PlayerLocation { #[derive(Debug, Clone, PartialEq)] #[non_exhaustive] -pub struct QueueItem<'a> { - pub(super) item: QueueItemType<'a>, +pub struct QueueItem { + pub(super) item: Song, pub(super) state: QueueState, pub(super) source: PlayerLocation, pub(super) by_human: bool } -impl QueueItem<'_> { - fn new() -> Self { +impl From for QueueItem { + fn from(song: Song) -> Self { QueueItem { - item: QueueItemType::None, + item: song, state: QueueState::NoState, source: PlayerLocation::Library, by_human: false @@ -107,15 +56,14 @@ impl QueueItem<'_> { #[derive(Debug)] -pub struct Queue<'a> { - pub player: Player, - pub name: String, - pub items: Vec>, - pub played: Vec>, - pub loop_: bool +pub struct Queue { + pub items: Vec, + pub played: Vec, + pub loop_: bool, + pub shuffle: bool } -impl<'a> Queue<'a> { +impl Queue { fn has_addhere(&self) -> bool { for item in &self.items { if item.state == QueueState::AddHere { @@ -126,28 +74,26 @@ impl<'a> Queue<'a> { } fn dbg_items(&self) { - dbg!(self.items.iter().map(|item| item.item.clone() ).collect::>(), self.items.len()); + dbg!(self.items.iter().map(|item| item.item.clone() ).collect::>(), self.items.len()); } - pub fn new() -> Result { - Ok( - Queue { - player: Player::new()?, - name: String::new(), - items: Vec::new(), - played: Vec::new(), - loop_: false, - } - ) + pub fn new() -> Self { + //TODO: Make the queue take settings from config/state if applicable + Queue { + items: Vec::new(), + played: Vec::new(), + loop_: false, + shuffle: false, + } } - pub fn set_items(&mut self, tracks: Vec>) { + pub fn set_items(&mut self, tracks: Vec) { let mut tracks = tracks; self.items.clear(); self.items.append(&mut tracks); } - pub fn add_item(&mut self, item: QueueItemType<'a>, source: PlayerLocation, by_human: bool) { + pub fn add_item(&mut self, item: Song, source: PlayerLocation, by_human: bool) { let mut i: usize = 0; self.items = self.items.iter().enumerate().map(|(j, item_)| { @@ -168,7 +114,7 @@ impl<'a> Queue<'a> { }); } - pub fn add_item_next(&mut self, item: QueueItemType<'a>, source: PlayerLocation) { + pub fn add_item_next(&mut self, item: Song, source: PlayerLocation) { use QueueState::*; let empty = self.items.is_empty(); @@ -176,14 +122,14 @@ impl<'a> Queue<'a> { (if empty { 0 } else { 1 }), QueueItem { item, - state: if (self.items.get(1).is_none() || (!self.has_addhere() && self.items.get(1).is_some()) || empty) { AddHere } else { NoState }, + state: if (self.items.get(1).is_none() || !self.has_addhere() && self.items.get(1).is_some()) || empty { AddHere } else { NoState }, source, by_human: true } ) } - pub fn add_multi(&mut self, items: Vec, source: PlayerLocation, by_human: bool) { + pub fn add_multi(&mut self, items: Vec, source: PlayerLocation, by_human: bool) { } @@ -239,14 +185,10 @@ impl<'a> Queue<'a> { use QueueState::*; let empty = self.items.is_empty(); - let nothing_error = Err(QueueError::EmptyQueue); - let index = if !empty { index } else { return nothing_error; }; + + let index = if !empty { index } else { return Err(QueueError::EmptyQueue); }; if !empty && index < self.items.len() { - let position = self.player.position(); - if position.is_some_and(|dur| !dur.is_zero() ) { - self.played.push(self.items[0].clone()); - } let to_item = self.items[index].clone(); @@ -263,13 +205,13 @@ impl<'a> Queue<'a> { } // dbg!(&to_item.item, &self.items[ind].item); }else if empty { - return nothing_error; + return Err(QueueError::EmptyQueue); }else { break; } } }else { - return Err(QueueError::EmptyQueue.into()); + return Err(QueueError::EmptyQueue); } Ok(()) } @@ -287,9 +229,7 @@ impl<'a> Queue<'a> { } #[allow(clippy::should_implement_trait)] - pub fn next(&mut self, lib: Arc>) -> Result> { - - + pub fn next(&mut self) -> Result<&QueueItem, Box> { if self.items.is_empty() { if self.loop_ { @@ -300,119 +240,21 @@ impl<'a> Queue<'a> { } // TODO: add an algorithm to detect if the song should be skipped let item = self.items[0].clone(); - let uri: URI = match &self.items[1].item { - QueueItemType::Song(uuid) => { - // TODO: Refactor later for multiple URIs - match &lib.read().unwrap().query_uuid(uuid) { - Some(song) => song.0.location.clone(), - None => return Err("Uuid does not exist!".into()), - } - }, - QueueItemType::Album { album, current, ..} => { - let (disc, track) = (current.0 as usize, current.1 as usize); - match album.track(disc, track) { - Some(track) => track.location.clone(), - None => return Err(format!("Track in Album {} at disc {} track {} does not exist!", album.title(), disc, track).into()) - } - }, - QueueItemType::Playlist { current, .. } => { - // TODO: Refactor later for multiple URIs - match &lib.read().unwrap().query_uuid(current) { - Some(song) => song.0.location.clone(), - None => return Err("Uuid does not exist!".into()), - } - }, - _ => todo!() - }; - if !self.player.is_paused() { - self.player.enqueue_next(&uri)?; - self.player.play()? - } + if self.items[0].state == QueueState::AddHere || !self.has_addhere() { self.items[1].state = QueueState::AddHere; } self.played.push(item); self.items.remove(0); - Ok(todo!()) + Ok(&self.items[1]) } pub fn prev() {} - pub fn enqueue_item(&mut self, item: QueueItem, lib: Arc>) -> Result<(), Box> { - if let Some(uri) = item.item.get_uri(lib) { - self.player.enqueue_next(&uri)?; - }else { - return Err("this item does not exist!".into()); - } - Ok(()) - } pub fn check_played(&mut self) { while self.played.len() > 50 { self.played.remove(0); } } } - -pub struct OutQueue { - -} - -pub enum OutQueueItem { - -} - - -#[test] -fn item_add_test() { - let mut q = Queue::new().unwrap(); - - for _ in 0..5 { - // dbg!("tick!"); - q.add_item(QueueItemType::Song(Uuid::new_v4()), PlayerLocation::Library, true); - // dbg!(&q.items, &q.items.len()); - } - - for _ in 0..1 { - q.remove_item(0).inspect_err(|e| println!("{e:?}")); - } - for _ in 0..2 { - q.items.push(QueueItem { item: QueueItemType::Test, state: QueueState::NoState, source: PlayerLocation::Library, by_human: false }); - } - dbg!(5); - - q.add_item_next(QueueItemType::Test, PlayerLocation::Test); - dbg!(6); - - dbg!(&q.items, &q.items.len()); -} - -#[test] -fn test_() { - let mut q = Queue::new().unwrap(); - for _ in 0..400 { - q.items.push(QueueItem { item: QueueItemType::Song(Uuid::new_v4()), state: QueueState::NoState, source: PlayerLocation::File, by_human: false }); - } - for _ in 0..50000 { - q.add_item(QueueItemType::Song(Uuid::new_v4()), PlayerLocation::Library, true); - } - // q.add_item_next(QueueItemType::Test, PlayerLocation::File); - - // dbg!(&q.items, &q.items.len()); - -} - -#[test] -fn move_test() { - let mut q = Queue::new().unwrap(); - - for _ in 0..5 { - q.add_item(QueueItemType::Song(Uuid::new_v4()), PlayerLocation::Library, true); - } - // q.add_item(QueueItemType::Test, QueueSource::Library, true).unwrap(); - dbg!(&q.items, &q.items.len()); - - q.move_to(3).inspect_err(|e| {dbg!(e);}); - dbg!(&q.items, &q.items.len()); - // q.dbg_items(); -} diff --git a/src/music_player.rs b/src/music_player/gstreamer.rs similarity index 94% rename from src/music_player.rs rename to src/music_player/gstreamer.rs index e310403..91abfde 100644 --- a/src/music_player.rs +++ b/src/music_player/gstreamer.rs @@ -15,8 +15,10 @@ use gstreamer::prelude::*; use chrono::Duration; use thiserror::Error; +use super::player::{Player, PlayerError}; + #[derive(Debug)] -pub enum PlayerCmd { +pub enum GstCmd { Play, Pause, Eos, @@ -24,7 +26,7 @@ pub enum PlayerCmd { } #[derive(Debug, PartialEq, Eq)] -pub enum PlayerState { +pub enum GstState { Playing, Paused, Ready, @@ -33,7 +35,7 @@ pub enum PlayerState { VoidPending, } -impl From for PlayerState { +impl From for GstState { fn from(value: gst::State) -> Self { match value { gst::State::VoidPending => Self::VoidPending, @@ -45,7 +47,7 @@ impl From for PlayerState { } } -impl TryInto for PlayerState { +impl TryInto for GstState { fn try_into(self) -> Result> { match self { Self::VoidPending => Ok(gst::State::VoidPending), @@ -60,24 +62,6 @@ impl TryInto for PlayerState { type Error = Box; } -#[derive(Error, Debug)] -pub enum PlayerError { - #[error("player initialization failed")] - Init(#[from] glib::Error), - #[error("element factory failed to create playbin3")] - Factory(#[from] glib::BoolError), - #[error("could not change playback state")] - StateChange(#[from] gst::StateChangeError), - #[error("the file or source is not found")] - NotFound, - #[error("failed to build gstreamer item")] - Build, - #[error("poison error")] - Poison, - #[error("general player error")] - General, -} - #[derive(Debug, PartialEq, Eq)] enum PlaybackStats { Idle, @@ -91,10 +75,10 @@ enum PlaybackStats { /// An instance of a music player with a GStreamer backend #[derive(Debug)] -pub struct Player { +pub struct GStreamer { source: Option, //pub message_tx: Sender, - pub message_rx: crossbeam::channel::Receiver, + pub message_rx: crossbeam::channel::Receiver, playback_tx: crossbeam::channel::Sender, playbin: Arc>, @@ -105,7 +89,7 @@ pub struct Player { position: Arc>>, } -impl Player { +impl GStreamer { pub fn new() -> Result { // Initialize GStreamer, maybe figure out how to nicely fail here gst::init()?; @@ -162,14 +146,14 @@ impl Player { // Check if the current playback position is close to the end let finish_point = end - Duration::milliseconds(250); if pos_temp.unwrap() >= end { - let _ = playback_tx.try_send(PlayerCmd::Eos); + let _ = playback_tx.try_send(GstCmd::Eos); playbin_arc .write() .unwrap() .set_state(gst::State::Ready) .expect("Unable to set the pipeline state"); } else if pos_temp.unwrap() >= finish_point { - let _ = playback_tx.try_send(PlayerCmd::AboutToFinish); + let _ = playback_tx.try_send(GstCmd::AboutToFinish); } // This has to be done AFTER the current time in the file @@ -468,7 +452,7 @@ impl Player { } /// Get the current state of the playback - pub fn state(&mut self) -> PlayerState { + pub fn state(&mut self) -> GstState { self.playbin().unwrap().current_state().into() /* match *self.buffer.read().unwrap() { @@ -498,7 +482,9 @@ impl Player { } } -impl Drop for Player { +// impl Player for GStreamer {} + +impl Drop for GStreamer { /// Cleans up the `GStreamer` pipeline and the monitoring /// thread when [Player] is dropped. fn drop(&mut self) { diff --git a/src/music_player/kira.rs b/src/music_player/kira.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/music_player/player.rs b/src/music_player/player.rs new file mode 100644 index 0000000..a30b2a6 --- /dev/null +++ b/src/music_player/player.rs @@ -0,0 +1,57 @@ +use chrono::Duration; +use thiserror::Error; +use gstreamer as gst; + +use crate::music_storage::library::URI; + + +#[derive(Error, Debug)] +pub enum PlayerError { + #[error("player initialization failed")] + Init(#[from] glib::Error), + #[error("element factory failed to create playbin3")] + Factory(#[from] glib::BoolError), + #[error("could not change playback state")] + StateChange(#[from] gst::StateChangeError), + #[error("the file or source is not found")] + NotFound, + #[error("failed to build gstreamer item")] + Build, + #[error("poison error")] + Poison, + #[error("general player error")] + General, +} + +pub trait Player { + fn source(&self) -> &Option; + + fn enqueue_next(&mut self, next_track: &URI) -> Result<(), PlayerError>; + + fn set_volume(&mut self, volume: f64); + + fn volume(&mut self) -> f64; + + fn ready(&mut self) -> Result<(), PlayerError>; + + fn play(&mut self) -> Result<(), PlayerError>; + + fn resume(&mut self) -> Result<(), PlayerError>; + + fn pause(&mut self) -> Result<(), PlayerError>; + + fn stop(&mut self) -> Result<(), PlayerError>; + + fn is_paused(&mut self) -> bool; + + fn position(&mut self) -> Option; + + fn duration(&mut self) -> Option; + + fn raw_duration(&self) -> Option; + + fn seek_by(&mut self, seek_amount: Duration) -> Result<(), PlayerError>; + + fn seek_to(&mut self, target_pos: Duration) -> Result<(), PlayerError>; + +} \ No newline at end of file