diff --git a/.gitignore b/.gitignore index a9e4c0b..7a0594f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ music_database* *.m3u *.m3u8 *.json +*.zip +*.xml diff --git a/src/config/config.rs b/src/config/config.rs index 9314d65..15b7121 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -71,8 +71,10 @@ impl ConfigLibraries { } pub fn get_library(&self, uuid: &Uuid) -> Result { + dbg!(&uuid); for library in &self.libraries { if &library.uuid == uuid { + dbg!(&library.uuid); return Ok(library.to_owned()) } } @@ -128,8 +130,8 @@ impl Config { let mut file: File = File::open(path)?; let mut bun: String = String::new(); _ = file.read_to_string(&mut bun); - let ny: Config = serde_json::from_str::(&bun)?; - Ok(ny) + let config: Config = serde_json::from_str::(&bun)?; + Ok(config) } } diff --git a/src/music_controller/controller.rs b/src/music_controller/controller.rs index 98c3ca8..da2ea0f 100644 --- a/src/music_controller/controller.rs +++ b/src/music_controller/controller.rs @@ -2,7 +2,7 @@ //! player. It manages queues, playback, library access, and //! other functions -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; use std::time::Duration; use crossbeam_channel::{Sender, Receiver}; @@ -18,7 +18,7 @@ use rayon::iter::Rev; use uuid::Uuid; use crate::config; -use crate::music_storage::library::Tag; +use crate::music_storage::library::{Tag, URI}; use crate::music_storage::playlist::Playlist; use crate::{ music_player::Player, @@ -31,7 +31,7 @@ use crate::{ struct Queue { player: Player, name: String, - songs: Playlist, + songs: Vec, } impl Queue { fn new() -> Result> { @@ -39,10 +39,15 @@ impl Queue { Queue { player: Player::new()?, name: String::new(), - songs: Playlist::new() + songs: Vec::new() } ) } + fn set_tracks(&mut self, tracks: Vec) { + let mut tracks = tracks; + self.songs.clear(); + self.songs.append(&mut tracks); + } } pub struct Controller { @@ -54,39 +59,47 @@ pub struct Controller { queue_mail: Vec>, } #[derive(Debug)] - pub enum ControllerCommand { Default, Test } -#[derive(Debug)] +#[derive(Debug)] enum ControllerResponse { Empty, QueueMailMan(MailMan), } -#[derive(Debug)] +#[derive(Debug)] pub enum DatabaseCommand { Default, Test, GetSongs, + QueryUuid(Uuid), + QueryUuids(Vec), + ReadFolder(String), } -#[derive(Debug)] +#[derive(Debug)] enum DatabaseResponse { Empty, + Song(Song), Songs(Vec), } + #[derive(Debug)] enum QueueCommand { Default, Test, Play, Pause, + SetSongs(Vec), + // SetLocation(URI), + Enqueue(URI), } + #[derive(Debug)] enum QueueResponse { Default, @@ -94,11 +107,11 @@ enum QueueResponse { } #[derive(Debug)] - struct MailMan { pub tx: Sender, rx: Receiver } + impl MailMan { pub fn new() -> Self { let (tx, rx) = unbounded::(); @@ -129,12 +142,13 @@ impl MailMan { #[allow(unused_variables)] impl Controller { - pub fn start(config: PathBuf) -> Result> { - let config = Config::read_file(config)?; + pub fn start(config_path: String) -> Result> { + 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 lib = MusicLibrary::init(config.clone(), uuid)?; + let mut lib = MusicLibrary::init(config.clone(), uuid)?; let (out_thread_controller, in_thread) = MailMan::double(); let monitor_thread = spawn(move || { @@ -167,6 +181,26 @@ impl Controller { 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(); }, + 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(); + } } } @@ -184,6 +218,7 @@ impl Controller { } ) } + fn get_db_songs(&self) -> Vec { self.db_mail.send(DatabaseCommand::GetSongs); match self.db_mail.recv().unwrap() { @@ -192,29 +227,76 @@ impl Controller { } } + pub fn new_queue(&mut self) { let (out_thread_queue, in_thread) = MailMan::::double(); let queues_monitor = spawn(move || { use QueueCommand::*; + let mut queue = Queue::new().unwrap(); loop { let command = in_thread.recv().unwrap(); match command { Default => {}, - Test => {}, - Play => {}, + Test => { in_thread.send(QueueResponse::Test).unwrap() }, + Play => { + queue.player.play().unwrap(); + in_thread.send(QueueResponse::Default).unwrap(); + }, Pause => {}, + SetSongs(songs) => { + queue.set_tracks(songs); + in_thread.send(QueueResponse::Default).unwrap(); + }, + Enqueue(uri) => { + queue.player.enqueue_next(&uri); + } } } }); self.queue_mail.push(out_thread_queue); } + + fn play(&self, index: usize) -> Result<(), Box> { + let mail = &self.queue_mail[index]; + mail.send(QueueCommand::Play)?; + dbg!(mail.recv()?); + Ok(()) + } + + fn set_songs(&self, index: usize, songs: Vec) -> Result<(), Box> { + let mail = &self.queue_mail[index]; + mail.send(QueueCommand::SetSongs(songs))?; + dbg!(mail.recv()?); + Ok(()) + } + + fn enqueue(&self, index: usize, uri: URI) -> Result<(), Box> { + let mail = &self.queue_mail[index]; + mail.send(QueueCommand::Enqueue(uri))?; + dbg!(mail.recv()?); + Ok(()) + } + fn scan_folder(&self, folder: String) -> Result<(), Box> { + let mail = &self.db_mail; + mail.send(DatabaseCommand::ReadFolder(folder))?; + dbg!(mail.recv()?); + Ok(()) + } + } #[test] fn name() { - let a = Controller::start(PathBuf::from("test-config/config_test.json")).unwrap(); - // sleep(Duration::from_millis(5000)); - _ = a.controller_mail.send(ControllerCommand::Test); - // dbg!(a.get_db_songs()); - // sleep(Duration::from_secs(6)); + 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)); + a.scan_folder("test-config/music/".to_string()); + a.new_queue(); + let songs = a.get_db_songs(); + a.enqueue(0, songs[0].location.clone()); + a.play(0).unwrap(); + + sleep(Duration::from_secs(10)); } \ No newline at end of file diff --git a/src/music_storage/db_reader/foobar/reader.rs b/src/music_storage/db_reader/foobar/reader.rs index 3cb417d..324b863 100644 --- a/src/music_storage/db_reader/foobar/reader.rs +++ b/src/music_storage/db_reader/foobar/reader.rs @@ -1,6 +1,8 @@ use std::collections::BTreeMap; use std::{fs::File, io::Read, path::Path, time::Duration}; +use uuid::Uuid; + use super::utils::meta_offset; use crate::music_storage::db_reader::common::{get_bytes, get_bytes_vec}; use crate::music_storage::db_reader::extern_library::ExternalLibrary; @@ -177,6 +179,7 @@ impl FoobarPlaylistTrack { Song { location, + uuid: Uuid::new_v4(), plays: 0, skips: 0, favorited: false, diff --git a/src/music_storage/db_reader/itunes/reader.rs b/src/music_storage/db_reader/itunes/reader.rs index 3bc73f6..15f89a9 100644 --- a/src/music_storage/db_reader/itunes/reader.rs +++ b/src/music_storage/db_reader/itunes/reader.rs @@ -170,6 +170,7 @@ impl ExternalLibrary for ITunesLibrary { let ny: Song = Song { location: sug, + uuid: Uuid::new_v4(), plays: track.plays, skips: 0, favorited: track.favorited, diff --git a/src/music_storage/library.rs b/src/music_storage/library.rs index 502429b..e788639 100644 --- a/src/music_storage/library.rs +++ b/src/music_storage/library.rs @@ -119,6 +119,7 @@ impl ToString for Field { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct Song { pub location: URI, + pub uuid: Uuid, pub plays: i32, pub skips: i32, pub favorited: bool, @@ -256,6 +257,7 @@ impl Song { let new_song = Song { location: URI::Local(binding), + uuid: Uuid::new_v4(), plays: 0, skips: 0, favorited: false, @@ -378,6 +380,7 @@ impl Song { start, end, }, + uuid: Uuid::new_v4(), plays: 0, skips: 0, favorited: false, @@ -552,7 +555,7 @@ pub struct MusicLibrary { #[test] fn library_init() { - let config = Config::read_file(PathBuf::from("config_test.json")).unwrap(); + let config = Config::read_file(PathBuf::from("test_config/config_test.json")).unwrap(); let target_uuid = config.libraries.libraries[0].uuid; let a = MusicLibrary::init(Arc::new(RwLock::from(config)), target_uuid).unwrap(); dbg!(a); @@ -575,13 +578,14 @@ impl MusicLibrary { /// the [MusicLibrary] Vec pub fn init(config: Arc>, uuid: Uuid) -> Result> { let global_config = &*config.read().unwrap(); + let path = global_config.libraries.get_library(&uuid)?.path; - let library: MusicLibrary = match global_config.libraries.get_library(&uuid)?.path.exists() { - true => read_file(global_config.libraries.get_library(&uuid)?.path)?, + let library: MusicLibrary = match path.exists() { + true => read_file(path)?, false => { // If the library does not exist, re-create it let lib = MusicLibrary::new(String::new(), uuid); - write_file(&lib, global_config.libraries.get_library(&uuid)?.path)?; + write_file(&lib, path)?; lib } }; @@ -643,6 +647,26 @@ impl MusicLibrary { } } + /// Queries for a [Song] by its [Uuid], returning a single `Song` + /// with the `Uuid` that matches along with its position in the library + pub fn query_uuid(&self, uuid: &Uuid) -> Option<(&Song, usize)> { + let result = self + .library + .par_iter() + .enumerate() + .try_for_each(|(i, track)| { + if uuid == &track.uuid { + return std::ops::ControlFlow::Break((track, i)); + } + Continue(()) + }); + + match result { + Break(song) => Some(song), + Continue(_) => None, + } + } + /// Queries for a [Song] by its [PathBuf], returning a `Vec<&Song>` /// with matching `PathBuf`s fn query_path(&self, path: PathBuf) -> Option> {