From 15988ae8085b4e27a9fe3f8cbfa4e63873a3087e Mon Sep 17 00:00:00 2001 From: MrDulfin Date: Mon, 23 Sep 2024 20:52:23 -0400 Subject: [PATCH] cobbled together some test code for controller and ran `cargo fmt` --- .gitignore | 4 +- Cargo.toml | 6 + src/config/mod.rs | 8 +- src/config/other_settings.rs | 21 +- src/lib.rs | 2 +- src/music_controller/connections.rs | 3 - src/music_controller/controller.rs | 490 ++++++++++++++----- src/music_player/gstreamer.rs | 81 +-- src/music_player/player.rs | 4 +- src/music_storage/db_reader/itunes/reader.rs | 47 +- src/music_storage/db_reader/mod.rs | 2 +- src/music_storage/library.rs | 74 +-- 12 files changed, 515 insertions(+), 227 deletions(-) diff --git a/.gitignore b/.gitignore index b2d0597..0714bdf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ target/ test-config/ # Rust configuration Cargo.lock +.cargo/ # Database files *.db3* @@ -14,5 +15,4 @@ music_database* *.m3u8 *.json *.zip -*.xml - +*.xml \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index b4ab8d0..3b0a6d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,3 +49,9 @@ listenbrainz = "0.7.0" discord-rpc-client = "0.4.0" nestify = "0.3.3" kushi = "0.1.3" +moro = "0.4.0" +moro-local = "0.4.0" +futures = "0.3.30" +text_io = "0.1.12" +tokio = { version = "1.40.0", features = ["macros", "rt"] } +async-channel = "2.3.1" diff --git a/src/config/mod.rs b/src/config/mod.rs index 6371afb..85bfffe 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -193,10 +193,10 @@ pub mod tests { use crate::music_storage::library::MusicLibrary; use std::{ path::PathBuf, - sync::{Arc, RwLock}, }; pub fn new_config_lib() -> (Config, MusicLibrary) { + _ = std::fs::create_dir_all("test-config/music/"); let lib = ConfigLibrary::new( PathBuf::from("test-config/library"), String::from("library"), @@ -216,7 +216,8 @@ pub mod tests { ) .unwrap(); lib.scan_folder("test-config/music/").unwrap(); - lib.save(config.libraries.get_default().unwrap().path.clone()).unwrap(); + lib.save(config.libraries.get_default().unwrap().path.clone()) + .unwrap(); (config, lib) } @@ -234,7 +235,8 @@ pub mod tests { lib.scan_folder("test-config/music/").unwrap(); - lib.save(config.libraries.get_default().unwrap().path.clone()).unwrap(); + lib.save(config.libraries.get_default().unwrap().path.clone()) + .unwrap(); (config, lib) } diff --git a/src/config/other_settings.rs b/src/config/other_settings.rs index 164221f..1b80e94 100644 --- a/src/config/other_settings.rs +++ b/src/config/other_settings.rs @@ -1,20 +1,7 @@ pub enum Setting { - String { - name: String, - value: String - }, - Int { - name: String, - value: i32 - }, - Bool { - name: String, - value: bool - }, - -} - -pub struct Form { - + String { name: String, value: String }, + Int { name: String, value: i32 }, + Bool { name: String, value: bool }, } +pub struct Form {} diff --git a/src/lib.rs b/src/lib.rs index 6f93ec1..0f41571 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,8 @@ pub mod music_storage { } pub mod music_controller { - pub mod controller; pub mod connections; + pub mod controller; pub mod queue; } diff --git a/src/music_controller/connections.rs b/src/music_controller/connections.rs index b2ceb85..5ea823e 100644 --- a/src/music_controller/connections.rs +++ b/src/music_controller/connections.rs @@ -13,8 +13,6 @@ // use super::controller::DatabaseResponse; - - // impl Controller { // pub fn listenbrainz_authenticate(&mut self) -> Result> { // let config = &self.config.read().unwrap(); @@ -96,7 +94,6 @@ // c.q_enqueue(0, songs[1].location.to_owned()).unwrap(); // c.q_play(0).unwrap(); - // sleep(Duration::from_secs(100)); // c.lbz_scrobble(client, songs[1].uuid).unwrap(); // } diff --git a/src/music_controller/controller.rs b/src/music_controller/controller.rs index 3c9dcb3..12ce0fa 100644 --- a/src/music_controller/controller.rs +++ b/src/music_controller/controller.rs @@ -1,35 +1,24 @@ //! The [Controller] is the input and output for the entire //! player. It manages queues, playback, library access, and //! other functions +#![allow(while_true)] -use crossbeam_channel; -use crossbeam_channel::{Receiver, Sender}; -use kushi::QueueError; use kushi::{Queue, QueueItemType}; -use std::path::PathBuf; -use std::sync::{Arc, Mutex, RwLock}; -use std::thread::spawn; -use thiserror::Error; - -use crossbeam_channel::unbounded; +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, PlayerCommand, PlayerError}; -use crate::{ - config::Config, music_storage::library::MusicLibrary, -}; +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 { - pub queue: Arc>>, - pub config: Arc>, - pub library: MusicLibrary, - pub player: Arc>, -} +pub struct Controller<'a, P>(&'a PhantomData

); #[derive(Error, Debug)] pub enum ControllerError { @@ -52,146 +41,405 @@ pub enum PlayerLocation { Custom, } -#[derive(Debug)] -pub(super) struct MailMan { - pub tx: Sender, - rx: Receiver, +#[derive(Debug, Clone)] +pub struct MailMan { + tx: async_channel::Sender, + rx: async_channel::Receiver, } -impl MailMan { - pub fn new() -> Self { - let (tx, rx) = unbounded::(); - MailMan { tx, rx } - } -} -impl MailMan { - pub fn double() -> (MailMan, MailMan) { - let (tx, rx) = unbounded::(); - let (tx1, rx1) = unbounded::(); +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 fn send(&self, mail: T) -> Result<(), Box> { - self.tx.send(mail).unwrap(); - Ok(()) + pub async fn send(&self, mail: Tx) -> Result<(), async_channel::SendError> { + self.tx.send(mail).await } - pub fn recv(&self) -> Result> { - let u = self.rx.recv()?; - Ok(u) + 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 Controller

