mirror of
https://github.com/CDrummond/bliss-analyser.git
synced 2025-04-12 23:17:14 +03:00
Add support for multiple music folders
This commit is contained in:
parent
cf729aaef3
commit
a6284c3761
@ -1,6 +1,8 @@
|
||||
0.1.0
|
||||
-----
|
||||
1. Add support for analysing CUE files.
|
||||
2. Support up to 5 music folders (music, music_1, music_2, music_3, and
|
||||
music_4).
|
||||
|
||||
0.0.2
|
||||
-----
|
||||
|
@ -102,7 +102,8 @@ ignore=ignore.txt
|
||||
|
||||
The following items are supported:
|
||||
* `music` specifies the location of your music collection - e.g. `c:\Users\user\Music`
|
||||
for windows. This default to `Music` within the user's home folder.
|
||||
for windows. This default to `Music` within the user's home folder. Up to 4 other
|
||||
music folders may be specified via `music_1`, `music_2`, `music_3`, and `music_4`
|
||||
* `db` specifies the name and location of the database file used to store the
|
||||
analysis results. This will default to `bliss.db` in the current folder.
|
||||
* `lms` specifies the hostname, or IP address, of your LMS server. This is used
|
||||
|
@ -287,54 +287,61 @@ pub fn analyse_new_cue_tracks(db:&db::Db, mpath: &PathBuf, cue_tracks:Vec<cue::C
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn analyse_files(db_path: &str, mpath: &PathBuf, dry_run:bool, keep_old:bool, max_num_tracks:usize) {
|
||||
let mut track_paths:Vec<String> = Vec::new();
|
||||
let mut cue_tracks:Vec<cue::CueTrack> = Vec::new();
|
||||
pub fn analyse_files(db_path: &str, mpaths: &Vec<PathBuf>, dry_run:bool, keep_old:bool, max_num_tracks:usize) {
|
||||
let mut db = db::Db::new(&String::from(db_path));
|
||||
let cur = PathBuf::from(mpath);
|
||||
|
||||
db.init();
|
||||
log::info!("Looking for new tracks");
|
||||
get_file_list(&mut db, mpath, &cur, &mut track_paths, &mut cue_tracks);
|
||||
log::info!("Num new tracks: {}", track_paths.len());
|
||||
if !cue_tracks.is_empty() {
|
||||
log::info!("Num new cue tracks: {}", cue_tracks.len());
|
||||
}
|
||||
if !dry_run && max_num_tracks>0 && track_paths.len()>max_num_tracks {
|
||||
log::info!("Only analysing {} tracks", max_num_tracks);
|
||||
track_paths.truncate(max_num_tracks);
|
||||
}
|
||||
if !dry_run && max_num_tracks>0 && cue_tracks.len()>max_num_tracks {
|
||||
log::info!("Only analysing {} cue tracks", max_num_tracks);
|
||||
cue_tracks.truncate(max_num_tracks);
|
||||
}
|
||||
if !keep_old {
|
||||
db.remove_old(mpath, dry_run);
|
||||
}
|
||||
if !dry_run {
|
||||
if track_paths.len()>0 {
|
||||
match analyse_new_files(&db, mpath, track_paths) {
|
||||
Ok(_) => { },
|
||||
Err(e) => { log::error!("Analysis returned error: {}", e); }
|
||||
}
|
||||
} else {
|
||||
log::info!("No new tracks to analyse");
|
||||
}
|
||||
|
||||
for path in mpaths {
|
||||
let mpath = path.clone();
|
||||
let cur = path.clone();
|
||||
let mut track_paths:Vec<String> = Vec::new();
|
||||
let mut cue_tracks:Vec<cue::CueTrack> = Vec::new();
|
||||
|
||||
log::info!("Looking for new tracks in {}", mpath.to_string_lossy());
|
||||
get_file_list(&mut db, &mpath, &cur, &mut track_paths, &mut cue_tracks);
|
||||
log::info!("Num new tracks: {}", track_paths.len());
|
||||
if !cue_tracks.is_empty() {
|
||||
match analyse_new_cue_tracks(&db, mpath, cue_tracks) {
|
||||
Ok(_) => { },
|
||||
Err(e) => { log::error!("Cue analysis returned error: {}", e); }
|
||||
log::info!("Num new cue tracks: {}", cue_tracks.len());
|
||||
}
|
||||
if !dry_run && max_num_tracks>0 && track_paths.len()>max_num_tracks {
|
||||
log::info!("Only analysing {} tracks", max_num_tracks);
|
||||
track_paths.truncate(max_num_tracks);
|
||||
}
|
||||
if !dry_run && max_num_tracks>0 && cue_tracks.len()>max_num_tracks {
|
||||
log::info!("Only analysing {} cue tracks", max_num_tracks);
|
||||
cue_tracks.truncate(max_num_tracks);
|
||||
}
|
||||
|
||||
if !dry_run {
|
||||
if track_paths.len()>0 {
|
||||
match analyse_new_files(&db, &mpath, track_paths) {
|
||||
Ok(_) => { },
|
||||
Err(e) => { log::error!("Analysis returned error: {}", e); }
|
||||
}
|
||||
} else {
|
||||
log::info!("No new tracks to analyse");
|
||||
}
|
||||
if !cue_tracks.is_empty() {
|
||||
match analyse_new_cue_tracks(&db, &mpath, cue_tracks) {
|
||||
Ok(_) => { },
|
||||
Err(e) => { log::error!("Cue analysis returned error: {}", e); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !keep_old {
|
||||
db.remove_old(mpaths, dry_run);
|
||||
}
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
pub fn read_tags(db_path: &str, mpath: &PathBuf) {
|
||||
pub fn read_tags(db_path: &str, mpaths: &Vec<PathBuf>) {
|
||||
let db = db::Db::new(&String::from(db_path));
|
||||
db.init();
|
||||
db.update_tags(&mpath);
|
||||
db.update_tags(&mpaths);
|
||||
db.close();
|
||||
}
|
||||
|
||||
|
47
src/db.rs
47
src/db.rs
@ -10,7 +10,7 @@ use bliss_audio::{Analysis, AnalysisIndex};
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use rusqlite::{Connection, params};
|
||||
use std::convert::TryInto;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
use crate::cue;
|
||||
use crate::tags;
|
||||
@ -150,7 +150,7 @@ impl Db {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_old(&self, mpath:&Path, dry_run:bool) {
|
||||
pub fn remove_old(&self, mpaths: &Vec<PathBuf>, dry_run:bool) {
|
||||
log::info!("Looking for non-existant tracks");
|
||||
let mut stmt = self.conn.prepare("SELECT File FROM Tracks;").unwrap();
|
||||
let track_iter = stmt.query_map([], |row| {
|
||||
@ -169,13 +169,23 @@ impl Db {
|
||||
if cfg!(windows) {
|
||||
db_path = db_path.replace("/", "\\");
|
||||
}
|
||||
let path = mpath.join(PathBuf::from(db_path.clone()));
|
||||
//log::debug!("Check if '{}' exists.", path.to_string_lossy());
|
||||
let mut exists = false;
|
||||
|
||||
if !path.exists() {
|
||||
for mpath in mpaths {
|
||||
let path = mpath.join(PathBuf::from(db_path.clone()));
|
||||
//log::debug!("Check if '{}' exists.", path.to_string_lossy());
|
||||
|
||||
if path.exists() {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
to_remove.push(orig_path);
|
||||
}
|
||||
}
|
||||
|
||||
let num_to_remove = to_remove.len();
|
||||
log::info!("Num non-existant tracks: {}", num_to_remove);
|
||||
if !dry_run && num_to_remove>0 {
|
||||
@ -207,7 +217,7 @@ impl Db {
|
||||
count
|
||||
}
|
||||
|
||||
pub fn update_tags(&self, mpath:&PathBuf) {
|
||||
pub fn update_tags(&self, mpaths: &Vec<PathBuf>) {
|
||||
let total = self.get_track_count();
|
||||
if total>0 {
|
||||
let pb = ProgressBar::new(total.try_into().unwrap());
|
||||
@ -242,15 +252,22 @@ impl Db {
|
||||
duration:dbtags.duration
|
||||
};
|
||||
pb.set_message(format!("{}", dbtags.file));
|
||||
let path = String::from(mpath.join(&dbtags.file).to_string_lossy());
|
||||
let ftags = tags::read(&path);
|
||||
if ftags.title.is_empty() && ftags.artist.is_empty() && ftags.album_artist.is_empty() && ftags.album.is_empty() && ftags.genre.is_empty() {
|
||||
log::error!("Failed to read tags of '{}'", dbtags.file);
|
||||
} else if ftags.duration!=dtags.duration || ftags.title!=dtags.title || ftags.artist!=dtags.artist || ftags.album_artist!=dtags.album_artist || ftags.album!=dtags.album || ftags.genre!=dtags.genre {
|
||||
match self.conn.execute("UPDATE Tracks SET Title=?, Artist=?, AlbumArtist=?, Album=?, Genre=?, Duration=? WHERE rowid=?;",
|
||||
params![ftags.title, ftags.artist, ftags.album_artist, ftags.album, ftags.genre, ftags.duration, dbtags.rowid]) {
|
||||
Ok(_) => { updated += 1; },
|
||||
Err(e) => { log::error!("Failed to update tags of '{}'. {}", dbtags.file, e); }
|
||||
|
||||
for mpath in mpaths {
|
||||
let track_path = mpath.join(&dbtags.file);
|
||||
if track_path.exists() {
|
||||
let path = String::from(track_path.to_string_lossy());
|
||||
let ftags = tags::read(&path);
|
||||
if ftags.title.is_empty() && ftags.artist.is_empty() && ftags.album_artist.is_empty() && ftags.album.is_empty() && ftags.genre.is_empty() {
|
||||
log::error!("Failed to read tags of '{}'", dbtags.file);
|
||||
} else if ftags.duration!=dtags.duration || ftags.title!=dtags.title || ftags.artist!=dtags.artist || ftags.album_artist!=dtags.album_artist || ftags.album!=dtags.album || ftags.genre!=dtags.genre {
|
||||
match self.conn.execute("UPDATE Tracks SET Title=?, Artist=?, AlbumArtist=?, Album=?, Genre=?, Duration=? WHERE rowid=?;",
|
||||
params![ftags.title, ftags.artist, ftags.album_artist, ftags.album, ftags.genre, ftags.duration, dbtags.rowid]) {
|
||||
Ok(_) => { updated += 1; },
|
||||
Err(e) => { log::error!("Failed to update tags of '{}'. {}", dbtags.file, e); }
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
41
src/main.rs
41
src/main.rs
@ -34,6 +34,7 @@ fn main() {
|
||||
let mut task = "".to_string();
|
||||
let mut lms_host = "127.0.0.1".to_string();
|
||||
let mut max_num_tracks:usize = 0;
|
||||
let mut music_paths:Vec<PathBuf> = Vec::new();
|
||||
|
||||
match dirs::home_dir() {
|
||||
Some(path) => { music_path = String::from(path.join("Music").to_string_lossy()); }
|
||||
@ -90,20 +91,23 @@ fn main() {
|
||||
let mut config = Ini::new();
|
||||
match config.load(&config_file) {
|
||||
Ok(_) => {
|
||||
match config.get(TOP_LEVEL_INI_TAG, "music") {
|
||||
Some(val) => { music_path = val; },
|
||||
None => { }
|
||||
let path_keys: [&str; 5] = ["music", "music_1", "music_2", "music_3", "music_4"];
|
||||
for key in &path_keys {
|
||||
match config.get(TOP_LEVEL_INI_TAG, key) {
|
||||
Some(val) => { music_paths.push(PathBuf::from(&val)); },
|
||||
None => { }
|
||||
}
|
||||
}
|
||||
match config.get(TOP_LEVEL_INI_TAG, "db") {
|
||||
Some(val) => { db_path = val; },
|
||||
Some(val) => { db_path = val; },
|
||||
None => { }
|
||||
}
|
||||
match config.get(TOP_LEVEL_INI_TAG, "lms") {
|
||||
Some(val) => { lms_host = val; },
|
||||
Some(val) => { lms_host = val; },
|
||||
None => { }
|
||||
}
|
||||
match config.get(TOP_LEVEL_INI_TAG, "ignore") {
|
||||
Some(val) => { ignore_file = val; },
|
||||
Some(val) => { ignore_file = val; },
|
||||
None => { }
|
||||
}
|
||||
},
|
||||
@ -115,6 +119,10 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
if music_paths.is_empty() {
|
||||
music_paths.push(PathBuf::from(&music_path));
|
||||
}
|
||||
|
||||
if task.eq_ignore_ascii_case("stopmixer") {
|
||||
upload::stop_mixer(&lms_host);
|
||||
} else {
|
||||
@ -137,18 +145,19 @@ fn main() {
|
||||
process::exit(-1);
|
||||
}
|
||||
} else {
|
||||
let mpath = PathBuf::from(&music_path);
|
||||
if !mpath.exists() {
|
||||
log::error!("Music path ({}) does not exist", music_path);
|
||||
process::exit(-1);
|
||||
}
|
||||
if !mpath.is_dir() {
|
||||
log::error!("Music path ({}) is not a directory", music_path);
|
||||
process::exit(-1);
|
||||
for mpath in &music_paths {
|
||||
if !mpath.exists() {
|
||||
log::error!("Music path ({}) does not exist", mpath.to_string_lossy());
|
||||
process::exit(-1);
|
||||
}
|
||||
if !mpath.is_dir() {
|
||||
log::error!("Music path ({}) is not a directory", mpath.to_string_lossy());
|
||||
process::exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if task.eq_ignore_ascii_case("tags") {
|
||||
analyse::read_tags(&db_path, &mpath);
|
||||
analyse::read_tags(&db_path, &music_paths);
|
||||
} else if task.eq_ignore_ascii_case("ignore") {
|
||||
let ignore_path = PathBuf::from(&ignore_file);
|
||||
if !ignore_path.exists() {
|
||||
@ -161,7 +170,7 @@ fn main() {
|
||||
}
|
||||
analyse::update_ignore(&db_path, &ignore_path);
|
||||
} else {
|
||||
analyse::analyse_files(&db_path, &mpath, dry_run, keep_old, max_num_tracks);
|
||||
analyse::analyse_files(&db_path, &music_paths, dry_run, keep_old, max_num_tracks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user