mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-04-19 10:02:53 -05:00
Modified library.rs
to properly save with bincode, added new functions to it as well
This commit is contained in:
parent
097cc1147e
commit
e931c2b528
4 changed files with 80 additions and 74 deletions
|
@ -14,8 +14,7 @@ categories = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
file-format = { version = "0.23.0", features = ["reader-asf", "reader-ebml", "reader-mp4", "reader-rm", "reader-txt", "reader-xml", "serde"] }
|
file-format = { version = "0.23.0", features = ["reader-asf", "reader-ebml", "reader-mp4", "reader-rm", "reader-txt", "reader-xml", "serde"] }
|
||||||
lofty = "0.18.0"
|
lofty = "0.18.0"
|
||||||
serde = { version = "1.0.191", features = ["derive"] }
|
serde = { version = "1.0.195", features = ["derive"] }
|
||||||
toml = "0.8.8"
|
|
||||||
walkdir = "2.4.0"
|
walkdir = "2.4.0"
|
||||||
chrono = { version = "0.4.31", features = ["serde"] }
|
chrono = { version = "0.4.31", features = ["serde"] }
|
||||||
bincode = { version = "2.0.0-rc.3", features = ["serde"] }
|
bincode = { version = "2.0.0-rc.3", features = ["serde"] }
|
||||||
|
@ -23,10 +22,10 @@ unidecode = "0.3.0"
|
||||||
rayon = "1.8.0"
|
rayon = "1.8.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
base64 = "0.21.5"
|
base64 = "0.21.5"
|
||||||
snap = "1.1.0"
|
snap = "1"
|
||||||
rcue = "0.1.3"
|
rcue = "0.1.3"
|
||||||
gstreamer = "0.21.2"
|
gstreamer = "0.21.3"
|
||||||
glib = "0.18.3"
|
glib = "0.18.5"
|
||||||
crossbeam-channel = "0.5.8"
|
crossbeam-channel = "0.5.8"
|
||||||
crossbeam = "0.8.2"
|
crossbeam = "0.8.2"
|
||||||
quick-xml = "0.31.0"
|
quick-xml = "0.31.0"
|
||||||
|
|
|
@ -16,10 +16,25 @@ pub struct ConfigLibrary {
|
||||||
pub uuid: Uuid
|
pub uuid: Uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigLibrary {
|
impl Default for ConfigLibrary {
|
||||||
pub fn new() -> Self {
|
fn default() -> Self {
|
||||||
ConfigLibrary::default()
|
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<File, Error> {
|
pub fn open(&self) -> Result<File, Error> {
|
||||||
match File::open(self.path.as_path()) {
|
match File::open(self.path.as_path()) {
|
||||||
Ok(ok) => Ok(ok),
|
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)]
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct ConfigLibraries {
|
pub struct ConfigLibraries {
|
||||||
default_library: Uuid,
|
pub default_library: Uuid,
|
||||||
pub library_folder: PathBuf,
|
pub library_folder: PathBuf,
|
||||||
pub libraries: Vec<ConfigLibrary>,
|
pub libraries: Vec<ConfigLibrary>,
|
||||||
}
|
}
|
||||||
|
@ -83,7 +88,7 @@ impl ConfigLibraries {
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub libraries: ConfigLibraries,
|
pub libraries: ConfigLibraries,
|
||||||
volume: f32,
|
pub volume: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -92,9 +97,9 @@ fn config_test() {
|
||||||
path: PathBuf::from("config_test.json"),
|
path: PathBuf::from("config_test.json"),
|
||||||
libraries: ConfigLibraries {
|
libraries: ConfigLibraries {
|
||||||
libraries: vec![
|
libraries: vec![
|
||||||
ConfigLibrary::default(),
|
ConfigLibrary::new(PathBuf::from("library1"), String::from("library1")),
|
||||||
ConfigLibrary::default(),
|
ConfigLibrary::new(PathBuf::from("library2"), String::from("library2")),
|
||||||
ConfigLibrary::default()
|
ConfigLibrary::new(PathBuf::from("library3"), String::from("library3"))
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|
|
@ -323,13 +323,15 @@ pub struct MusicLibrary {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn library_init() {
|
fn library_init() {
|
||||||
let uuidv4 = Uuid::new_v4();
|
let config = Config::read_file(PathBuf::from("config_test.json")).unwrap();
|
||||||
let a = MusicLibrary::init(Arc::new(RwLock::from(Config::new())), uuidv4).unwrap();
|
let target_uuid = config.libraries.libraries[0].uuid;
|
||||||
|
let a = MusicLibrary::init(Arc::new(RwLock::from(config)), target_uuid).unwrap();
|
||||||
dbg!(a);
|
dbg!(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MusicLibrary {
|
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 {
|
MusicLibrary {
|
||||||
name,
|
name,
|
||||||
uuid,
|
uuid,
|
||||||
|
@ -337,18 +339,6 @@ impl MusicLibrary {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn with_uuid(uuid: Uuid, path: PathBuf) -> Result<Self, Box<dyn Error>> {
|
|
||||||
MusicLibrary {
|
|
||||||
name: String::new(),
|
|
||||||
uuid,
|
|
||||||
library: Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// Initialize the database
|
/// Initialize the database
|
||||||
///
|
///
|
||||||
/// If the database file already exists, return the [MusicLibrary], otherwise create
|
/// If the database file already exists, return the [MusicLibrary], otherwise create
|
||||||
|
@ -357,7 +347,7 @@ impl MusicLibrary {
|
||||||
pub fn init(config: Arc<RwLock<Config>>, uuid: Uuid) -> Result<Self, Box<dyn Error>> {
|
pub fn init(config: Arc<RwLock<Config>>, uuid: Uuid) -> Result<Self, Box<dyn Error>> {
|
||||||
let global_config = &*config.read().unwrap();
|
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)?,
|
true => read_file(global_config.libraries.get_library(&uuid)?.path)?,
|
||||||
false => {
|
false => {
|
||||||
// If the library does not exist, re-create it
|
// If the library does not exist, re-create it
|
||||||
|
@ -370,13 +360,24 @@ impl MusicLibrary {
|
||||||
Ok(library)
|
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<Self, Box<dyn Error>> {
|
||||||
|
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
|
/// Serializes the database out to the file specified in the config
|
||||||
pub fn save(&self, config: &Config) -> Result<(), Box<dyn Error>> {
|
pub fn save(&self, config: &Config) -> Result<(), Box<dyn Error>> {
|
||||||
let path = config.libraries.get_library(&self.uuid)?.path;
|
let path = config.libraries.get_library(&self.uuid)?.path;
|
||||||
match path.try_exists() {
|
match path.try_exists() {
|
||||||
Ok(_) => {
|
Ok(_) => write_file(self, path)?,
|
||||||
write_file(&self.library, path)?;
|
|
||||||
}
|
|
||||||
Err(error) => return Err(error.into()),
|
Err(error) => return Err(error.into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,12 +385,12 @@ impl MusicLibrary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the library size in number of tracks
|
/// Returns the library size in number of tracks
|
||||||
pub fn size(&self) -> usize {
|
pub fn len_tracks(&self) -> usize {
|
||||||
self.library.len()
|
self.library.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queries for a [Song] by its [URI], returning a single `Song`
|
/// 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)> {
|
pub fn query_uri(&self, path: &URI) -> Option<(&Song, usize)> {
|
||||||
let result = self
|
let result = self
|
||||||
.library
|
.library
|
||||||
|
@ -408,7 +409,7 @@ impl MusicLibrary {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queries for a [Song] by its [PathBuf], returning a `Vec<Song>`
|
/// Queries for a [Song] by its [PathBuf], returning a `Vec<&Song>`
|
||||||
/// with matching `PathBuf`s
|
/// with matching `PathBuf`s
|
||||||
fn query_path(&self, path: PathBuf) -> Option<Vec<&Song>> {
|
fn query_path(&self, path: PathBuf) -> Option<Vec<&Song>> {
|
||||||
let result: Arc<Mutex<Vec<&Song>>> = Arc::new(Mutex::new(Vec::new()));
|
let result: Arc<Mutex<Vec<&Song>>> = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
@ -428,7 +429,6 @@ impl MusicLibrary {
|
||||||
pub fn scan_folder(
|
pub fn scan_folder(
|
||||||
&mut self,
|
&mut self,
|
||||||
target_path: &str,
|
target_path: &str,
|
||||||
config: &Config,
|
|
||||||
) -> Result<i32, Box<dyn std::error::Error>> {
|
) -> Result<i32, Box<dyn std::error::Error>> {
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
let mut errors = 0;
|
let mut errors = 0;
|
||||||
|
@ -488,9 +488,6 @@ impl MusicLibrary {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the database after scanning finishes
|
|
||||||
self.save(config).unwrap();
|
|
||||||
|
|
||||||
println!("ERRORS: {}", errors);
|
println!("ERRORS: {}", errors);
|
||||||
|
|
||||||
Ok(total)
|
Ok(total)
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
use file_format::{FileFormat, Kind};
|
use std::fs::{File, self};
|
||||||
use std::io::{BufReader, BufWriter};
|
use std::io::{BufReader, BufWriter};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{error::Error, fs};
|
use std::error::Error;
|
||||||
|
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
use file_format::{FileFormat, Kind};
|
||||||
use snap;
|
use snap;
|
||||||
|
|
||||||
use super::library::{AlbumArt, URI};
|
|
||||||
use unidecode::unidecode;
|
use unidecode::unidecode;
|
||||||
|
|
||||||
|
use super::library::{AlbumArt, URI};
|
||||||
|
|
||||||
pub(super) fn normalize(input_string: &str) -> String {
|
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);
|
let mut normalized = unidecode(input_string);
|
||||||
|
|
||||||
// Remove non alphanumeric characters
|
// Remove non alphanumeric characters
|
||||||
|
@ -19,25 +21,10 @@ pub(super) fn normalize(input_string: &str) -> String {
|
||||||
normalized
|
normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn read_file<T: for<'de> serde::Deserialize<'de>>(path: PathBuf) -> Result<T, Box<dyn Error>> {
|
/// Write any data structure which implements [serde::Serialize]
|
||||||
// Create a new snap reader over the database file
|
/// out to a [bincode] encoded file compressed using [snap]
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn write_file<T: serde::Serialize>(
|
pub(super) fn write_file<T: serde::Serialize>(
|
||||||
library: &T,
|
library: T,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
// Create a temporary name for writing out
|
// Create a temporary name for writing out
|
||||||
|
@ -45,7 +32,7 @@ pub(super) fn write_file<T: serde::Serialize>(
|
||||||
writer_name.set_extension("tmp");
|
writer_name.set_extension("tmp");
|
||||||
|
|
||||||
// Create a new BufWriter on the file and a snap frame encoder
|
// 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);
|
let mut e = snap::write::FrameEncoder::new(writer);
|
||||||
|
|
||||||
// Write out the data
|
// Write out the data
|
||||||
|
@ -61,6 +48,24 @@ pub(super) fn write_file<T: serde::Serialize>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a file serialized out with [write_file] and turn it into
|
||||||
|
/// the desired structure
|
||||||
|
pub(super) fn read_file<T: for<'de> serde::Deserialize<'de>>(path: PathBuf) -> Result<T, Box<dyn Error>> {
|
||||||
|
// 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<Vec<AlbumArt>, Box<dyn Error>> {
|
pub fn find_images(song_path: &Path) -> Result<Vec<AlbumArt>, Box<dyn Error>> {
|
||||||
let mut images: Vec<AlbumArt> = Vec::new();
|
let mut images: Vec<AlbumArt> = Vec::new();
|
||||||
|
|
||||||
|
@ -85,7 +90,7 @@ pub fn find_images(song_path: &Path) -> Result<Vec<AlbumArt>, Box<dyn Error>> {
|
||||||
break;
|
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));
|
images.push(AlbumArt::External(image_uri));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue