Polish the playlist example a bit more
This commit is contained in:
parent
eee2bf612c
commit
80b8541f8f
4 changed files with 102 additions and 23 deletions
52
Cargo.lock
generated
52
Cargo.lock
generated
|
@ -26,6 +26,15 @@ dependencies = [
|
|||
"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]]
|
||||
name = "anyhow"
|
||||
version = "1.0.45"
|
||||
|
@ -81,6 +90,7 @@ version = "0.4.1"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"bliss-audio-aubio-rs",
|
||||
"clap",
|
||||
"crossbeam",
|
||||
"env_logger",
|
||||
"ffmpeg-next",
|
||||
|
@ -236,6 +246,21 @@ dependencies = [
|
|||
"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]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.1"
|
||||
|
@ -1111,6 +1136,12 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3ff2f71c82567c565ba4b3009a9350a96a7269eaa4001ebedae926230bc2254"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.21.0"
|
||||
|
@ -1149,6 +1180,15 @@ dependencies = [
|
|||
"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]]
|
||||
name = "thiserror"
|
||||
version = "1.0.29"
|
||||
|
@ -1235,6 +1275,12 @@ version = "1.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
|
@ -1253,6 +1299,12 @@ version = "0.2.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
|
|
|
@ -50,3 +50,4 @@ mime_guess = "2.0.3"
|
|||
glob = "0.3.0"
|
||||
anyhow = "1.0.45"
|
||||
serde_json = "1.0.59"
|
||||
clap = "2.33.3"
|
||||
|
|
21
README.md
21
README.md
|
@ -23,10 +23,25 @@ different, more accurate values, based on
|
|||
[actual literature](https://lelele.io/thesis.pdf). It is also faster.
|
||||
|
||||
## 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`.
|
||||
|
||||
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
|
||||
```
|
||||
|
@ -72,7 +87,7 @@ fn main() -> Result<(), BlissError> {
|
|||
|
||||
Instead of reinventing ways to fetch a user library, play songs, etc,
|
||||
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.
|
||||
|
||||
By implementing a few functions to get songs from a media library, and store
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::Result;
|
||||
use bliss_audio::distance::{closest_to_first_song, dedup_playlist, euclidean_distance};
|
||||
use bliss_audio::{library::analyze_paths_streaming, Song};
|
||||
use clap::{App, Arg};
|
||||
use glob::glob;
|
||||
use mime_guess;
|
||||
use serde_json;
|
||||
|
@ -11,28 +12,34 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
/* Analyzes a folder recursively, and make a playlist out of the file
|
||||
* provided by the user. */
|
||||
// TODO still:
|
||||
// * Mention it in the README
|
||||
// * Make the output file configurable
|
||||
// * Allow to choose between outputing to stdout and a file
|
||||
// How to use: ./playlist [-o file.m3u] [-a analysis.json] <folder> <file to start the playlist from>
|
||||
#[cfg(feature = "serde")]
|
||||
fn main() -> Result<()> {
|
||||
let args: Vec<String> = env::args().skip(1).collect();
|
||||
if args.len() > 3 || args.len() < 2 {
|
||||
println!("Usage: ./playlist <folder> <file>");
|
||||
println!(
|
||||
"Creates a playlist of all audio files in a folder (recursively), \
|
||||
starting with <file>, and outputs the result both to stdout and \
|
||||
a `playlist.m3u` file in the current folder."
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
let folder = &args[0];
|
||||
let file = fs::canonicalize(&args[1])?;
|
||||
let matches = App::new("playlist")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.author("Polochon_street")
|
||||
.about("Analyze a folder and make a playlist from a target song")
|
||||
.arg(Arg::with_name("output-playlist").short("o").long("output-playlist")
|
||||
.value_name("PLAYLIST.M3U")
|
||||
.help("Outputs the playlist to a file.")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("analysis-file").short("a").long("analysis-file")
|
||||
.value_name("ANALYSIS.JSON")
|
||||
.help("Use the songs that have been analyzed in <analysis-file>, and appends newly analyzed songs to it. Defaults to /tmp/analysis.json.")
|
||||
.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 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 {
|
||||
let reader = BufReader::new(f);
|
||||
songs = serde_json::from_reader(reader)?;
|
||||
|
@ -75,14 +82,18 @@ fn main() -> Result<()> {
|
|||
.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)?;
|
||||
|
||||
fs::write(analysis_path, serialized)?;
|
||||
let playlist = songs_to_chose_from
|
||||
.iter()
|
||||
.map(|s| s.path.to_string_lossy().to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
if let Some(m) = matches.value_of("output-playlist") {
|
||||
fs::write(m, playlist)?;
|
||||
} else {
|
||||
println!("{}", playlist);
|
||||
fs::write("./playlist.m3u", playlist)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue