diff --git a/.gitignore b/.gitignore index eb63ef5..a4fc833 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ dist-ssr target Cargo.lock .cargo + +test-config \ No newline at end of file diff --git a/dmp-core/Cargo.toml b/dmp-core/Cargo.toml index 3e44a8d..916eb73 100644 --- a/dmp-core/Cargo.toml +++ b/dmp-core/Cargo.toml @@ -37,7 +37,7 @@ crossbeam = "0.8.2" quick-xml = "0.31.0" leb128 = "0.2.5" urlencoding = "2.1.3" -m3u8-rs = "5.0.5" +m3u8-rs = "6.0.0" thiserror = "1.0.56" uuid = { version = "1.6.1", features = ["v4", "serde"] } serde_json = "1.0.111" @@ -54,3 +54,5 @@ text_io = "0.1.12" tokio = { version = "1.40.0", features = ["macros", "rt"] } async-channel = "2.3.1" ciborium = "0.2.2" +itertools = "0.13.0" +directories = "5.0.1" diff --git a/dmp-core/src/music_controller/controller.rs b/dmp-core/src/music_controller/controller.rs index 56d8088..895a1bf 100644 --- a/dmp-core/src/music_controller/controller.rs +++ b/dmp-core/src/music_controller/controller.rs @@ -3,11 +3,13 @@ //! other functions #![allow(while_true)] +use itertools::Itertools; use kushi::{Queue, QueueItemType}; use kushi::{QueueError, QueueItem}; use serde::{Deserialize, Serialize}; use std::error::Error; use std::marker::PhantomData; +use std::path::PathBuf; use std::sync::{Arc, RwLock}; use thiserror::Error; use uuid::Uuid; @@ -15,6 +17,7 @@ use uuid::Uuid; use crate::config::ConfigError; use crate::music_player::player::{Player, PlayerError}; use crate::music_storage::library::Song; +use crate::music_storage::playlist::{ExternalPlaylist, Playlist, PlaylistFolderItem}; use crate::{config::Config, music_storage::library::MusicLibrary}; use super::queue::{QueueAlbum, QueueSong}; @@ -87,28 +90,20 @@ pub enum LibraryCommand { Song(Uuid), AllSongs, GetLibrary, + Playlist(Uuid), + ImportM3UPlayList(PathBuf) } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum LibraryResponse { - Song(Song), + Ok, + Song(Song, usize), AllSongs(Vec), Library(MusicLibrary), + Playlist(ExternalPlaylist), + ImportM3UPlayList(Uuid, String) } -#[derive(Debug, PartialEq, PartialOrd, Clone)] -enum InnerLibraryCommand { - Song(Uuid), - AllSongs, -} - -#[derive(Debug, PartialEq, Clone)] -enum InnerLibraryResponse<'a> { - Song(&'a Song, usize), - AllSongs(&'a Vec), -} - - #[derive(Debug, PartialEq, Clone)] pub enum QueueCommand { Append(QueueItem, bool), @@ -134,7 +129,10 @@ pub struct ControllerInput { MailMan, MailMan, ), - lib_mail: MailMan, + lib_mail: ( + MailMan, + MailMan + ), queue_mail: ( MailMan, MailMan @@ -157,13 +155,13 @@ impl ControllerHandle { ( ControllerHandle { - lib_mail: lib_mail.0, + lib_mail: lib_mail.0.clone(), player_mail: player_mail.0.clone(), queue_mail: queue_mail.0.clone() }, ControllerInput { player_mail, - lib_mail: lib_mail.1, + lib_mail: lib_mail, queue_mail: queue_mail, library, config @@ -203,7 +201,6 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { // true, // ); // } - let inner_lib_mail = MailMan::double(); let queue = queue; std::thread::scope(|scope| { @@ -215,14 +212,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(); + let _lib_mail = lib_mail.0.clone(); scope .spawn(async move { Controller::

::player_command_loop( _player, player_mail.1, queue_mail.0, - _inner_lib_mail + _lib_mail, ) .await .unwrap(); @@ -235,12 +232,7 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { }); scope .spawn(async { - Controller::

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

::outer_library_loop(lib_mail, inner_lib_mail.0) + Controller::

::library_loop(lib_mail.1, &mut library) .await .unwrap(); }); @@ -265,7 +257,7 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { player: Arc>, player_mail: MailMan, queue_mail: MailMan, - inner_lib_mail: MailMan> + lib_mail: MailMan, ) -> Result<(), ()> { let mut first = true; while true { @@ -306,13 +298,13 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { 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(); + lib_mail.send(LibraryCommand::AllSongs).await.unwrap(); - let InnerLibraryResponse::AllSongs(songs) = inner_lib_mail.recv().await.unwrap() else { + let LibraryResponse::AllSongs(songs) = 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 { + lib_mail.send(LibraryCommand::Song(np_song.song.uuid.clone())).await.unwrap(); + let LibraryResponse::Song(_, i) = lib_mail.recv().await.unwrap() else { unreachable!() }; if let Some(song) = songs.get(i + 49) { @@ -373,8 +365,8 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { } 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 { + lib_mail.send(LibraryCommand::Song(uuid)).await.unwrap(); + let LibraryResponse::Song(song, index) = lib_mail.recv().await.unwrap() else { unreachable!() }; queue_mail.send(QueueCommand::Clear).await.unwrap(); @@ -392,8 +384,8 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { // ... // 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 { + lib_mail.send(LibraryCommand::AllSongs).await.unwrap(); + let LibraryResponse::AllSongs(songs) = lib_mail.recv().await.unwrap() else { unreachable!() }; @@ -419,59 +411,32 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { Ok(()) } - async fn outer_library_loop( + async fn 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 InnerLibraryResponse::Song(song, i) = inner_lib_mail.recv().await.unwrap() else { - unimplemented!(); - }; - lib_mail.send(LibraryResponse::Song(song.clone())).await.unwrap(); - } - LibraryCommand::AllSongs => { - inner_lib_mail - .send(InnerLibraryCommand::AllSongs) - .await - .unwrap(); - let x = inner_lib_mail.recv().await.unwrap(); - if let InnerLibraryResponse::AllSongs(songs) = x { - lib_mail.send(LibraryResponse::AllSongs(songs.clone())).await.unwrap(); - } else { - unreachable!() - } - }, - _ => { todo!() } - } - } - 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, i): (&'c Song, usize) = library.query_uuid(&uuid).unwrap(); - lib_mail - .send(InnerLibraryResponse::Song(song, i)) - .await - .unwrap(); + LibraryCommand::Song(uuid) => { + let (song, i) = library.query_uuid(&uuid).unwrap(); + lib_mail.send(LibraryResponse::Song(song.clone(), i)).await.unwrap(); } - InnerLibraryCommand::AllSongs => { - let songs: &'c Vec = &library.library; - lib_mail.send(InnerLibraryResponse::AllSongs(songs)) - .await - .unwrap(); + LibraryCommand::AllSongs => { + lib_mail.send(LibraryResponse::AllSongs(library.library.clone())).await.unwrap(); + }, + LibraryCommand::Playlist(uuid) => { + let playlist = library.query_playlist_uuid(&uuid).unwrap(); + lib_mail.send(LibraryResponse::Playlist(ExternalPlaylist::from_playlist(playlist, &library))).await.unwrap(); } + LibraryCommand::ImportM3UPlayList(path) => { + let playlist = Playlist::from_m3u(path, library).unwrap(); + let uuid = playlist.uuid.clone(); + let name = playlist.title.clone(); + library.playlists.items.push(PlaylistFolderItem::List(playlist)); + + lib_mail.send(LibraryResponse::ImportM3UPlayList(uuid, name)).await.unwrap(); + } + _ => { todo!() } } } Ok(()) diff --git a/dmp-core/src/music_storage/library.rs b/dmp-core/src/music_storage/library.rs index 587419d..a494d6c 100644 --- a/dmp-core/src/music_storage/library.rs +++ b/dmp-core/src/music_storage/library.rs @@ -2,6 +2,7 @@ use super::playlist::{Playlist, PlaylistFolder}; // Crate things use super::utils::{find_images, normalize, read_file, write_file}; use crate::config::Config; +use crate::music_storage::playlist::PlaylistFolderItem; use std::cmp::Ordering; // Various std things @@ -687,7 +688,7 @@ impl AlbumTrack { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct MusicLibrary { pub name: String, pub uuid: Uuid, @@ -1237,8 +1238,12 @@ impl MusicLibrary { Ok(albums) } - pub fn query_playlist_uuid(&self, uuid: &Uuid) -> Result> { - todo!() + pub fn query_playlist_uuid(&self, uuid: &Uuid) -> Option<&Playlist> { + self.playlists.query_uuid(uuid) + } + + pub fn push_playlist(&mut self, playlist: PlaylistFolderItem) { + self.playlists.items.push(playlist); } } diff --git a/dmp-core/src/music_storage/playlist.rs b/dmp-core/src/music_storage/playlist.rs index 30611d0..bc6c1e3 100644 --- a/dmp-core/src/music_storage/playlist.rs +++ b/dmp-core/src/music_storage/playlist.rs @@ -1,4 +1,5 @@ use std::error::Error; +use std::path::Path; use std::{ fs::File, io::Read, @@ -10,6 +11,7 @@ use std::time::Duration; // use chrono::Duration; use super::library::{AlbumArt, MusicLibrary, Song, Tag, URI}; +use itertools::Itertools; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -28,8 +30,8 @@ nest! { #[derive(Debug, Clone, Deserialize, Serialize)]* #[derive(Default)] pub struct PlaylistFolder { - name: String, - items: Vec< + pub(crate) name: String, + pub(crate) items: Vec< pub enum PlaylistFolderItem { Folder(PlaylistFolder), List(Playlist) @@ -38,15 +40,29 @@ nest! { } } +impl PlaylistFolder { + pub fn query_uuid(&self, uuid: &Uuid) -> Option<&Playlist> { + for item in &self.items { + match item { + PlaylistFolderItem::Folder(folder) => return folder.query_uuid(uuid), + PlaylistFolderItem::List(ref playlist) => if &playlist.uuid == uuid { + return Some(&playlist); + } + } + } + None + } +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Playlist { - uuid: Uuid, - title: String, - cover: Option, - tracks: Vec, - sort_order: SortOrder, - play_count: i32, - play_time: Duration, + pub(crate) uuid: Uuid, + pub(crate) title: String, + pub(crate) cover: Option, + pub(crate) tracks: Vec, + pub(crate) sort_order: SortOrder, + pub(crate) play_count: i32, + pub(crate) play_time: Duration, } impl Playlist { @@ -115,7 +131,7 @@ impl Playlist { super::utils::read_file(PathBuf::from(path)) } - pub fn to_m3u8( + pub fn to_m3u( &mut self, lib: Arc>, location: &str, @@ -146,9 +162,9 @@ impl Playlist { }) .collect::>(); - let m3u8 = MediaPlaylist { + let m3u = MediaPlaylist { version: Some(6), - target_duration: 3.0, + target_duration: 3, media_sequence: 338559, discontinuity_sequence: 1234, end_list: true, @@ -163,18 +179,15 @@ impl Playlist { .truncate(true) .write(true) .open(location)?; - m3u8.write_to(&mut file)?; + m3u.write_to(&mut file)?; Ok(()) } - pub fn from_m3u8( - path: &str, - lib: Arc>, + pub fn from_m3u( + m3u_path: impl AsRef, + lib: &mut MusicLibrary, ) -> Result> { - let mut file = match File::open(path) { - Ok(file) => file, - Err(e) => return Err(e.into()), - }; + let mut file = File::open(&m3u_path)?; let mut bytes = Vec::new(); file.read_to_end(&mut bytes).unwrap(); @@ -182,7 +195,7 @@ impl Playlist { let playlist = match parsed { Result::Ok((_, playlist)) => playlist, - Result::Err(e) => panic!("Parsing error: \n{}", e), + Result::Err(e) => panic!("Parsing error\n{e}"), }; match playlist { @@ -192,28 +205,39 @@ impl Playlist { List2::MediaPlaylist(playlist_) => { let mut uuids = Vec::new(); for seg in playlist_.segments { - let path_ = PathBuf::from(seg.uri.to_owned()); - let mut lib = lib.write().unwrap(); + let seg_path = seg.uri.to_owned(); - let uuid = if let Some((song, _)) = lib.query_uri(&URI::Local(path_.clone())) { + let song_path = if let Ok(path) = PathBuf::from(&seg_path).canonicalize() { + path + } else { + println!("{seg_path}"); + continue; + }; + + let uuid = if let Some((song, _)) = lib.query_uri(&URI::Local(song_path.clone())) { song.uuid } else { - let song_ = Song::from_file(&path_)?; + let song_: Song = match Song::from_file(&song_path) { + Ok(s) => s, + Err(e) => panic!("{e}\npath: {}", song_path.display()) + }; let uuid = song_.uuid.to_owned(); - lib.add_song(song_)?; + _ = lib.add_song(song_); // TODO: Add proper error handling with Library uuid }; uuids.push(uuid); } let mut playlist = Playlist::new(); + let path: &str = m3u_path.as_ref().to_str().unwrap(); + #[cfg(target_family = "windows")] { playlist.title = path .split('\\') .last() .unwrap_or_default() - .strip_suffix(".m3u8") + .strip_suffix(".m3u") .unwrap_or_default() .to_string(); } @@ -223,7 +247,7 @@ impl Playlist { .split("/") .last() .unwrap_or_default() - .strip_suffix(".m3u8") + .strip_suffix(".m3u") .unwrap_or_default() .to_string(); } @@ -313,42 +337,72 @@ impl Default for Playlist { } } + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ExternalPlaylist { + pub uuid: Uuid, + pub title: String, + pub tracks: Vec, + pub sort_order: SortOrder, + pub play_count: i32, + pub play_time: Duration, +} + +impl ExternalPlaylist { + pub(crate) fn from_playlist(playlist: &Playlist, library: &MusicLibrary) -> Self { + let tracks: Vec = playlist.tracks.iter().filter_map(|uuid| { + library.query_uuid(uuid).map(|res| res.0.clone()) + }).collect_vec(); + + Self { + uuid: playlist.uuid.clone(), + title: playlist.title.clone(), + tracks, + sort_order: playlist.sort_order.clone(), + play_count: playlist.play_count, + play_time: playlist.play_time + } + } +} + + #[cfg(test)] mod test_super { use super::*; - use crate::config::tests::read_config_lib; + use crate::config::tests::{new_config_lib, read_config_lib}; #[test] - fn list_to_m3u8() { + fn list_to_m3u() { let (_, lib) = read_config_lib(); let mut playlist = Playlist::new(); let tracks = lib.library.iter().map(|track| track.uuid).collect(); playlist.set_tracks(tracks); - _ = playlist.to_m3u8( + playlist.to_m3u( Arc::new(RwLock::from(lib)), - ".\\test-config\\playlists\\playlist.m3u8", - ); - } - - fn m3u8_to_list() -> Playlist { - let (_, lib) = read_config_lib(); - let arc = Arc::new(RwLock::from(lib)); - let playlist = - Playlist::from_m3u8(".\\test-config\\playlists\\playlist.m3u8", arc).unwrap(); - - _ = playlist.to_file(".\\test-config\\playlists\\playlist"); - dbg!(playlist) + ".\\test-config\\playlists\\playlist.m3u", + ).unwrap(); } #[test] - fn out_queue_sort() { - let (_, lib) = read_config_lib(); - let mut list = m3u8_to_list(); - list.sort_order = SortOrder::Tag(vec![Tag::Album]); + fn m3u_to_list() { + let (_, mut lib) = read_config_lib(); - let songs = &list.out_tracks(Arc::new(RwLock::from(lib))); + let playlist = + Playlist::from_m3u(".\\test-config\\playlists\\playlist", &mut lib).unwrap(); - dbg!(songs); + _ = playlist.to_file(".\\test-config\\playlists\\playlist"); + dbg!(&playlist, playlist.tracks.len()); } + + // #[test] + // fn out_queue_sort() { + // let (_, lib) = read_config_lib(); + // let mut list = m3u_to_list(); + // list.sort_order = SortOrder::Tag(vec![Tag::Album]); + + // let songs = &list.out_tracks(Arc::new(RwLock::from(lib))); + + // dbg!(songs); + // } } diff --git a/kushi-queue/src/lib.rs b/kushi-queue/src/lib.rs index 58de428..5d5b1ef 100644 --- a/kushi-queue/src/lib.rs +++ b/kushi-queue/src/lib.rs @@ -450,7 +450,7 @@ impl< use thiserror::Error; -#[derive(Error, Debug, PartialEq, PartialOrd, Clone)] +#[derive(Error, Debug, PartialEq, Eq, PartialOrd, Clone)] pub enum QueueError { #[error("Index out of bounds! Index {index} is over len {len}")] OutOfBounds { index: usize, len: usize }, diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fd0cc8a..ec4e613 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -33,6 +33,7 @@ mime = "0.3.17" file-format = "0.26.0" chrono = { version = "0.4.38", features = ["serde"] } itertools = "0.13.0" +rfd = "0.15.1" [features] default = [ "custom-protocol" ] diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index fc48be3..8075af9 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -10,7 +10,7 @@ use crate::wrappers::_Song; #[tauri::command] pub async fn add_song_to_queue(app: AppHandle, ctrl_handle: State<'_, ControllerHandle>, uuid: Uuid, location: PlayerLocation) -> Result<(), String> { ctrl_handle.lib_mail.send(dmp_core::music_controller::controller::LibraryCommand::Song(uuid)).await.unwrap(); - let LibraryResponse::Song(song) = ctrl_handle.lib_mail.recv().await.unwrap() else { + 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 })), true)).await.unwrap(); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index b86ee19..d11eaee 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -6,7 +6,7 @@ use dmp_core::{config::{Config, ConfigLibrary}, music_controller::controller::{C 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}; +use crate::wrappers::{get_library, play, pause, prev, set_volume, get_song, next, get_queue, import_playlist, get_playlist}; pub mod wrappers; pub mod commands; @@ -74,6 +74,8 @@ pub fn run() { get_queue, add_song_to_queue, play_now, + import_playlist, + get_playlist, ]).manage(ConfigRx(rx)) .manage(LibRx(lib_rx)) .manage(HandleTx(handle_tx)) @@ -94,7 +96,7 @@ pub fn run() { futures::executor::block_on(async move { let controller = ctx.app_handle().state::(); controller.lib_mail.send(dmp_core::music_controller::controller::LibraryCommand::Song(Uuid::parse_str(query.as_str()).unwrap())).await.unwrap(); - let LibraryResponse::Song(song) = controller.lib_mail.recv().await.unwrap() else { unreachable!() }; + 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( diff --git a/src-tauri/src/wrappers.rs b/src-tauri/src/wrappers.rs index 6d016b1..a56d6ce 100644 --- a/src-tauri/src/wrappers.rs +++ b/src-tauri/src/wrappers.rs @@ -1,14 +1,16 @@ -use std::collections::BTreeMap; +use std::{collections::BTreeMap, path::PathBuf}; use chrono::{DateTime, Utc, serde::ts_milliseconds_option}; use crossbeam::channel::Sender; -use dmp_core::{music_controller::controller::{ControllerHandle, LibraryCommand, LibraryResponse, PlayerResponse, QueueCommand, QueueResponse}, music_storage::library::{BannedType, Song, URI}}; +use dmp_core::{music_controller::controller::{ControllerHandle, LibraryCommand, LibraryResponse, PlayerResponse, QueueCommand, QueueResponse}, music_storage::library::{BannedType, Song, Tag, URI}}; use itertools::Itertools; use kushi::QueueItemType; use serde::Serialize; use tauri::{ipc::Response, AppHandle, Emitter, State, Wry}; use uuid::Uuid; +use crate::commands; + pub struct ArtworkRx(pub Sender>); #[tauri::command] @@ -130,6 +132,7 @@ impl From<&Song> for _Song { #[tauri::command] pub async fn get_library(ctrl_handle: State<'_, ControllerHandle>) -> Result, String> { ctrl_handle.lib_mail.send(LibraryCommand::AllSongs).await.unwrap(); + println!("getting library"); let LibraryResponse::AllSongs(songs) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") }; let _songs = songs.iter().map(|song| _Song::from(song)).collect::>(); @@ -138,9 +141,40 @@ pub async fn get_library(ctrl_handle: State<'_, ControllerHandle>) -> Result) -> Result<(), String> { +pub async fn get_playlist(ctrl_handle: State<'_, ControllerHandle>, uuid: Uuid) -> Result, String> { + ctrl_handle.lib_mail.send(LibraryCommand::Playlist(uuid)).await.unwrap(); + let LibraryResponse::Playlist(playlist) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") }; + + let songs = playlist.tracks.iter().map(|song| _Song::from(song)).collect::>(); + println!("Got Playlist {}, len {}", playlist.title, playlist.tracks.len()); + Ok(songs) +} + +#[tauri::command] +pub async fn import_playlist(ctrl_handle: State<'_, ControllerHandle>) -> Result { + let file = rfd::AsyncFileDialog::new() + .add_filter("m3u8 Playlist", &["m3u8", "m3u"]) + .set_title("Import a Playlist") + .pick_file() + .await + .unwrap(); + + ctrl_handle.lib_mail.send(LibraryCommand::ImportM3UPlayList(PathBuf::from(file.path()))).await.unwrap(); + let LibraryResponse::ImportM3UPlayList(uuid, name) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") }; + println!("Imported Playlist {name}"); + Ok(PlaylistPayload {uuid, name}) +} + +#[derive(Serialize)] +pub struct PlaylistPayload { + uuid: Uuid, + name: String +} + +#[tauri::command] +pub async fn get_song(ctrl_handle: State<'_, ControllerHandle>) -> Result<_Song, String> { ctrl_handle.lib_mail.send(LibraryCommand::Song(Uuid::default())).await.unwrap(); - let LibraryResponse::Song(_) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") }; - println!("got songs"); - Ok(()) + let LibraryResponse::Song(song, _) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") }; + println!("got song {}", &song.tags.get(&Tag::Title).unwrap_or(&String::new())); + Ok(_Song::from(&song)) } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index ba1afdf..8582336 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,6 +13,8 @@ function App() { const library = useState([]); const [queue, setQueue] = useState([]); const [playing, setPlaying] = useState(false); + const [playlists, setPlaylists] = useState([]); + const [viewName, setViewName] = useState("Library"); const [nowPlaying, setNowPlaying] = useState(

- - + +
{ nowPlaying } - +
@@ -87,47 +89,86 @@ function App() { export default App; -function getConfig(): any { - invoke('get_config').then( (_config) => { - let config = _config as Config; - if (config.libraries.libraries.length == 0) { - newWindow() - } else { - // console.log("else"); - invoke('lib_already_created').then(() => {}) - } - }) +interface PlaylistHeadProps { + playlists: JSX.Element[] + setPlaylists: React.Dispatch>, + setViewName: React.Dispatch>, + setLibrary: React.Dispatch>, } -function newWindow() { - invoke('new_library_window').then(() => {}) -} +function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: PlaylistHeadProps) { -function PlaylistHead() { + let handle_import = () => { + invoke('import_playlist').then((_res) => { + let res = _res as any; + + setPlaylists([ + ...playlists, + + ]) + console.log(res.name); + }) + } return (
- - - - - - - + + { playlists } +
) } interface MainViewProps { lib_ref: [JSX.Element[], React.Dispatch>], + viewName: string } -function MainView({ lib_ref }: MainViewProps) { +function MainView({ lib_ref, viewName }: MainViewProps) { const [library, setLibrary] = lib_ref; useEffect(() => { const unlisten = appWindow.listen("library_loaded", (_) => { + console.log("library_loaded"); + invoke('get_library').then((lib) => { - setLibrary([...(lib as any[]).map((song, i) => { + setLibrary([...(lib as any[]).map((song) => { console.log(song); return ( @@ -146,9 +187,10 @@ function MainView({ lib_ref }: MainViewProps) { return () => { unlisten.then((f) => f()) } }, []); + return (
-

Library

+

{ viewName }

{ library }
) @@ -263,3 +305,19 @@ function QueueSong({ song }: QueueSongProps) { // ) } + +function getConfig(): any { + invoke('get_config').then( (_config) => { + let config = _config as Config; + if (config.libraries.libraries.length == 0) { + newWindow() + } else { + // console.log("else"); + invoke('lib_already_created').then(() => {}) + } + }) +} + +function newWindow() { + invoke('new_library_window').then(() => {}) +}