diff --git a/Cargo.toml b/Cargo.toml index aaf4409..70c70e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,7 @@ categories = [] [dependencies] file-format = { version = "0.23.0", features = ["reader-asf", "reader-ebml", "reader-mp4", "reader-rm", "reader-txt", "reader-xml", "serde"] } lofty = "0.18.0" -serde = { version = "1.0.191", features = ["derive"] } -toml = "0.8.8" +serde = { version = "1.0.195", features = ["derive"] } walkdir = "2.4.0" chrono = { version = "0.4.31", features = ["serde"] } bincode = { version = "2.0.0-rc.3", features = ["serde"] } @@ -23,10 +22,10 @@ unidecode = "0.3.0" rayon = "1.8.0" log = "0.4" base64 = "0.21.5" -snap = "1.1.0" +snap = "1" rcue = "0.1.3" -gstreamer = "0.21.2" -glib = "0.18.3" +gstreamer = "0.21.3" +glib = "0.18.5" crossbeam-channel = "0.5.8" crossbeam = "0.8.2" quick-xml = "0.31.0" diff --git a/src/config/config.rs b/src/config/config.rs index 7c3ffa8..206287a 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -16,10 +16,25 @@ pub struct ConfigLibrary { pub uuid: Uuid } -impl ConfigLibrary { - pub fn new() -> Self { - ConfigLibrary::default() +impl Default for ConfigLibrary { + fn default() -> Self { + ConfigLibrary { + name: String::new(), + path: PathBuf::from("library"), + uuid: Uuid::new_v4(), + } } +} + +impl ConfigLibrary { + pub fn new(path: PathBuf, name: String) -> Self { + ConfigLibrary { + name, + path, + uuid: Uuid::new_v4(), + } + } + pub fn open(&self) -> Result { match File::open(self.path.as_path()) { Ok(ok) => Ok(ok), @@ -28,19 +43,9 @@ impl ConfigLibrary { } } -impl Default for ConfigLibrary { - fn default() -> Self { - ConfigLibrary { - name: String::default(), - path: PathBuf::default(), - uuid: Uuid::new_v4() - } - } -} - #[derive(Debug, Default, Serialize, Deserialize)] pub struct ConfigLibraries { - default_library: Uuid, + pub default_library: Uuid, pub library_folder: PathBuf, pub libraries: Vec, } @@ -83,7 +88,7 @@ impl ConfigLibraries { pub struct Config { pub path: PathBuf, pub libraries: ConfigLibraries, - volume: f32, + pub volume: f32, } #[test] @@ -92,9 +97,9 @@ fn config_test() { path: PathBuf::from("config_test.json"), libraries: ConfigLibraries { libraries: vec![ - ConfigLibrary::default(), - ConfigLibrary::default(), - ConfigLibrary::default() + ConfigLibrary::new(PathBuf::from("library1"), String::from("library1")), + ConfigLibrary::new(PathBuf::from("library2"), String::from("library2")), + ConfigLibrary::new(PathBuf::from("library3"), String::from("library3")) ], ..Default::default() }, diff --git a/src/music_storage/library.rs b/src/music_storage/library.rs index 4e4711b..444532f 100644 --- a/src/music_storage/library.rs +++ b/src/music_storage/library.rs @@ -323,13 +323,15 @@ pub struct MusicLibrary { #[test] fn library_init() { - let uuidv4 = Uuid::new_v4(); - let a = MusicLibrary::init(Arc::new(RwLock::from(Config::new())), uuidv4).unwrap(); + let config = Config::read_file(PathBuf::from("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); } impl MusicLibrary { - pub fn new(name: String, uuid: Uuid) -> Self { + /// Create a new library from a name and [Uuid] + fn new(name: String, uuid: Uuid) -> Self { MusicLibrary { name, uuid, @@ -337,18 +339,6 @@ impl MusicLibrary { } } - /* - pub fn with_uuid(uuid: Uuid, path: PathBuf) -> Result> { - MusicLibrary { - name: String::new(), - uuid, - library: Vec::new(), - }; - - todo!() - } - */ - /// Initialize the database /// /// If the database file already exists, return the [MusicLibrary], otherwise create @@ -357,7 +347,7 @@ impl MusicLibrary { pub fn init(config: Arc>, uuid: Uuid) -> Result> { let global_config = &*config.read().unwrap(); - let library: MusicLibrary = match global_config.libraries.uuid_exists(&uuid) { + let library: MusicLibrary = match global_config.libraries.get_library(&uuid)?.path.exists() { true => read_file(global_config.libraries.get_library(&uuid)?.path)?, false => { // If the library does not exist, re-create it @@ -370,13 +360,24 @@ impl MusicLibrary { Ok(library) } + //#[cfg(debug_assertions)] // We probably wouldn't want to use this for real, but maybe it would have some utility? + pub fn from_path(path: PathBuf) -> Result> { + let library: MusicLibrary = match path.exists() { + true => read_file(path)?, + false => { + let lib = MusicLibrary::new(String::new(), Uuid::new_v4()); + write_file(&lib, path)?; + lib + } + }; + Ok(library) + } + /// Serializes the database out to the file specified in the config pub fn save(&self, config: &Config) -> Result<(), Box> { let path = config.libraries.get_library(&self.uuid)?.path; match path.try_exists() { - Ok(_) => { - write_file(&self.library, path)?; - } + Ok(_) => write_file(self, path)?, Err(error) => return Err(error.into()), } @@ -384,12 +385,12 @@ impl MusicLibrary { } /// Returns the library size in number of tracks - pub fn size(&self) -> usize { + pub fn len_tracks(&self) -> usize { self.library.len() } /// Queries for a [Song] by its [URI], returning a single `Song` - /// with the `URI` that matches + /// with the `URI` that matches along with its position in the library pub fn query_uri(&self, path: &URI) -> Option<(&Song, usize)> { let result = self .library @@ -408,7 +409,7 @@ impl MusicLibrary { } } - /// Queries for a [Song] by its [PathBuf], returning a `Vec` + /// Queries for a [Song] by its [PathBuf], returning a `Vec<&Song>` /// with matching `PathBuf`s fn query_path(&self, path: PathBuf) -> Option> { let result: Arc>> = Arc::new(Mutex::new(Vec::new())); @@ -428,7 +429,6 @@ impl MusicLibrary { pub fn scan_folder( &mut self, target_path: &str, - config: &Config, ) -> Result> { let mut total = 0; let mut errors = 0; @@ -488,9 +488,6 @@ impl MusicLibrary { } } - // Save the database after scanning finishes - self.save(config).unwrap(); - println!("ERRORS: {}", errors); Ok(total) diff --git a/src/music_storage/utils.rs b/src/music_storage/utils.rs index 945aa4c..a14dbe1 100644 --- a/src/music_storage/utils.rs +++ b/src/music_storage/utils.rs @@ -1,15 +1,17 @@ -use file_format::{FileFormat, Kind}; +use std::fs::{File, self}; use std::io::{BufReader, BufWriter}; use std::path::{Path, PathBuf}; -use std::{error::Error, fs}; +use std::error::Error; + use walkdir::WalkDir; - +use file_format::{FileFormat, Kind}; use snap; - -use super::library::{AlbumArt, URI}; use unidecode::unidecode; +use super::library::{AlbumArt, URI}; + pub(super) fn normalize(input_string: &str) -> String { + // Normalize the string to latin characters... this needs a lot of work let mut normalized = unidecode(input_string); // Remove non alphanumeric characters @@ -19,25 +21,10 @@ pub(super) fn normalize(input_string: &str) -> String { normalized } -pub(super) fn read_file serde::Deserialize<'de>>(path: PathBuf) -> Result> { - // Create a new snap reader over the database file - let database = fs::File::open(path)?; - let reader = BufReader::new(database); - let mut d = snap::read::FrameDecoder::new(reader); - - // Decode the library from the serialized data into the vec - let library: T = bincode::serde::decode_from_std_read( - &mut d, - bincode::config::standard() - .with_little_endian() - .with_variable_int_encoding(), - )?; - - Ok(library) -} - +/// Write any data structure which implements [serde::Serialize] +/// out to a [bincode] encoded file compressed using [snap] pub(super) fn write_file( - library: &T, + library: T, path: PathBuf, ) -> Result<(), Box> { // Create a temporary name for writing out @@ -45,7 +32,7 @@ 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(fs::File::create(&writer_name)?); + let writer = BufWriter::new(File::create(&writer_name)?); let mut e = snap::write::FrameEncoder::new(writer); // Write out the data @@ -61,6 +48,24 @@ pub(super) fn write_file( Ok(()) } +/// Read a file serialized out with [write_file] and turn it into +/// the desired structure +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); + + // Decode the library from the serialized data into the vec + let library: T = bincode::serde::decode_from_std_read( + &mut d, + bincode::config::standard() + .with_little_endian() + .with_variable_int_encoding(), + )?; + + Ok(library) +} + pub fn find_images(song_path: &Path) -> Result, Box> { let mut images: Vec = Vec::new(); @@ -85,7 +90,7 @@ pub fn find_images(song_path: &Path) -> Result, Box> { break; } - let image_uri = URI::Local(path.to_path_buf().canonicalize().unwrap()); + let image_uri = URI::Local(path.to_path_buf().canonicalize()?); images.push(AlbumArt::External(image_uri)); }