diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a68e2f0..6cbd077 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -33,4 +33,4 @@ jobs: - name: Build benches run: cargo +nightly-2021-04-01 bench --verbose --features=bench --no-run - name: Build examples - run: cargo build --examples --verbose + run: cargo build --examples --verbose --features=serde diff --git a/Cargo.lock b/Cargo.lock index a44442a..d6c1560 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,6 +97,7 @@ dependencies = [ "ripemd160", "rustfft", "serde", + "serde_json", "strum", "strum_macros", "thiserror", @@ -518,6 +519,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + [[package]] name = "jobserver" version = "0.1.24" @@ -1037,6 +1044,12 @@ dependencies = [ "transpose", ] +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + [[package]] name = "scopeguard" version = "1.1.0" @@ -1063,6 +1076,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha-1" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index 2756b43..2024ea4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,3 +49,4 @@ serde = { version = "1.0", optional = true, features = ["derive"] } mime_guess = "2.0.3" glob = "0.3.0" anyhow = "1.0.45" +serde_json = "1.0.59" diff --git a/examples/playlist.rs b/examples/playlist.rs index cc279f1..c8c24b5 100644 --- a/examples/playlist.rs +++ b/examples/playlist.rs @@ -3,16 +3,19 @@ use bliss_audio::distance::{closest_to_first_song, dedup_playlist, euclidean_dis use bliss_audio::{library::analyze_paths_streaming, Song}; use glob::glob; use mime_guess; +use serde_json; use std::env; use std::fs; -use std::path::Path; +use std::io::BufReader; +use std::path::{Path, PathBuf}; /* Analyzes a folder recursively, and make a playlist out of the file * provided by the user. */ // TODO still: -// * Save the results somewhere to avoid analyzing stuff over and over +// * Mention it in the README // * Make the output file configurable // * Allow to choose between outputing to stdout and a file +#[cfg(feature = "serde")] fn main() -> Result<()> { let args: Vec = env::args().skip(1).collect(); if args.len() > 3 || args.len() < 2 { @@ -27,7 +30,20 @@ fn main() -> Result<()> { let folder = &args[0]; let file = fs::canonicalize(&args[1])?; let pattern = Path::new(folder).join("**").join("*"); - let songs = glob(&pattern.to_string_lossy())? + + let mut songs: Vec = Vec::new(); + let analysis_file = fs::File::open("./songs.json"); + if let Ok(f) = analysis_file { + let reader = BufReader::new(f); + songs = serde_json::from_reader(reader)?; + } + + let analyzed_paths = songs + .iter() + .map(|s| s.path.to_owned()) + .collect::>(); + + let paths = glob(&pattern.to_string_lossy())? .map(|e| fs::canonicalize(e.unwrap()).unwrap()) .filter(|e| match mime_guess::from_path(e).first() { Some(m) => m.type_() == "audio", @@ -35,7 +51,14 @@ fn main() -> Result<()> { }) .map(|x| x.to_string_lossy().to_string()) .collect::>(); - let rx = analyze_paths_streaming(songs)?; + + let rx = analyze_paths_streaming( + paths + .iter() + .filter(|p| !analyzed_paths.contains(&PathBuf::from(p))) + .map(|p| p.to_owned()) + .collect(), + )?; let first_song = Song::new(file)?; let mut analyzed_songs = vec![first_song.to_owned()]; for (path, result) in rx.iter() { @@ -44,9 +67,16 @@ fn main() -> Result<()> { Err(e) => println!("error analyzing {}: {}", path, e), }; } - closest_to_first_song(&first_song, &mut analyzed_songs, euclidean_distance); - dedup_playlist(&mut analyzed_songs, None); - let playlist = analyzed_songs + analyzed_songs.extend_from_slice(&songs); + let serialized = serde_json::to_string(&analyzed_songs).unwrap(); + let mut songs_to_chose_from = analyzed_songs + .into_iter() + .filter(|x| x == &first_song || paths.contains(&x.path.to_string_lossy().to_string())) + .collect(); + closest_to_first_song(&first_song, &mut songs_to_chose_from, euclidean_distance); + dedup_playlist(&mut songs_to_chose_from, None); + fs::write("./songs.json", serialized)?; + let playlist = songs_to_chose_from .iter() .map(|s| s.path.to_string_lossy().to_string()) .collect::>() @@ -55,3 +85,8 @@ fn main() -> Result<()> { fs::write("./playlist.m3u", playlist)?; Ok(()) } + +#[cfg(not(feature = "serde"))] +fn main() { + println!("You need the serde feature enabled to run this file."); +} diff --git a/src/lib.rs b/src/lib.rs index bf8ea63..f87f26b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ //! ``` #![cfg_attr(feature = "bench", feature(test))] #![warn(missing_docs)] -#![warn(missing_doc_code_examples)] +#![warn(rustdoc::missing_doc_code_examples)] mod chroma; pub mod distance; pub mod library;