From faedc0302b716c9e9af5b579104752eb4c75b2c7 Mon Sep 17 00:00:00 2001 From: Craig Drummond Date: Wed, 16 Feb 2022 12:17:03 +0000 Subject: [PATCH] Add optio to just update tags. --- src/analyse.rs | 71 ++++++++++++++++---------------------------------- src/db.rs | 41 ++++++++++++++++++++++++++++- src/main.rs | 14 +++++++--- src/tags.rs | 43 ++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 53 deletions(-) create mode 100644 src/tags.rs diff --git a/src/analyse.rs b/src/analyse.rs index 880fc69..184f12d 100644 --- a/src/analyse.rs +++ b/src/analyse.rs @@ -9,14 +9,14 @@ use anyhow::{Result}; use bliss_audio::{library::analyze_paths_streaming}; use indicatif::{ProgressBar, ProgressStyle}; -use lofty::{Accessor, Probe}; use std::convert::TryInto; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use crate::db; +use crate::tags; const DONT_ANALYSE:&str = ".nomusic"; -fn get_file_list(db:&mut db::Db, mpath:& Path, path: &Path, to_add:&mut Vec) { +fn get_file_list(db:&mut db::Db, mpath:&PathBuf, path:&PathBuf, track_paths:&mut Vec) { if path.is_dir() { match path.read_dir() { Ok(items) => { @@ -30,7 +30,7 @@ fn get_file_list(db:&mut db::Db, mpath:& Path, path: &Path, to_add:&mut Vec { if id<=0 { - to_add.push(String::from(pb.to_string_lossy())); + track_paths.push(String::from(pb.to_string_lossy())); } }, Err(_) => { } @@ -71,47 +71,15 @@ fn get_file_list(db:&mut db::Db, mpath:& Path, path: &Path, to_add:&mut Vec db::Metadata { - let mut meta = db::Metadata{ - title:String::new(), - artist:String::new(), - album:String::new(), - genre:String::new(), - duration:180 - }; - let path = Path::new(track); - match Probe::open(path) { - Ok(probe) => { - match probe.read(true) { - Ok(file) => { - let tag = match file.primary_tag() { - Some(primary_tag) => primary_tag, - None => file.first_tag().expect("Error: No tags found!"), - }; - - meta.title=tag.title().unwrap_or("").to_string(); - meta.artist=tag.artist().unwrap_or("").to_string(); - meta.album=tag.album().unwrap_or("").to_string(); - meta.genre=tag.genre().unwrap_or("").to_string(); - meta.duration=file.properties().duration().as_secs() as u32; - }, - Err(_) => { } - } - }, - Err(_) => { } - } - meta -} - -pub fn analyse_new_files(db:&db::Db, mpath: &Path, to_add:Vec) -> Result<()> { - let total = to_add.len(); +pub fn analyse_new_files(db:&db::Db, mpath: &PathBuf, track_paths:Vec) -> Result<()> { + let total = track_paths.len(); let pb = ProgressBar::new(total.try_into().unwrap()); let style = ProgressStyle::default_bar() .template("[{elapsed_precise}] {bar:40} {pos:>7}/{len:7} {wide_msg}") .progress_chars("##-"); pb.set_style(style); - let results = analyze_paths_streaming(to_add)?; + let results = analyze_paths_streaming(track_paths)?; let mut analysed = 0; let mut failed = 0; let mut tag_error = 0; @@ -120,7 +88,7 @@ pub fn analyse_new_files(db:&db::Db, mpath: &Path, to_add:Vec) -> Result pb.set_message(format!("Analysing {}", path)); match result { Ok(track) => { - let meta = read_metadata(&path); + let meta = tags::read(&path); let pb = PathBuf::from(path); if meta.title.is_empty() && meta.artist.is_empty() && meta.album.is_empty() && meta.genre.is_empty() { tag_error += 1; @@ -145,20 +113,20 @@ pub fn analyse_new_files(db:&db::Db, mpath: &Path, to_add:Vec) -> Result Ok(()) } -pub fn analyse_files(db_path: &str, mpath: &Path, path: &Path, dry_run:bool, keep_old:bool) { - let mut to_add:Vec = Vec::new(); +pub fn analyse_files(db_path: &str, mpath: &PathBuf, dry_run:bool, keep_old:bool) { + let mut track_paths:Vec = Vec::new(); let mut db = db::Db::new(&String::from(db_path)); + let cur = PathBuf::from(mpath); db.init(); - get_file_list(&mut db, mpath, path, &mut to_add); - log::info!("Num new tracks: {}", to_add.len()); + get_file_list(&mut db, mpath, &cur, &mut track_paths); + log::info!("Num new tracks: {}", track_paths.len()); if !keep_old { db.remove_old(mpath, dry_run); } if !dry_run { - to_add.sort(); - if to_add.len()>0 { - match analyse_new_files(&db, mpath, to_add) { + if track_paths.len()>0 { + match analyse_new_files(&db, mpath, track_paths) { Ok(_) => { }, Err(_) => { } } @@ -167,3 +135,10 @@ pub fn analyse_files(db_path: &str, mpath: &Path, path: &Path, dry_run:bool, kee db.close(); } + +pub fn read_tags(db_path: &str, mpath: &PathBuf) { + let db = db::Db::new(&String::from(db_path)); + db.init(); + db.update_tags(&mpath); + db.close(); +} \ No newline at end of file diff --git a/src/db.rs b/src/db.rs index b490afd..8e92bcf 100644 --- a/src/db.rs +++ b/src/db.rs @@ -10,6 +10,17 @@ use bliss_audio::{Analysis, AnalysisIndex}; use rusqlite::{Connection, params}; use std::path::{Path, PathBuf}; use std::process; +use crate::tags; + +pub struct FileMetadata { + pub rowid:usize, + pub file:String, + pub title:String, + pub artist:String, + pub album:String, + pub genre:String, + pub duration:u32 +} pub struct Metadata { pub title:String, @@ -145,7 +156,7 @@ impl Db { db_path = db_path.replace("/", "\\"); } let path = mpath.join(PathBuf::from(db_path.clone())); - + if !path.exists() { to_remove.push(db_path); } @@ -160,4 +171,32 @@ impl Db { } } } + + pub fn update_tags(&self, mpath:&PathBuf) { + let mut stmt = self.conn.prepare("SELECT rowid, File, Title, Artist, Album, Genre, Duration FROM Tracks;").unwrap(); + let track_iter = stmt.query_map([], |row| { + Ok(FileMetadata { + rowid: row.get(0)?, + file: row.get(1)?, + title: row.get(2)?, + artist: row.get(3)?, + album: row.get(4)?, + genre: row.get(5)?, + duration: row.get(6)?, + }) + }).unwrap(); + + for tr in track_iter { + let dtags = tr.unwrap(); + let path = String::from(mpath.join(&dtags.file).to_string_lossy()); + let ftags = tags::read(&path); + if ftags.duration!=dtags.duration || ftags.title!=dtags.title || ftags.artist!=dtags.artist || ftags.album!=dtags.album || ftags.genre!=dtags.genre { + match self.conn.execute("UPDATE Tracks SET Title=?, Artist=?, Album=?, Genre=?, Duration=? WHERE rowid=?);", + params![ftags.title, ftags.artist, ftags.album, ftags.genre, ftags.duration, dtags.rowid]) { + Ok(_) => { }, + Err(e) => { log::error!("Failed to update tags of '{}'. {}", dtags.file, e); } + } + } + } + } } diff --git a/src/main.rs b/src/main.rs index 904d3b1..11ddb3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,11 @@ * **/ use argparse::{ArgumentParser, Store}; -use std::path::Path; +use std::path::PathBuf; use std::process; mod analyse; mod db; +mod tags; fn main() { let mut db_path = "bliss.db".to_string(); @@ -17,6 +18,7 @@ fn main() { let mut music_path = ".".to_string(); let mut keep_old:bool = false; let mut dry_run:bool = false; + let mut tags_only:bool = false; { // arg_parse.refer 'borrows' db_path, etc, and can only have one @@ -28,6 +30,7 @@ fn main() { arg_parse.refer(&mut logging).add_option(&["-l", "--logging"], Store, "Log level (trace, debug, info, warn, error)"); arg_parse.refer(&mut keep_old).add_option(&["-k", "--keep-old"], Store, "Don't remove tracks from DB if they don't exist"); arg_parse.refer(&mut dry_run).add_option(&["-r", "--dry-run"], Store, "Dry run, only show what needs to be done"); + arg_parse.refer(&mut tags_only).add_option(&["-t", "--tags-only"], Store, "Re-read tags"); /* TODO: -i --ignore Update ignore column @@ -49,13 +52,13 @@ fn main() { process::exit(-1); } - let path = Path::new(&db_path); + let path = PathBuf::from(&db_path); if path.exists() && !path.is_file() { log::error!("DB path ({}) is not a file", db_path); process::exit(-1); } - let mpath = Path::new(&music_path); + let mpath = PathBuf::from(&music_path); if !mpath.exists() { log::error!("Music path ({}) does not exist", music_path); process::exit(-1); @@ -65,5 +68,8 @@ fn main() { process::exit(-1); } - analyse::analyse_files(&db_path, Path::new(&music_path), &mpath, dry_run, keep_old); + if tags_only { + analyse::read_tags(&db_path, &mpath); + } + analyse::analyse_files(&db_path, &mpath, dry_run, keep_old); } diff --git a/src/tags.rs b/src/tags.rs new file mode 100644 index 0000000..4039674 --- /dev/null +++ b/src/tags.rs @@ -0,0 +1,43 @@ +/** + * Analyse music with Bliss + * + * Copyright (c) 2022 Craig Drummond + * GPLv3 license. + * + **/ + +use lofty::{Accessor, Probe}; +use std::path::Path; +use crate::db; + +pub fn read(track:&String) -> db::Metadata { + let mut meta = db::Metadata{ + title:String::new(), + artist:String::new(), + album:String::new(), + genre:String::new(), + duration:180 + }; + let path = Path::new(track); + match Probe::open(path) { + Ok(probe) => { + match probe.read(true) { + Ok(file) => { + let tag = match file.primary_tag() { + Some(primary_tag) => primary_tag, + None => file.first_tag().expect("Error: No tags found!"), + }; + + meta.title=tag.title().unwrap_or("").to_string(); + meta.artist=tag.artist().unwrap_or("").to_string(); + meta.album=tag.album().unwrap_or("").to_string(); + meta.genre=tag.genre().unwrap_or("").to_string(); + meta.duration=file.properties().duration().as_secs() as u32; + }, + Err(_) => { } + } + }, + Err(_) => { } + } + meta +} \ No newline at end of file