diff --git a/dmp-core/src/music_controller/controller.rs b/dmp-core/src/music_controller/controller.rs index 1b40033..56d8088 100644 --- a/dmp-core/src/music_controller/controller.rs +++ b/dmp-core/src/music_controller/controller.rs @@ -32,7 +32,7 @@ pub enum ControllerError { } // TODO: move this to a different location to be used elsewhere -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)] #[non_exhaustive] pub enum PlayerLocation { Test, @@ -73,6 +73,7 @@ pub enum PlayerCommand { Play, Enqueue(usize), SetVolume(f64), + PlayNow(Uuid, PlayerLocation), } #[derive(Debug, PartialEq, Clone)] @@ -81,39 +82,45 @@ pub enum PlayerResponse { NowPlaying(Song) } +#[derive(Debug, PartialEq, PartialOrd, Clone)] pub enum LibraryCommand { Song(Uuid), AllSongs, GetLibrary, } +#[derive(Debug)] pub enum LibraryResponse { Song(Song), AllSongs(Vec), Library(MusicLibrary), } +#[derive(Debug, PartialEq, PartialOrd, Clone)] enum InnerLibraryCommand { Song(Uuid), AllSongs, } +#[derive(Debug, PartialEq, Clone)] enum InnerLibraryResponse<'a> { - Song(&'a Song), + Song(&'a Song, usize), AllSongs(&'a Vec), } -#[derive(Debug, Clone)] + +#[derive(Debug, PartialEq, Clone)] pub enum QueueCommand { - Append(QueueItem), + Append(QueueItem, bool), Next, Prev, GetIndex(usize), NowPlaying, - Get + Get, + Clear } -#[derive(Debug, Clone)] +#[derive(Debug, PartialEq, Clone)] pub enum QueueResponse { Ok, Item(QueueItem), @@ -208,12 +215,14 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { let player = Arc::new(RwLock::new(P::new().unwrap())); let _player = player.clone(); + let _inner_lib_mail = inner_lib_mail.0.clone(); scope .spawn(async move { Controller::

::player_command_loop( _player, player_mail.1, queue_mail.0, + _inner_lib_mail ) .await .unwrap(); @@ -256,6 +265,7 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { player: Arc>, player_mail: MailMan, queue_mail: MailMan, + inner_lib_mail: MailMan> ) -> Result<(), ()> { let mut first = true; while true { @@ -294,6 +304,39 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { }; player.write().unwrap().enqueue_next(uri).unwrap(); let QueueItemType::Single(np_song) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")}; + + // Append next song in library + inner_lib_mail.send(InnerLibraryCommand::AllSongs).await.unwrap(); + + let InnerLibraryResponse::AllSongs(songs) = inner_lib_mail.recv().await.unwrap() else { + unreachable!() + }; + inner_lib_mail.send(InnerLibraryCommand::Song(np_song.song.uuid.clone())).await.unwrap(); + let InnerLibraryResponse::Song(_, i) = inner_lib_mail.recv().await.unwrap() else { + unreachable!() + }; + if let Some(song) = songs.get(i + 49) { + queue_mail.send( + QueueCommand::Append( + QueueItem::from_item_type( + QueueItemType::Single( + QueueSong { + song: song.clone(), + location: np_song.location + } + ) + ), + false + ) + ).await + .unwrap(); + let QueueResponse::Ok = queue_mail.recv().await.unwrap() else { + unreachable!() + }; + } else { + println!("Library Empty"); + } + player_mail.send(PlayerResponse::NowPlaying(np_song.song.clone())).await.unwrap(); } } @@ -306,7 +349,7 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { _ => unimplemented!(), }; let QueueItemType::Single(np_song) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")}; - player_mail.send(PlayerResponse::NowPlaying(np_song.song.clone())).await.unwrap();; + player_mail.send(PlayerResponse::NowPlaying(np_song.song.clone())).await.unwrap(); } } PlayerCommand::Enqueue(index) => { @@ -328,6 +371,46 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { player_mail.send(PlayerResponse::Empty).await.unwrap(); } } + PlayerCommand::PlayNow(uuid, location) => { + // TODO: This assumes the uuid doesn't point to an album. we've been over this. + inner_lib_mail.send(InnerLibraryCommand::Song(uuid)).await.unwrap(); + let InnerLibraryResponse::Song(song, index) = inner_lib_mail.recv().await.unwrap() else { + unreachable!() + }; + queue_mail.send(QueueCommand::Clear).await.unwrap(); + let QueueResponse::Ok = queue_mail.recv().await.unwrap() else { + unreachable!() + }; + queue_mail.send(QueueCommand::Append(QueueItem::from_item_type(QueueItemType::Single(QueueSong { song: song.clone(), location: location })), true)).await.unwrap(); + let QueueResponse::Ok = queue_mail.recv().await.unwrap() else { + unreachable!() + }; + + player.write().unwrap().enqueue_next(song.primary_uri().unwrap().0).unwrap(); + + // how grab all the songs in a certain subset of the library, I reckon? + // ... + // let's just pretend I figured that out already + + inner_lib_mail.send(InnerLibraryCommand::AllSongs).await.unwrap(); + let InnerLibraryResponse::AllSongs(songs) = inner_lib_mail.recv().await.unwrap() else { + unreachable!() + }; + + for i in index+1..(index+50) { + if let Some(song) = songs.get(i) { + queue_mail.send(QueueCommand::Append(QueueItem::from_item_type(QueueItemType::Single(QueueSong { song: song.clone(), location })), false)).await.unwrap(); + let QueueResponse::Ok = queue_mail.recv().await.unwrap() else { + unreachable!() + }; + } else { + println!("End of Library"); + break; + } + } + // ^ This be my solution for now ^ + player_mail.send(PlayerResponse::NowPlaying(song.clone())).await.unwrap(); + } } } else { return Err(()); @@ -347,7 +430,7 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { .send(InnerLibraryCommand::Song(uuid)) .await .unwrap(); - let InnerLibraryResponse::Song(song) = inner_lib_mail.recv().await.unwrap() else { + let InnerLibraryResponse::Song(song, i) = inner_lib_mail.recv().await.unwrap() else { unimplemented!(); }; lib_mail.send(LibraryResponse::Song(song.clone())).await.unwrap(); @@ -377,9 +460,9 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { while true { match lib_mail.recv().await.unwrap() { InnerLibraryCommand::Song(uuid) => { - let song: &'c Song = library.query_uuid(&uuid).unwrap().0; + let (song, i): (&'c Song, usize) = library.query_uuid(&uuid).unwrap(); lib_mail - .send(InnerLibraryResponse::Song(song)) + .send(InnerLibraryResponse::Song(song, i)) .await .unwrap(); } @@ -408,9 +491,9 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { ) { while true { match queue_mail.recv().await.unwrap() { - QueueCommand::Append(item) => { + QueueCommand::Append(item, by_human) => { match item.item { - QueueItemType::Single(song) => queue.add_item(song, true), + QueueItemType::Single(song) => queue.add_item(song, by_human), _ => unimplemented!(), } queue_mail @@ -446,6 +529,10 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { QueueCommand::Get => { queue_mail.send(QueueResponse::GetAll(queue.items.clone())).await.unwrap(); } + QueueCommand::Clear => { + queue.clear(); + queue_mail.send(QueueResponse::Ok).await.unwrap(); + } } } } diff --git a/kushi-queue/src/lib.rs b/kushi-queue/src/lib.rs index 77e5f89..58de428 100644 --- a/kushi-queue/src/lib.rs +++ b/kushi-queue/src/lib.rs @@ -133,7 +133,6 @@ impl< self.items.get_mut(i).expect("There should be an item at index {i}").state = QueueState::NoState; } - if by_human { self.items.insert( i + if empty { 0 } else { 1 }, diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore index b21bd68..31134e7 100644 --- a/src-tauri/.gitignore +++ b/src-tauri/.gitignore @@ -4,4 +4,4 @@ # Generated by Tauri # will have schema files for capabilities auto-completion -/gen/schemas +/gen/schemas \ No newline at end of file diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index a26b677..fc48be3 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1,8 +1,10 @@ -use dmp_core::music_controller::{controller::{ControllerHandle, LibraryResponse, PlayerLocation, QueueResponse}, queue::QueueSong}; +use dmp_core::music_controller::{controller::{ControllerHandle, LibraryResponse, PlayerCommand, PlayerLocation, PlayerResponse, QueueResponse}, queue::QueueSong}; use kushi::QueueItem; use tauri::{AppHandle, Emitter, State, Wry}; use uuid::Uuid; +use crate::wrappers::_Song; + #[tauri::command] @@ -11,10 +13,22 @@ pub async fn add_song_to_queue(app: AppHandle, ctrl_handle: State<'_, Contr let LibraryResponse::Song(song) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!() }; - ctrl_handle.queue_mail.send(dmp_core::music_controller::controller::QueueCommand::Append(QueueItem::from_item_type(kushi::QueueItemType::Single(QueueSong { song, location })))).await.unwrap(); + ctrl_handle.queue_mail.send(dmp_core::music_controller::controller::QueueCommand::Append(QueueItem::from_item_type(kushi::QueueItemType::Single(QueueSong { song, location })), true)).await.unwrap(); let QueueResponse::Ok = ctrl_handle.queue_mail.recv().await.unwrap() else { panic!() }; app.emit("queue_updated", ()).unwrap(); Ok(()) +} + +#[tauri::command] +pub async fn play_now(app: AppHandle, ctrl_handle: State<'_, ControllerHandle>, uuid: Uuid, location: PlayerLocation) -> Result<(), String> { + ctrl_handle.player_mail.send(PlayerCommand::PlayNow(uuid, location)).await.unwrap(); + let PlayerResponse::NowPlaying(song) = ctrl_handle.player_mail.recv().await.unwrap() else { + unreachable!() + }; + app.emit("queue_updated", ()).unwrap(); + app.emit("now_playing_change", _Song::from(&song)).unwrap(); + app.emit("playing", ()).unwrap(); + Ok(()) } \ No newline at end of file diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 937b6af..b86ee19 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,9 +1,9 @@ use std::{fs, path::PathBuf, str::FromStr, thread::spawn}; -use commands::add_song_to_queue; +use commands::{add_song_to_queue, play_now}; use crossbeam::channel::{unbounded, Receiver, Sender}; use dmp_core::{config::{Config, ConfigLibrary}, music_controller::controller::{Controller, ControllerHandle, LibraryResponse}, music_player::gstreamer::GStreamer, music_storage::library::{AlbumArt, MusicLibrary}}; -use tauri::{http::Response, Manager, State, Url, WebviewWindowBuilder, Wry}; +use tauri::{http::Response, Emitter, Manager, State, Url, WebviewWindowBuilder, Wry}; use uuid::Uuid; use crate::wrappers::{get_library, play, pause, prev, set_volume, get_song, next, get_queue}; @@ -73,6 +73,7 @@ pub fn run() { lib_already_created, get_queue, add_song_to_queue, + play_now, ]).manage(ConfigRx(rx)) .manage(LibRx(lib_rx)) .manage(HandleTx(handle_tx)) @@ -96,8 +97,6 @@ pub fn run() { let LibraryResponse::Song(song) = controller.lib_mail.recv().await.unwrap() else { unreachable!() }; song.album_art(0).unwrap_or_else(|_| None).unwrap_or(DEFAULT_IMAGE.to_vec()) })}; - - res.respond( Response::builder() .header("Origin", "*") @@ -124,7 +123,6 @@ struct ConfigRx(Sender); struct LibRx(Sender>); struct HandleTx(Receiver); -struct DefaultImage<'a>(&'a [u8]); #[tauri::command] @@ -196,6 +194,7 @@ async fn create_library( lib_rx.inner().0.send(Some(path)).unwrap(); app.manage(handle_tx.inner().0.recv().unwrap()); + app.emit("library_loaded", ()).unwrap(); window.close().unwrap(); Ok(()) @@ -206,5 +205,6 @@ async fn create_library( println!("lib already created"); lib_rx.inner().0.send(None).unwrap(); app.manage(handle_tx.inner().0.recv().unwrap()); + app.emit("library_loaded", ()).unwrap(); Ok(()) } diff --git a/src-tauri/src/wrappers.rs b/src-tauri/src/wrappers.rs index e591705..6d016b1 100644 --- a/src-tauri/src/wrappers.rs +++ b/src-tauri/src/wrappers.rs @@ -21,15 +21,17 @@ pub async fn play(app: AppHandle, ctrl_handle: State<'_, ControllerHandle>) } else { unreachable!() }; + app.emit("playing", ()).unwrap(); Ok(()) } #[tauri::command] -pub async fn pause(ctrl_handle: State<'_, ControllerHandle>) -> Result<(), String> { +pub async fn pause(app: AppHandle, ctrl_handle: State<'_, ControllerHandle>) -> Result<(), String> { ctrl_handle.player_mail.send(dmp_core::music_controller::controller::PlayerCommand::Pause).await.unwrap(); let PlayerResponse::Empty = ctrl_handle.player_mail.recv().await.unwrap() else { unreachable!() }; + app.emit("paused", ()).unwrap(); Ok(()) } @@ -58,6 +60,7 @@ pub async fn next(app: AppHandle, ctrl_handle: State<'_, ControllerHandle>) println!("next"); app.emit("now_playing_change", _Song::from(&song)).unwrap(); app.emit("queue_updated", ()).unwrap(); + app.emit("playing", ()).unwrap(); Ok(()) } diff --git a/src/App.tsx b/src/App.tsx index a354661..ba1afdf 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,7 @@ const appWindow = getCurrentWebviewWindow(); function App() { const library = useState([]); const [queue, setQueue] = useState([]); + const [playing, setPlaying] = useState(false); const [nowPlaying, setNowPlaying] = useState( { unlisten.then((f) => f()) } }, []); + useEffect(() => { + const unlisten = appWindow.listen("playing", (_) => { + setPlaying(true) + }) + return () => { unlisten.then((f) => f()) } + }, []); + useEffect(() => { + const unlisten = appWindow.listen("paused", (_) => { + setPlaying(false) + }) + return () => { unlisten.then((f) => f()) } + }, []); + useEffect(() => { getConfig(); }, []) @@ -60,7 +74,7 @@ function App() {

- +
{ nowPlaying } @@ -110,28 +124,31 @@ interface MainViewProps { function MainView({ lib_ref }: MainViewProps) { const [library, setLibrary] = lib_ref; + useEffect(() => { + const unlisten = appWindow.listen("library_loaded", (_) => { + invoke('get_library').then((lib) => { + setLibrary([...(lib as any[]).map((song, i) => { + console.log(song); + + return ( + + ) + })]) + }) + }) + return () => { unlisten.then((f) => f()) } + }, []); + return (
- main view - +

Library

{ library }
) @@ -162,16 +179,19 @@ function Song(props: SongProps) { invoke('add_song_to_queue', { uuid: props.uuid, location: 'Library' }).then(() => {} ) }} >Add to Queue +
) } -function PlayBar() { - let [playing, setPlaying] = useState('play'); - - - +interface PlayBarProps { + playing: boolean, + setPlaying: React.Dispatch> +} +function PlayBar({ playing, setPlaying }: PlayBarProps) { return (
@@ -181,14 +201,9 @@ function PlayBar() {
+ setPlaying( playing ? false : true ); + invoke( playing ? 'pause' : 'play' ).then(() => {}) + }}>{ playing ? 'pause' : 'play' } { invoke('set_volume', { volume: volume.target.value }).then(() => {})