//! The [Controller] is the input and output for the entire //! player. It manages queues, playback, library access, and //! other functions #![allow(while_true)] use kushi::{Queue, QueueItemType}; use kushi::{QueueError, QueueItem}; use std::error::Error; use std::marker::PhantomData; use std::sync::{Arc, RwLock}; use thiserror::Error; use uuid::Uuid; use crate::config::ConfigError; use crate::music_player::player::{Player, PlayerError}; use crate::music_storage::library::Song; use crate::{config::Config, music_storage::library::MusicLibrary}; use super::queue::{QueueAlbum, QueueSong}; pub struct Controller<'a, P>(&'a PhantomData

); #[derive(Error, Debug)] pub enum ControllerError { #[error("{0:?}")] QueueError(#[from] QueueError), #[error("{0:?}")] PlayerError(#[from] PlayerError), #[error("{0:?}")] ConfigError(#[from] ConfigError), } // TODO: move this to a different location to be used elsewhere #[derive(Debug, Clone, Copy, PartialEq)] #[non_exhaustive] pub enum PlayerLocation { Test, Library, Playlist(Uuid), File, Custom, } #[derive(Debug, Clone)] pub struct MailMan { tx: async_channel::Sender, rx: async_channel::Receiver, } impl MailMan { pub fn double() -> (MailMan, MailMan) { let (tx, rx) = async_channel::unbounded::(); let (tx1, rx1) = async_channel::unbounded::(); (MailMan { tx, rx: rx1 }, MailMan { tx: tx1, rx }) } pub async fn send(&self, mail: Tx) -> Result<(), async_channel::SendError> { self.tx.send(mail).await } pub async fn recv(&self) -> Result { self.rx.recv().await } } #[derive(Debug, PartialEq, PartialOrd, Clone)] pub enum PlayerCommand { NextSong, PrevSong, Pause, Play, Enqueue(usize), SetVolume(f64), } #[derive(Debug, PartialEq, PartialOrd, Clone)] pub enum PlayerResponse { Empty, } pub enum LibraryCommand { Song(Uuid), } pub enum LibraryResponse { Songs(Song), } enum InnerLibraryCommand { Song(Uuid), } enum InnerLibraryResponse<'a> { Song(&'a Song), } pub enum QueueCommand { Append(QueueItem), Next, Prev, GetIndex(usize), NowPlaying, } pub enum QueueResponse { Ok, Item(QueueItem), } #[allow(unused_variables)] impl<'c, P: Player + Send + Sync> Controller<'c, P> { pub async fn start( player_mail: ( MailMan, MailMan, ), lib_mail: MailMan, mut library: MusicLibrary, config: Arc>, ) -> Result<(), Box> where P: Player, { //TODO: make a separate event loop for sccessing library that clones borrowed values from inner library loop? let mut queue: Queue = Queue { items: Vec::new(), played: Vec::new(), loop_: false, shuffle: None, }; for song in &library.library { queue.add_item( QueueSong { song: song.clone(), location: PlayerLocation::Test, }, true, ); } let inner_lib_mail = MailMan::double(); let queue = queue; std::thread::scope(|scope| { let queue_mail = MailMan::double(); let a = scope.spawn(|| { futures::executor::block_on(async { moro::async_scope!(|scope| { println!("async scope created"); let player = Arc::new(RwLock::new(P::new().unwrap())); let _player = player.clone(); scope .spawn(async move { Controller::

