From 47483127ed4830053f38757080997b4bbb900640 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Fri, 28 Jun 2024 22:20:13 -0500 Subject: [PATCH] Made albums own their contents --- src/music_controller/controller.rs | 9 +- src/music_storage/library.rs | 140 ++++++++++++++--------------- src/music_storage/utils.rs | 12 +-- 3 files changed, 78 insertions(+), 83 deletions(-) diff --git a/src/music_controller/controller.rs b/src/music_controller/controller.rs index 8c64baa..caf6581 100644 --- a/src/music_controller/controller.rs +++ b/src/music_controller/controller.rs @@ -9,8 +9,7 @@ use kushi::traits::Location; use kushi::{Queue, QueueItemType}; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock}; -use std::thread::{sleep, spawn}; -use std::time::Duration; +use std::thread::spawn; use thiserror::Error; use crossbeam_channel::unbounded; @@ -24,8 +23,8 @@ use crate::{ config::Config, music_storage::library::MusicLibrary, }; -pub struct Controller<'a, P: Player + Send + Sync> { - pub queue: Arc, PlayerLocation>>>, +pub struct Controller { + pub queue: Arc>>, pub config: Arc>, pub library: MusicLibrary, pub player: Arc>, @@ -86,7 +85,7 @@ impl MailMan { } #[allow(unused_variables)] -impl Controller<'static, P> { +impl Controller

{ pub fn start(config_path: T) -> Result > where std::path::PathBuf: std::convert::From, diff --git a/src/music_storage/library.rs b/src/music_storage/library.rs index 022e1de..84867df 100644 --- a/src/music_storage/library.rs +++ b/src/music_storage/library.rs @@ -4,7 +4,7 @@ use super::utils::{find_images, normalize, read_file, write_file}; use crate::config::Config; // Various std things -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::error::Error; use std::ops::ControlFlow::{Break, Continue}; @@ -320,7 +320,6 @@ impl Song { } /// creates a `Vec` from a cue file - pub fn from_cue(cuesheet: &Path) -> Result, Box> { let mut tracks = Vec::new(); @@ -574,43 +573,43 @@ pub enum Service { } #[derive(Clone, Debug, PartialEq)] -pub struct Album<'a> { - title: &'a String, - artist: Option<&'a String>, - cover: Option<&'a AlbumArt>, - discs: BTreeMap>, +pub struct Album { + title: String, + artist: Option, + cover: Option, + discs: BTreeMap>, } #[allow(clippy::len_without_is_empty)] -impl Album<'_> { +impl Album { //returns the Album title pub fn title(&self) -> &String { - self.title + &self.title } /// Returns the album cover as an AlbumArt struct, if it exists - fn cover(&self) -> Option<&AlbumArt> { - self.cover + fn cover(&self) -> &Option { + &self.cover } /// Returns the Album Artist, if they exist - pub fn artist(&self) -> Option<&String> { - self.artist + pub fn artist(&self) -> &Option { + &self.artist } - pub fn discs(&self) -> &BTreeMap> { + pub fn discs(&self) -> &BTreeMap> { &self.discs } /// Returns the specified track at `index` from the album, returning /// an error if the track index is out of range - pub fn track(&self, disc: usize, index: usize) -> Option<&Song> { - Some(self.discs.get(&disc)?[index]) + pub fn track(&self, disc: u16, index: usize) -> Option<&Uuid> { + self.discs.get(&disc)?.get(index) } - fn tracks(&self) -> Vec<&Song> { + fn tracks(&self) -> Vec { let mut songs = Vec::new(); - for disc in &self.discs { - songs.append(&mut disc.1.clone()) + for disc in self.discs.values() { + songs.extend_from_slice(&disc) } songs } @@ -618,15 +617,14 @@ impl Album<'_> { /// Returns the number of songs in the album pub fn len(&self) -> usize { let mut total = 0; - for disc in &self.discs { - total += disc.1.len(); + for disc in self.discs.values() { + total += disc.len(); } total } } -impl TrackGroup for Album<'_> {} - +impl TrackGroup for Album {} #[derive(Debug, Serialize, Deserialize)] pub struct MusicLibrary { @@ -688,6 +686,17 @@ impl MusicLibrary { Ok(library) } + /// Serializes the database out to the file specified in the config + pub fn save_path>(&self, path: &P) -> Result<(), Box> { + let path = path.as_ref(); + match path.try_exists() { + Ok(_) => write_file(self, path)?, + Err(error) => return Err(error.into()), + } + + Ok(()) + } + /// Serializes the database out to the file specified in the config pub fn save(&self, config: Arc>) -> Result<(), Box> { let path = config.read().unwrap().libraries.get_library(&self.uuid)?.path.clone(); @@ -711,6 +720,7 @@ impl MusicLibrary { /// Queries for a [Song] by its [URI], returning a single `Song` /// with the `URI` that matches along with its position in the library + #[inline(always)] pub fn query_uri(&self, path: &URI) -> Option<(&Song, usize)> { let result = self .library @@ -759,9 +769,10 @@ impl MusicLibrary { self.library.par_iter().for_each(|track| { if path == track.primary_uri().unwrap().0.path() { //TODO: make this also not unwrap - result.clone().lock().unwrap().push(track); + Arc::clone(&result).lock().unwrap().push(track); } }); + if result.lock().unwrap().len() > 0 { Some(Arc::try_unwrap(result).unwrap().into_inner().unwrap()) } else { @@ -786,19 +797,11 @@ impl MusicLibrary { continue; } - /* TODO: figure out how to increase the speed of this maybe // Check if the file path is already in the db if self.query_uri(&URI::Local(path.to_path_buf())).is_some() { continue; } - // Save periodically while scanning - i += 1; - if i % 500 == 0 { - self.save(config).unwrap(); - } - */ - let format = FileFormat::from_file(path)?; let extension = match path.extension() { Some(ext) => ext.to_string_lossy().to_ascii_lowercase(), @@ -834,6 +837,22 @@ impl MusicLibrary { Ok(total) } + pub fn remove_missing(&mut self) { + let target_removals = Arc::new(Mutex::new(Vec::new())); + 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()); + } + } + }); + + let target_removals = Arc::try_unwrap(target_removals).unwrap().into_inner().unwrap(); + for location in target_removals { + self.remove_uri(&location).unwrap(); + } + } + pub fn add_file(&mut self, target_file: &Path) -> Result<(), Box> { let new_song = Song::from_file(target_file)?; match self.add_song(new_song) { @@ -1038,65 +1057,42 @@ impl MusicLibrary { /// Generates all albums from the track list pub fn albums(&self) -> BTreeMap { let mut albums: BTreeMap = BTreeMap::new(); - for result in &self.library { - let title = match result.get_tag(&Tag::Album) { - Some(title) => title, + for song in &self.library { + let album_title = match song.get_tag(&Tag::Album) { + Some(title) => title.clone(), None => continue, }; - let norm_title = normalize(title); + //let norm_title = normalize(&album_title); - let disc_num = result + let disc_num = song .get_tag(&Tag::Disk) .unwrap_or(&"".to_string()) - .parse::() + .parse::() .unwrap_or(1); - match albums.get_mut(&norm_title) { - // If the album is in the list, add the track to the appropriate disc in it + match albums.get_mut(&album_title) { + // If the album is in the list, add the track to the appropriate disc within the album Some(album) => match album.discs.get_mut(&disc_num) { - Some(disc) => disc.push(result), + Some(disc) => disc.push(song.uuid), None => { - album.discs.insert(disc_num, vec![result]); + album.discs.insert(disc_num, vec![song.uuid]); } }, - // If the album is not in the list, make a new one and add it + // If the album is not in the list, make it new one and add it None => { - let album_art = result.album_art.first(); + let album_art = song.album_art.first(); let new_album = Album { - title, - artist: result.get_tag(&Tag::AlbumArtist), - discs: BTreeMap::from([(disc_num, vec![result])]), - cover: album_art, + title: album_title.clone(), + artist: song.get_tag(&Tag::AlbumArtist).cloned(), + discs: BTreeMap::from([(disc_num, vec![song.uuid])]), + cover: album_art.cloned(), }; - albums.insert(norm_title, new_album); + albums.insert(album_title, new_album); } } } - // Sort the tracks in each disk in each album - let blank = String::from(""); - albums.par_iter_mut().for_each(|album| { - for disc in &mut album.1.discs { - disc.1.par_sort_by(|a, b| { - let a_track = a.get_tag(&Tag::Track).unwrap_or(&blank); - let b_track = b.get_tag(&Tag::Track).unwrap_or(&blank); - - if let (Ok(num_a), Ok(num_b)) = (a_track.parse::(), b_track.parse::()) - { - // If parsing the track numbers succeeds, compare as numbers - num_a.cmp(&num_b) - } else { - // If parsing doesn't succeed, compare the locations - let path_a = PathBuf::from(a.get_field("location").unwrap().to_string()); - let path_b = PathBuf::from(b.get_field("location").unwrap().to_string()); - - path_a.file_name().cmp(&path_b.file_name()) - } - }); - } - }); - // Return the albums! albums } diff --git a/src/music_storage/utils.rs b/src/music_storage/utils.rs index e29a6e0..b1171da 100644 --- a/src/music_storage/utils.rs +++ b/src/music_storage/utils.rs @@ -37,13 +37,13 @@ pub(super) fn write_file< writer_name.set_extension("tmp"); // Create a new BufWriter on the file and a snap frame encoder - let writer = BufWriter::new(File::create(&writer_name)?); - let mut e = snap::write::FrameEncoder::new(writer); + let mut writer = BufWriter::new(File::create(&writer_name)?); + //let mut e = snap::write::FrameEncoder::new(writer); // Write out the data bincode::serde::encode_into_std_write( library, - &mut e, + &mut writer, bincode::config::standard() .with_little_endian() .with_variable_int_encoding(), @@ -59,12 +59,12 @@ pub(super) fn read_file serde::Deserialize<'de>>( path: PathBuf, ) -> Result> { // Create a new snap reader over the file - let file_reader = BufReader::new(File::open(path)?); - let mut d = snap::read::FrameDecoder::new(file_reader); + let mut file_reader = BufReader::new(File::open(path)?); + //let mut d = snap::read::FrameDecoder::new(file_reader); // Decode the library from the serialized data into the vec let library: T = bincode::serde::decode_from_std_read( - &mut d, + &mut file_reader, bincode::config::standard() .with_little_endian() .with_variable_int_encoding(),