Polish the playlist example a bit more

This commit is contained in:
Polochon-street 2021-11-27 13:13:54 +01:00
parent eee2bf612c
commit 80b8541f8f
4 changed files with 102 additions and 23 deletions

52
Cargo.lock generated
View File

@ -26,6 +26,15 @@ dependencies = [
"memchr 2.4.1", "memchr 2.4.1",
] ]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.45" version = "1.0.45"
@ -81,6 +90,7 @@ version = "0.4.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bliss-audio-aubio-rs", "bliss-audio-aubio-rs",
"clap",
"crossbeam", "crossbeam",
"env_logger", "env_logger",
"ffmpeg-next", "ffmpeg-next",
@ -236,6 +246,21 @@ dependencies = [
"libloading", "libloading",
] ]
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.2.1" version = "1.2.1"
@ -1111,6 +1136,12 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3ff2f71c82567c565ba4b3009a9350a96a7269eaa4001ebedae926230bc2254" checksum = "a3ff2f71c82567c565ba4b3009a9350a96a7269eaa4001ebedae926230bc2254"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.21.0" version = "0.21.0"
@ -1149,6 +1180,15 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.29" version = "1.0.29"
@ -1235,6 +1275,12 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.2" version = "0.2.2"
@ -1253,6 +1299,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.3" version = "0.9.3"

View File

@ -50,3 +50,4 @@ mime_guess = "2.0.3"
glob = "0.3.0" glob = "0.3.0"
anyhow = "1.0.45" anyhow = "1.0.45"
serde_json = "1.0.59" serde_json = "1.0.59"
clap = "2.33.3"

View File

@ -23,10 +23,25 @@ different, more accurate values, based on
[actual literature](https://lelele.io/thesis.pdf). It is also faster. [actual literature](https://lelele.io/thesis.pdf). It is also faster.
## Examples ## Examples
For simple analysis / distance computing, a look at `examples/distance.rs` and For simple analysis / distance computing, take a look at `examples/distance.rs` and
`examples/analyse.rs`. `examples/analyse.rs`.
Ready to use examples: If you simply want to try out making playlists from a folder containing songs,
[this example](https://github.com/Polochon-street/bliss-rs/blob/master/examples/playlist.rs)
contains all you need. Usage:
cargo run --features=serde --release --example=playlist /path/to/folder /path/to/first/song
Don't forget the `--release` flag!
By default, it outputs the playlist to stdout, but you can use `-o <path>`
to output it to a specific path.
To avoid having to analyze the entire folder
several times, it also stores the analysis in `/tmp/analysis.json`. You can customize
this behavior by using `-a <path>` to store this file in a specific place.
Ready to use code examples:
### Compute the distance between two songs ### Compute the distance between two songs
``` ```
@ -72,7 +87,7 @@ fn main() -> Result<(), BlissError> {
Instead of reinventing ways to fetch a user library, play songs, etc, Instead of reinventing ways to fetch a user library, play songs, etc,
and embed that into bliss, it is easier to look at the and embed that into bliss, it is easier to look at the
[Library](https://github.com/Polochon-street/bliss-rs/blob/master/src/library.rs#L12) [Library](https://docs.rs/bliss-audio/0.4.1/bliss_audio/library/trait.Library.html)
trait. trait.
By implementing a few functions to get songs from a media library, and store By implementing a few functions to get songs from a media library, and store

View File

@ -1,6 +1,7 @@
use anyhow::Result; use anyhow::Result;
use bliss_audio::distance::{closest_to_first_song, dedup_playlist, euclidean_distance}; use bliss_audio::distance::{closest_to_first_song, dedup_playlist, euclidean_distance};
use bliss_audio::{library::analyze_paths_streaming, Song}; use bliss_audio::{library::analyze_paths_streaming, Song};
use clap::{App, Arg};
use glob::glob; use glob::glob;
use mime_guess; use mime_guess;
use serde_json; use serde_json;
@ -11,28 +12,34 @@ use std::path::{Path, PathBuf};
/* Analyzes a folder recursively, and make a playlist out of the file /* Analyzes a folder recursively, and make a playlist out of the file
* provided by the user. */ * provided by the user. */
// TODO still: // How to use: ./playlist [-o file.m3u] [-a analysis.json] <folder> <file to start the playlist from>
// * Mention it in the README
// * Make the output file configurable
// * Allow to choose between outputing to stdout and a file
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
fn main() -> Result<()> { fn main() -> Result<()> {
let args: Vec<String> = env::args().skip(1).collect(); let matches = App::new("playlist")
if args.len() > 3 || args.len() < 2 { .version(env!("CARGO_PKG_VERSION"))
println!("Usage: ./playlist <folder> <file>"); .author("Polochon_street")
println!( .about("Analyze a folder and make a playlist from a target song")
"Creates a playlist of all audio files in a folder (recursively), \ .arg(Arg::with_name("output-playlist").short("o").long("output-playlist")
starting with <file>, and outputs the result both to stdout and \ .value_name("PLAYLIST.M3U")
a `playlist.m3u` file in the current folder." .help("Outputs the playlist to a file.")
); .takes_value(true))
return Ok(()); .arg(Arg::with_name("analysis-file").short("a").long("analysis-file")
} .value_name("ANALYSIS.JSON")
let folder = &args[0]; .help("Use the songs that have been analyzed in <analysis-file>, and appends newly analyzed songs to it. Defaults to /tmp/analysis.json.")
let file = fs::canonicalize(&args[1])?; .takes_value(true))
.arg(Arg::with_name("FOLDER").help("Folders containing some songs.").required(true))
.arg(Arg::with_name("FIRST-SONG").help("Song to start from (can be outside of FOLDER).").required(true))
.get_matches();
let folder = matches.value_of("FOLDER").unwrap();
let file = fs::canonicalize(matches.value_of("FIRST-SONG").unwrap())?;
let pattern = Path::new(folder).join("**").join("*"); let pattern = Path::new(folder).join("**").join("*");
let mut songs: Vec<Song> = Vec::new(); let mut songs: Vec<Song> = Vec::new();
let analysis_file = fs::File::open("./songs.json"); let analysis_path = matches
.value_of("analysis-file")
.unwrap_or("/tmp/analysis.json");
let analysis_file = fs::File::open(analysis_path);
if let Ok(f) = analysis_file { if let Ok(f) = analysis_file {
let reader = BufReader::new(f); let reader = BufReader::new(f);
songs = serde_json::from_reader(reader)?; songs = serde_json::from_reader(reader)?;
@ -75,14 +82,18 @@ fn main() -> Result<()> {
.collect(); .collect();
closest_to_first_song(&first_song, &mut songs_to_chose_from, euclidean_distance); closest_to_first_song(&first_song, &mut songs_to_chose_from, euclidean_distance);
dedup_playlist(&mut songs_to_chose_from, None); dedup_playlist(&mut songs_to_chose_from, None);
fs::write("./songs.json", serialized)?;
fs::write(analysis_path, serialized)?;
let playlist = songs_to_chose_from let playlist = songs_to_chose_from
.iter() .iter()
.map(|s| s.path.to_string_lossy().to_string()) .map(|s| s.path.to_string_lossy().to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n"); .join("\n");
if let Some(m) = matches.value_of("output-playlist") {
fs::write(m, playlist)?;
} else {
println!("{}", playlist); println!("{}", playlist);
fs::write("./playlist.m3u", playlist)?; }
Ok(()) Ok(())
} }