Merge pull request #27 from Polochon-street/example-playlist-3
Polish the playlist example a bit more
This commit is contained in:
commit
a27b91c6fd
5 changed files with 107 additions and 25 deletions
|
@ -1,5 +1,8 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## bliss 0.4.2
|
||||||
|
* Add a binary example to easily make playlists.
|
||||||
|
|
||||||
## bliss 0.4.1
|
## bliss 0.4.1
|
||||||
* Add a function to make album playlists.
|
* Add a function to make album playlists.
|
||||||
|
|
||||||
|
|
54
Cargo.lock
generated
54
Cargo.lock
generated
|
@ -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"
|
||||||
|
@ -77,10 +86,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bliss-audio"
|
name = "bliss-audio"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
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"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bliss-audio"
|
name = "bliss-audio"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
authors = ["Polochon-street <polochonstreet@gmx.fr>"]
|
authors = ["Polochon-street <polochonstreet@gmx.fr>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
@ -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"
|
||||||
|
|
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.
|
[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
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue