Add cargo features flag to enable libav usage.

This commit is contained in:
CDrummond 2025-03-03 17:06:09 +00:00
parent e5ef67f8c6
commit 136651ada7
6 changed files with 274 additions and 3 deletions

109
Cargo.lock generated
View File

@ -100,6 +100,24 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "bindgen"
version = "0.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
dependencies = [
"bitflags 2.9.0",
"cexpr",
"clang-sys",
"itertools",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 2.0.32",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@ -157,6 +175,8 @@ dependencies = [
"adler32",
"bliss-audio-aubio-rs",
"extended-isolation-forest",
"ffmpeg-next",
"ffmpeg-sys-next",
"log",
"ndarray",
"ndarray-stats",
@ -208,6 +228,15 @@ dependencies = [
"libc",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -233,6 +262,17 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "configparser"
version = "3.0.0"
@ -398,6 +438,31 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "ffmpeg-next"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da02698288e0275e442a47fc12ca26d50daf0d48b15398ba5906f20ac2e2a9f9"
dependencies = [
"bitflags 2.9.0",
"ffmpeg-sys-next",
"libc",
]
[[package]]
name = "ffmpeg-sys-next"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bc3234d0a4b2f7d083699d0860c6c9dd83713908771b60f94a96f8704adfe45"
dependencies = [
"bindgen",
"cc",
"libc",
"num_cpus",
"pkg-config",
"vcpkg",
]
[[package]]
name = "flate2"
version = "1.0.34"
@ -440,6 +505,12 @@ dependencies = [
"wasi 0.10.0+wasi-snapshot-preview1",
]
[[package]]
name = "glob"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "hashbrown"
version = "0.11.2"
@ -586,6 +657,16 @@ version = "0.2.170"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
[[package]]
name = "libloading"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets",
]
[[package]]
name = "libm"
version = "0.2.11"
@ -672,6 +753,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.0"
@ -719,6 +806,16 @@ dependencies = [
"num-traits",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-complex"
version = "0.4.0"
@ -988,6 +1085,12 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.2.3"
@ -1126,6 +1229,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "smallvec"
version = "1.8.0"

View File

@ -29,6 +29,9 @@ which = "7.0.2"
rcue = "0.1.3"
hhmmss = "0.1.0"
[features]
libav = ["bliss-audio/ffmpeg"]
[dependencies.bliss-audio]
default-features = false
features = ["aubio-static"]

View File

@ -2,7 +2,9 @@
-----
1. Add support for (DSD) WavPack - thanks to Bart Lauret
2. Update version of bliss library.
3. Use 'ffmpeg' commandline to decode files, and not ffmpeg libraries.
3. Use 'ffmpeg' commandline to decode files, and not ffmpeg libraries by
defualt. Pass "--features=libav" to cargo to build against ffmpeg
libraries.
0.2.3
-----

View File

@ -8,23 +8,34 @@
use crate::cue;
use crate::db;
#[cfg(not(feature = "libav"))]
use crate::ffmpeg;
use crate::tags;
use anyhow::Result;
use bliss_audio::{decoder::Decoder, BlissResult, Song};
#[cfg(not(feature = "libav"))]
use hhmmss::Hhmmss;
use if_chain::if_chain;
use indicatif::{ProgressBar, ProgressStyle};
#[cfg(feature = "libav")]
use std::collections::HashSet;
use std::convert::TryInto;
use std::fs::{DirEntry, File};
use std::io::{BufRead, BufReader};
use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};
#[cfg(not(feature = "libav"))]
use std::sync::mpsc;
#[cfg(not(feature = "libav"))]
use std::sync::mpsc::{Receiver, Sender};
#[cfg(not(feature = "libav"))]
use std::thread;
#[cfg(not(feature = "libav"))]
use std::time::Duration;
use num_cpus;
#[cfg(feature = "libav")]
use bliss_audio::{decoder::Decoder, decoder::ffmpeg::FFmpeg};
#[cfg(not(feature = "libav"))]
use bliss_audio::{decoder::Decoder, BlissResult, Song};
const DONT_ANALYSE: &str = ".notmusic";
const MAX_ERRORS_TO_SHOW: usize = 100;
@ -74,13 +85,22 @@ fn check_dir_entry(db: &mut db::Db, mpath: &Path, entry: DirEntry, track_paths:
if let Ok(cue_track_stripped) = cue_track_path.strip_prefix(mpath) {
let cue_track_sname = String::from(cue_track_stripped.to_string_lossy());
if let Ok(id) = db.get_rowid(&cue_track_sname) {
#[cfg(feature = "libav")]
if id<=0 {
track_paths.push(String::from(cue_file.to_string_lossy()));
*file_count+=1;
}
#[cfg(not(feature = "libav"))]
if id<=0 {
let this_cue_tracks = cue::parse(&pb, &cue_file);
for track in this_cue_tracks {
cue_tracks.push(track.clone());
*file_count+=1;
}
*file_count+=1;
}
}
}
} else {
@ -96,6 +116,124 @@ fn check_dir_entry(db: &mut db::Db, mpath: &Path, entry: DirEntry, track_paths:
}
}
#[cfg(feature = "libav")]
pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>, max_threads: usize) -> Result<()> {
let total = track_paths.len();
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
ProgressStyle::default_bar()
.template("[{elapsed_precise}] [{bar:25}] {percent:>3}% {pos:>6}/{len:6} {wide_msg}")
.progress_chars("=> "),
);
let cpu_threads: NonZeroUsize = match max_threads {
0 => NonZeroUsize::new(num_cpus::get()).unwrap(),
_ => NonZeroUsize::new(max_threads).unwrap(),
};
let mut analysed = 0;
let mut failed: Vec<String> = Vec::new();
let mut tag_error: Vec<String> = Vec::new();
let mut reported_cue:HashSet<String> = HashSet::new();
log::info!("Analysing new files");
for (path, result) in <FFmpeg as Decoder>::analyze_paths_with_cores(track_paths, cpu_threads) {
let stripped = path.strip_prefix(mpath).unwrap();
let spbuff = stripped.to_path_buf();
let sname = String::from(spbuff.to_string_lossy());
progress.set_message(format!("{}", sname));
let mut inc_progress = true; // Only want to increment progress once for cue tracks
match result {
Ok(track) => {
let cpath = String::from(path.to_string_lossy());
match track.cue_info {
Some(cue) => {
match track.track_number {
Some(track_num) => {
if reported_cue.contains(&cpath) {
inc_progress = false;
} else {
analysed += 1;
reported_cue.insert(cpath);
}
let meta = db::Metadata {
title: track.title.unwrap_or_default().to_string(),
artist: track.artist.unwrap_or_default().to_string(),
album: track.album.unwrap_or_default().to_string(),
album_artist: track.album_artist.unwrap_or_default().to_string(),
genre: track.genre.unwrap_or_default().to_string(),
duration: track.duration.as_secs() as u32
};
// Remove prefix from audio_file_path
let pbuff = PathBuf::from(&cue.audio_file_path);
let stripped = pbuff.strip_prefix(mpath).unwrap();
let spbuff = stripped.to_path_buf();
let sname = String::from(spbuff.to_string_lossy());
let db_path = format!("{}{}{}", sname, db::CUE_MARKER, track_num);
db.add_track(&db_path, &meta, &track.analysis);
}
None => { failed.push(format!("{} - No track number?", sname)); }
}
}
None => {
// Use lofty to read tags here, and not bliss's, so that if update
// tags is ever used they are from the same source.
let mut meta = tags::read(&cpath);
if meta.is_empty() {
// Lofty failed? Try from bliss...
meta.title = track.title.unwrap_or_default().to_string();
meta.artist = track.artist.unwrap_or_default().to_string();
meta.album = track.album.unwrap_or_default().to_string();
meta.album_artist = track.album_artist.unwrap_or_default().to_string();
meta.genre = track.genre.unwrap_or_default().to_string();
meta.duration = track.duration.as_secs() as u32;
}
if meta.is_empty() {
tag_error.push(sname.clone());
}
db.add_track(&sname, &meta, &track.analysis);
analysed += 1;
}
}
}
Err(e) => { failed.push(format!("{} - {}", sname, e)); }
};
if inc_progress {
progress.inc(1);
}
}
progress.finish_with_message("Finished!");
log::info!("{} Analysed. {} Failure(s).", analysed, failed.len());
if !failed.is_empty() {
let total = failed.len();
failed.truncate(MAX_ERRORS_TO_SHOW);
log::error!("Failed to analyse the following file(s):");
for err in failed {
log::error!(" {}", err);
}
if total > MAX_ERRORS_TO_SHOW {
log::error!(" + {} other(s)", total - MAX_ERRORS_TO_SHOW);
}
}
if !tag_error.is_empty() {
let total = tag_error.len();
tag_error.truncate(MAX_TAG_ERRORS_TO_SHOW);
log::error!("Failed to read tags of the following file(s):");
for err in tag_error {
log::error!(" {}", err);
}
if total > MAX_TAG_ERRORS_TO_SHOW {
log::error!(" + {} other(s)", total - MAX_TAG_ERRORS_TO_SHOW);
}
}
Ok(())
}
#[cfg(not(feature = "libav"))]
pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>, max_threads: usize) -> Result<()> {
let total = track_paths.len();
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
@ -171,6 +309,7 @@ pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>,
Ok(())
}
#[cfg(not(feature = "libav"))]
pub fn analyze_cue_streaming(tracks: Vec<cue::CueTrack>,) -> BlissResult<Receiver<(cue::CueTrack, BlissResult<Song>)>> {
let num_cpus = num_cpus::get();
@ -210,6 +349,7 @@ pub fn analyze_cue_streaming(tracks: Vec<cue::CueTrack>,) -> BlissResult<Receive
Ok(rx)
}
#[cfg(not(feature = "libav"))]
pub fn analyse_new_cue_tracks(db:&db::Db, mpath: &PathBuf, cue_tracks:Vec<cue::CueTrack>) -> Result<()> {
let total = cue_tracks.len();
let pb = ProgressBar::new(total.try_into().unwrap());
@ -313,6 +453,7 @@ pub fn analyse_files(db_path: &str, mpaths: &Vec<PathBuf>, dry_run: bool, keep_o
log::info!("No new files to analyse");
}
#[cfg(not(feature = "libav"))]
if !cue_tracks.is_empty() {
match analyse_new_cue_tracks(&db, &mpath, cue_tracks) {
Ok(_) => { },

View File

@ -8,14 +8,26 @@
extern crate rcue;
#[cfg(not(feature = "libav"))]
use crate::db;
#[cfg(not(feature = "libav"))]
use rcue::parser::parse_from_file;
use std::path::PathBuf;
#[cfg(not(feature = "libav"))]
use std::time::Duration;
#[cfg(not(feature = "libav"))]
pub const LAST_TRACK_DURATION:u64 = 60*60*24;
#[cfg(not(feature = "libav"))]
const GENRE:&str = "GENRE";
#[cfg(feature = "libav")]
#[derive(Clone)]
pub struct CueTrack {
pub track_path:PathBuf
}
#[cfg(not(feature = "libav"))]
#[derive(Clone)]
pub struct CueTrack {
pub audio_path:PathBuf,
@ -29,6 +41,7 @@ pub struct CueTrack {
pub duration:Duration
}
#[cfg(not(feature = "libav"))]
pub fn parse(audio_path:&PathBuf, cue_path:&PathBuf) -> Vec<CueTrack> {
let mut resp:Vec<CueTrack> = Vec::new();

View File

@ -14,10 +14,12 @@ use log::LevelFilter;
use std::io::Write;
use std::path::PathBuf;
use std::process;
#[cfg(not(feature = "libav"))]
use which::which;
mod analyse;
mod cue;
mod db;
#[cfg(not(feature = "libav"))]
mod ffmpeg;
mod tags;
mod upload;
@ -96,6 +98,7 @@ fn main() {
}
// Ensure ffmpeg is in PATH...
#[cfg(not(feature = "libav"))]
match which("ffmpeg") {
Ok(_) => { }
Err(_) => {