::player_command_loop( _player, player_mail.1, queue_mail.0, ) .await .unwrap(); }) .await; scope .spawn(async move { Controller::

::player_event_loop(player, player_mail.0) .await .unwrap(); }) .await; scope .spawn(async { Controller::

::inner_library_loop(inner_lib_mail.1, &mut library) .await .unwrap() }) .await; scope .spawn(async { Controller::

::outer_library_loop(lib_mail, inner_lib_mail.0) .await .unwrap(); }) .await }) .await; }) }); let b = scope.spawn(|| { futures::executor::block_on(async { Controller::

::queue_loop(queue, queue_mail.1).await; }) }); a.join().unwrap(); b.join().unwrap(); }); Ok(()) } async fn player_command_loop( player: Arc>, player_mail: MailMan, queue_mail: MailMan, ) -> Result<(), ()> { { player.write().unwrap().set_volume(0.05); } while true { let _mail = player_mail.recv().await; if let Ok(mail) = _mail { match mail { PlayerCommand::Play => { player.write().unwrap().play().unwrap(); player_mail.send(PlayerResponse::Empty).await.unwrap(); } PlayerCommand::Pause => { player.write().unwrap().pause().unwrap(); player_mail.send(PlayerResponse::Empty).await.unwrap(); } PlayerCommand::SetVolume(volume) => { player.write().unwrap().set_volume(volume); println!("volume set to {volume}"); player_mail.send(PlayerResponse::Empty).await.unwrap(); } PlayerCommand::NextSong => { queue_mail.send(QueueCommand::Next).await.unwrap(); if let QueueResponse::Item(item) = queue_mail.recv().await.unwrap() { let uri = match &item.item { QueueItemType::Single(song) => song.song.primary_uri().unwrap().0, _ => unimplemented!(), }; player.write().unwrap().enqueue_next(uri).unwrap(); player_mail.send(PlayerResponse::Empty).await.unwrap(); } } PlayerCommand::PrevSong => { queue_mail.send(QueueCommand::Prev).await.unwrap(); if let QueueResponse::Item(item) = queue_mail.recv().await.unwrap() { let uri = match &item.item { QueueItemType::Single(song) => song.song.primary_uri().unwrap().0, _ => unimplemented!(), }; player.write().unwrap().enqueue_next(uri).unwrap(); player_mail.send(PlayerResponse::Empty).await.unwrap(); } } PlayerCommand::Enqueue(index) => { queue_mail .send(QueueCommand::GetIndex(index)) .await .unwrap(); if let QueueResponse::Item(item) = queue_mail.recv().await.unwrap() { match item.item { QueueItemType::Single(song) => { player .write() .unwrap() .enqueue_next(song.song.primary_uri().unwrap().0) .unwrap(); } _ => unimplemented!(), } player_mail.send(PlayerResponse::Empty).await.unwrap(); } } } } else { return Err(()); } } Ok(()) } async fn outer_library_loop( lib_mail: MailMan, inner_lib_mail: MailMan>, ) -> Result<(), ()> { while true { match lib_mail.recv().await.unwrap() { LibraryCommand::Song(uuid) => { inner_lib_mail .send(InnerLibraryCommand::Song(uuid)) .await .unwrap(); let x = inner_lib_mail.recv().await.unwrap(); } } } Ok(()) } async fn inner_library_loop( lib_mail: MailMan, InnerLibraryCommand>, library: &'c mut MusicLibrary, ) -> Result<(), ()> { while true { match lib_mail.recv().await.unwrap() { InnerLibraryCommand::Song(uuid) => { let song: &'c Song = library.query_uuid(&uuid).unwrap().0; lib_mail .send(InnerLibraryResponse::Song(song)) .await .unwrap(); } } } Ok(()) } async fn player_event_loop( player: Arc>, player_mail: MailMan, ) -> Result<(), ()> { // just pretend this does something Ok(()) } async fn queue_loop( mut queue: Queue, queue_mail: MailMan, ) { while true { match queue_mail.recv().await.unwrap() { QueueCommand::Append(item) => match item.item { QueueItemType::Single(song) => queue.add_item(song, true), _ => unimplemented!(), }, QueueCommand::Next => { let next = queue.next().unwrap(); queue_mail .send(QueueResponse::Item(next.clone())) .await .unwrap(); } QueueCommand::Prev => { let next = queue.prev().unwrap(); queue_mail .send(QueueResponse::Item(next.clone())) .await .unwrap(); } QueueCommand::GetIndex(index) => { let item = queue.items[index].clone(); queue_mail.send(QueueResponse::Item(item)).await.unwrap(); } QueueCommand::NowPlaying => { let item = queue.current().unwrap(); queue_mail .send(QueueResponse::Item(item.clone())) .await .unwrap(); } } } } } #[cfg(test)] mod test_super { use std::{ path::PathBuf, sync::{Arc, RwLock}, thread::spawn, }; use crate::{ config::{tests::new_config_lib, Config}, music_controller::controller::{ LibraryCommand, LibraryResponse, MailMan, PlayerCommand, PlayerResponse, }, music_player::gstreamer::GStreamer, music_storage::library::MusicLibrary, }; use super::Controller; #[tokio::test] async fn construct_controller() { // use if you don't have a config setup and add music to the music folder new_config_lib(); let lib_mail: (MailMan, MailMan<_, _>) = MailMan::double(); let player_mail: (MailMan, MailMan<_, _>) = MailMan::double(); let _player_mail = player_mail.0.clone(); let b = spawn(move || { futures::executor::block_on(async { _player_mail .send(PlayerCommand::SetVolume(0.01)) .await .unwrap(); loop { let buf: String = text_io::read!(); dbg!(&buf); _player_mail .send(match buf.to_lowercase().as_str() { "next" => PlayerCommand::NextSong, "prev" => PlayerCommand::PrevSong, "pause" => PlayerCommand::Pause, "play" => PlayerCommand::Play, x if x.parse::().is_ok() => { PlayerCommand::Enqueue(x.parse::().unwrap()) } _ => continue, }) .await .unwrap(); println!("sent it"); println!("{:?}", _player_mail.recv().await.unwrap()) } }) }); let a = spawn(move || { futures::executor::block_on(async { let config = Config::read_file(PathBuf::from(std::env!("CONFIG-PATH"))).unwrap(); let library = { MusicLibrary::init( config.libraries.get_default().unwrap().path.clone(), config.libraries.get_default().unwrap().uuid, ) .unwrap() }; Controller::::start( player_mail, lib_mail.1, library, Arc::new(RwLock::new(config)), ) .await .unwrap(); }); }); b.join().unwrap(); a.join().unwrap(); } }