{ - pub fn start(config_path: T) -> Result > +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 - std::path::PathBuf: std::convert::From, P: Player, { - let config_path = PathBuf::from(config_path); - - let config = Config::read_file(config_path)?; - let uuid = config.libraries.get_default()?.uuid; - - let library = MusicLibrary::init(config.libraries.get_default()?.path.clone(), uuid)?; - let config_ = Arc::new(RwLock::from(config)); - - - let queue: Queue = Queue { + //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 + shuffle: None, }; - let controller = Controller { - queue: Arc::new(RwLock::from(queue)), - config: config_.clone(), - library, - player: Arc::new(Mutex::new(P::new()?)), - }; + 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 = controller.player.clone(); - let queue = controller.queue.clone(); - let controller_thread = spawn(move || { - loop { - let signal = { player.lock().unwrap().message_channel().recv().unwrap() }; - match signal { - PlayerCommand::AboutToFinish => { - println!("Switching songs!"); - - let mut queue = queue.write().unwrap(); - - let uri = queue - .next() - .unwrap() - .clone(); - - player - .lock() - .unwrap() - .enqueue_next(&{ - match uri.item { - QueueItemType::Single(song) => song.song.primary_uri().unwrap().0.clone(), - _ => unimplemented!() - } + let _player = player.clone(); + scope + .spawn(async move { + Controller::

::player_command_loop( + _player, + player_mail.1, + queue_mail.0, + ) + .await + .unwrap(); }) - .unwrap(); - }, - PlayerCommand::EndOfStream => {dbg!()} - _ => {} - } - } + .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(controller) + Ok(()) } - pub fn q_add(&mut self, item: &Uuid, source: PlayerLocation, by_human: bool) { - let item = self.library.query_uuid(item).unwrap().0.to_owned(); - self.queue.write().unwrap().add_item(QueueSong { song: item, location: source }, by_human) + 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::{thread::sleep, time::Duration}; + use std::{ + path::PathBuf, + sync::{Arc, RwLock}, + thread::spawn, + }; - use crate::{config::tests::read_config_lib, music_controller::controller::{PlayerLocation, QueueSong}, music_player::{gstreamer::GStreamer, player::Player}}; + 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; - #[test] - fn construct_controller() { - println!("starto!"); - let config = read_config_lib(); + #[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 next = config.1.library[2].clone(); - { - let controller = Controller::::start("test-config/config_test.json").unwrap(); - { - let mut queue = controller.queue.write().unwrap(); - for x in config.1.library { - queue.add_item(QueueSong { song: x, location: PlayerLocation::Library }, true); + 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()) } - } - { - controller.player.lock().unwrap().enqueue_next(next.primary_uri().unwrap().0).unwrap(); - } - { - controller.player.lock().unwrap().set_volume(0.1); - } - { - controller.player.lock().unwrap().play().unwrap(); - } - println!("I'm a tire"); - } - sleep(Duration::from_secs(10)) + }) + }); + 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(); } } diff --git a/src/music_player/gstreamer.rs b/src/music_player/gstreamer.rs index a6915aa..7733d85 100644 --- a/src/music_player/gstreamer.rs +++ b/src/music_player/gstreamer.rs @@ -46,30 +46,30 @@ impl TryInto for PlayerState { enum PlaybackInfo { Idle, Switching, - Playing{ + Playing { start: Duration, - end: Duration, + end: Duration, }, /// When this is sent, the thread will die! Use it when the [Player] is /// done playing - Finished + Finished, } /// An instance of a music player with a GStreamer backend #[derive(Debug)] pub struct GStreamer { - source: Option, + source: Option, message_rx: crossbeam::channel::Receiver, playback_tx: crossbeam::channel::Sender, - playbin: Arc>, - volume: f64, - start: Option, - end: Option, - paused: Arc>, - position: Arc>>, + playbin: Arc>, + volume: f64, + start: Option, + end: Option, + paused: Arc>, + position: Arc>>, } impl From for PlayerError { @@ -89,7 +89,7 @@ impl GStreamer { fn set_source(&mut self, source: &URI) -> Result<(), PlayerError> { if !source.exists().is_ok_and(|x| x) { // If the source doesn't exist, gstreamer will crash! - return Err(PlayerError::NotFound) + return Err(PlayerError::NotFound); } // Make sure the playback tracker knows the stuff is stopped @@ -110,10 +110,12 @@ impl GStreamer { self.end = Some(Duration::from_std(*end).unwrap()); // Send the updated position to the tracker - self.playback_tx.send(PlaybackInfo::Playing{ - start: self.start.unwrap(), - end: self.end.unwrap() - }).unwrap(); + self.playback_tx + .send(PlaybackInfo::Playing { + start: self.start.unwrap(), + end: self.end.unwrap(), + }) + .unwrap(); // Wait for it to be ready, and then move to the proper position self.play().unwrap(); @@ -125,7 +127,9 @@ impl GStreamer { std::thread::sleep(std::time::Duration::from_millis(1)); } //panic!("Couldn't seek to beginning of cue track in reasonable time (>20ms)"); - return Err(PlayerError::StateChange("Could not seek to beginning of CUE track".into())) + return Err(PlayerError::StateChange( + "Could not seek to beginning of CUE track".into(), + )); } _ => { self.playbin @@ -145,10 +149,12 @@ impl GStreamer { self.end = self.raw_duration(); // Send the updated position to the tracker - self.playback_tx.send(PlaybackInfo::Playing{ - start: self.start.unwrap(), - end: self.end.unwrap() - }).unwrap(); + self.playback_tx + .send(PlaybackInfo::Playing { + start: self.start.unwrap(), + end: self.end.unwrap(), + }) + .unwrap(); } } @@ -223,7 +229,7 @@ impl Player for GStreamer { fn new() -> Result { // Initialize GStreamer, maybe figure out how to nicely fail here if let Err(err) = gst::init() { - return Err(PlayerError::Init(err.to_string())) + return Err(PlayerError::Init(err.to_string())); }; let ctx = glib::MainContext::default(); let _guard = ctx.acquire(); @@ -233,7 +239,7 @@ impl Player for GStreamer { match gst::ElementFactory::make("playbin3").build() { Ok(playbin) => playbin, Err(error) => return Err(PlayerError::Init(error.to_string())), - } + }, )); let playbin = playbin_arc.clone(); @@ -252,7 +258,10 @@ impl Player for GStreamer { .build() .ok_or(PlayerError::Build)?; - playbin.write().unwrap().set_property_from_value("flags", &flags); + playbin + .write() + .unwrap() + .set_property_from_value("flags", &flags); //playbin.write().unwrap().set_property("instant-uri", true); let position = Arc::new(RwLock::new(None)); @@ -262,7 +271,9 @@ impl Player for GStreamer { let (status_tx, status_rx) = unbounded::(); let position_update = Arc::clone(&position); - std::thread::spawn(|| playback_monitor(playbin_arc, status_rx, playback_tx, position_update)); + std::thread::spawn(|| { + playback_monitor(playbin_arc, status_rx, playback_tx, position_update) + }); // Set up the thread to monitor bus messages let playbin_bus_ctrl = Arc::clone(&playbin); @@ -279,11 +290,11 @@ impl Player for GStreamer { gst::MessageView::StreamStart(_) => println!("Stream start"), gst::MessageView::Error(err) => { println!("Error recieved: {}", err); - return glib::ControlFlow::Break + return glib::ControlFlow::Break; } gst::MessageView::Buffering(buffering) => { if *bus_paused.read().unwrap() == true { - return glib::ControlFlow::Continue + return glib::ControlFlow::Continue; } // If the player is not paused, pause it @@ -349,7 +360,7 @@ impl Player for GStreamer { fn play(&mut self) -> Result<(), PlayerError> { if self.state() == PlayerState::Playing { - return Ok(()) + return Ok(()); } *self.paused.write().unwrap() = false; self.set_state(gst::State::Playing)?; @@ -358,7 +369,7 @@ impl Player for GStreamer { fn pause(&mut self) -> Result<(), PlayerError> { if self.state() == PlayerState::Paused || *self.paused.read().unwrap() { - return Ok(()) + return Ok(()); } *self.paused.write().unwrap() = true; self.set_state(gst::State::Paused)?; @@ -472,7 +483,7 @@ fn playback_monitor( .map(|pos| Duration::nanoseconds(pos.nseconds() as i64)); match stats { - PlaybackInfo::Playing{start, end} if pos_temp.is_some() => { + PlaybackInfo::Playing { start, end } if pos_temp.is_some() => { // Check if the current playback position is close to the end let finish_point = end - Duration::milliseconds(2000); if pos_temp.unwrap().num_microseconds() >= end.num_microseconds() { @@ -495,16 +506,14 @@ fn playback_monitor( // This has to be done AFTER the current time in the file // is calculated, or everything else is wrong pos_temp = Some(pos_temp.unwrap() - start) - }, + } PlaybackInfo::Finished => { println!("MONITOR: Shutting down"); *position.write().unwrap() = None; - break - }, - PlaybackInfo::Idle | PlaybackInfo::Switching => { - sent_atf = false - }, - _ => () + break; + } + PlaybackInfo::Idle | PlaybackInfo::Switching => sent_atf = false, + _ => (), } *position.write().unwrap() = pos_temp; diff --git a/src/music_player/player.rs b/src/music_player/player.rs index 1bfe18c..4b74498 100644 --- a/src/music_player/player.rs +++ b/src/music_player/player.rs @@ -41,7 +41,9 @@ pub enum PlayerCommand { pub trait Player { /// Create a new player. - fn new() -> Result where Self: Sized; + fn new() -> Result + where + Self: Sized; /// Get the currently playing [URI] from the player. fn source(&self) -> &Option; diff --git a/src/music_storage/db_reader/itunes/reader.rs b/src/music_storage/db_reader/itunes/reader.rs index 6ff383c..05075f2 100644 --- a/src/music_storage/db_reader/itunes/reader.rs +++ b/src/music_storage/db_reader/itunes/reader.rs @@ -2,13 +2,13 @@ use file_format::FileFormat; use lofty::{AudioFile, LoftyError, ParseOptions, Probe, TagType, TaggedFileExt}; use quick_xml::events::Event; use quick_xml::reader::Reader; -use uuid::Uuid; use std::collections::{BTreeMap, HashMap}; use std::fs::File; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::time::Duration as StdDur; use std::vec::Vec; +use uuid::Uuid; use chrono::prelude::*; @@ -118,7 +118,12 @@ impl ExternalLibrary for ITunesLibrary { buf.clear(); } let elasped = now.elapsed(); - println!("\n\niTunesReader grabbed {} songs in {:#?} seconds\nIDs Skipped: {}", count3, elasped.as_secs(), count4); + println!( + "\n\niTunesReader grabbed {} songs in {:#?} seconds\nIDs Skipped: {}", + count3, + elasped.as_secs(), + count4 + ); let mut lib = ITunesLibrary::new(); lib.tracks.append(converted_songs.as_mut()); lib @@ -175,10 +180,10 @@ impl ExternalLibrary for ITunesLibrary { skips: 0, favorited: track.favorited, banned: if track.banned { - Some(BannedType::All) - }else { - None - }, + Some(BannedType::All) + } else { + None + }, rating: track.rating, format: match FileFormat::from_file(PathBuf::from(&loc)) { Ok(e) => Some(e), @@ -334,25 +339,43 @@ impl ITunesSong { #[cfg(test)] mod tests { - use std::{path::{Path, PathBuf}, sync::{Arc, RwLock}}; + use std::{ + path::{Path, PathBuf}, + sync::{Arc, RwLock}, + }; - use crate::{config::{Config, ConfigLibrary}, music_storage::{db_reader::extern_library::ExternalLibrary, library::MusicLibrary}}; + use crate::{ + config::{Config, ConfigLibrary}, + music_storage::{db_reader::extern_library::ExternalLibrary, library::MusicLibrary}, + }; use super::ITunesLibrary; #[test] fn itunes_lib_test() { let mut config = Config::read_file(PathBuf::from("test-config/config_test.json")).unwrap(); - let config_lib = ConfigLibrary::new(PathBuf::from("test-config/library2"), String::from("library2"), None); + let config_lib = ConfigLibrary::new( + PathBuf::from("test-config/library2"), + String::from("library2"), + None, + ); config.libraries.libraries.push(config_lib.clone()); let songs = ITunesLibrary::from_file(Path::new("test-config\\iTunesLib.xml")).to_songs(); - let mut library = MusicLibrary::init(config.libraries.get_default().unwrap().path.clone(), config_lib.uuid).unwrap(); + let mut library = MusicLibrary::init( + config.libraries.get_default().unwrap().path.clone(), + config_lib.uuid, + ) + .unwrap(); - songs.iter().for_each(|song| library.add_song(song.to_owned()).unwrap()); + songs + .iter() + .for_each(|song| library.add_song(song.to_owned()).unwrap()); config.write_file().unwrap(); - library.save(config.libraries.get_default().unwrap().path.clone()).unwrap(); + library + .save(config.libraries.get_default().unwrap().path.clone()) + .unwrap(); } } diff --git a/src/music_storage/db_reader/mod.rs b/src/music_storage/db_reader/mod.rs index 2a5b979..be1841f 100644 --- a/src/music_storage/db_reader/mod.rs +++ b/src/music_storage/db_reader/mod.rs @@ -10,4 +10,4 @@ pub mod itunes { pub mod reader; } pub mod common; -pub mod extern_library; \ No newline at end of file +pub mod extern_library; diff --git a/src/music_storage/library.rs b/src/music_storage/library.rs index 010fed3..fc24915 100644 --- a/src/music_storage/library.rs +++ b/src/music_storage/library.rs @@ -636,16 +636,12 @@ impl IntoIterator for Album { for (disc, mut tracks) in self.discs { tracks.par_sort_by(|a, b| a.0.cmp(&b.0)); - let mut tracks = tracks.into_iter() - .map(|(track, uuid)| - AlbumTrack { - disc, - track, - uuid - }) + let mut tracks = tracks + .into_iter() + .map(|(track, uuid)| AlbumTrack { disc, track, uuid }) .collect::>(); - vec.append(&mut tracks); + vec.append(&mut tracks); } vec.into_iter() } @@ -654,7 +650,7 @@ impl IntoIterator for Album { pub struct AlbumTrack { disc: u16, track: u16, - uuid: Uuid + uuid: Uuid, } impl AlbumTrack { @@ -820,7 +816,10 @@ impl MusicLibrary { } /// Finds all the audio files within a specified folder - pub fn scan_folder>(&mut self, target_path: &P) -> Result> { + pub fn scan_folder>( + &mut self, + target_path: &P, + ) -> Result> { let mut total = 0; let mut errors = 0; for target_file in WalkDir::new(target_path) @@ -878,15 +877,21 @@ impl MusicLibrary { pub fn remove_missing(&mut self) { let target_removals = Arc::new(Mutex::new(Vec::new())); - self.library.par_iter().for_each(|t|{ + self.library.par_iter().for_each(|t| { for location in &t.location { if !location.exists().unwrap() { - Arc::clone(&target_removals).lock().unwrap().push(location.clone()); + Arc::clone(&target_removals) + .lock() + .unwrap() + .push(location.clone()); } } }); - let target_removals = Arc::try_unwrap(target_removals).unwrap().into_inner().unwrap(); + let target_removals = Arc::try_unwrap(target_removals) + .unwrap() + .into_inner() + .unwrap(); for location in target_removals { self.remove_uri(&location).unwrap(); } @@ -1119,16 +1124,19 @@ impl MusicLibrary { .unwrap_or(&String::new()) .parse::() .unwrap_or_default(), - song.uuid + song.uuid, )), None => { - album.discs.insert(disc_num, vec![( - song.get_tag(&Tag::Track) - .unwrap_or(&String::new()) - .parse::() - .unwrap_or_default(), - song.uuid - )]); + album.discs.insert( + disc_num, + vec![( + song.get_tag(&Tag::Track) + .unwrap_or(&String::new()) + .parse::() + .unwrap_or_default(), + song.uuid, + )], + ); } }, // If the album is not in the list, make it new one and add it @@ -1144,13 +1152,13 @@ impl MusicLibrary { .unwrap_or(&String::new()) .parse::() .unwrap_or_default(), - song.uuid - )])]), + song.uuid, + )], + )]), cover: album_art.cloned(), }; albums.insert(album_title, new_album); } - } paths.insert(song.uuid, song.primary_uri().unwrap()); } @@ -1162,19 +1170,18 @@ impl MusicLibrary { let num_a = a.0; let num_b = b.0; - if (num_a, num_b) != (0,0) - { + if (num_a, num_b) != (0, 0) { // If parsing the track numbers succeeds, compare as numbers num_a.cmp(&num_b) } else { // If parsing doesn't succeed, compare the locations let a = match paths.get_key_value(&a.1) { Some((_, (uri, _))) => uri, - None => return Ordering::Equal + None => return Ordering::Equal, }; let b = match paths.get_key_value(&b.1) { Some((_, (uri, _))) => uri, - None => return Ordering::Equal + None => return Ordering::Equal, }; a.as_uri().cmp(&b.as_uri()) @@ -1217,13 +1224,20 @@ mod test { sync::{Arc, RwLock}, }; - use crate::{config::{tests::new_config_lib, Config}, music_storage::library::MusicLibrary}; + use crate::{ + config::{tests::new_config_lib, Config}, + music_storage::library::MusicLibrary, + }; #[test] fn library_init() { 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(config.libraries.get_default().unwrap().path.clone(), target_uuid).unwrap(); + let a = MusicLibrary::init( + config.libraries.get_default().unwrap().path.clone(), + target_uuid, + ) + .unwrap(); dbg!(a); } }