//! 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();
}
}