From b4eb7562cfb3a4faf4649db49e60621dcecb5e38 Mon Sep 17 00:00:00 2001 From: CDrummond Date: Thu, 22 May 2025 12:07:24 +0100 Subject: [PATCH] Gracefully handle Ctrl-C. --- Cargo.lock | 33 ++++++++++++++++-- Cargo.toml | 1 + ChangeLog | 1 + src/analyse.rs | 92 ++++++++++++++++++++++++++++++++++++-------------- src/db.rs | 30 +++++++++++++++- 5 files changed, 129 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46ed1da..1a42302 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,6 +187,7 @@ dependencies = [ "bliss-audio", "chrono", "configparser", + "ctrlc", "dirs", "env_logger", "hhmmss", @@ -289,6 +290,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.40" @@ -410,6 +417,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "ctrlc" +version = "3.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" +dependencies = [ + "nix", + "windows-sys", +] + [[package]] name = "data-encoding" version = "2.8.0" @@ -766,9 +783,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.170" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" @@ -909,6 +926,18 @@ dependencies = [ "rand", ] +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "noisy_float" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 7a6439c..845c59d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ num_cpus = "1.13.0" which = { version = "7.0.2", optional = true } rcue = { version = "0.1.3", optional = true } hhmmss = { version = "0.1.0", optional = true } +ctrlc = "3.4" [features] libav = ["bliss-audio/ffmpeg"] diff --git a/ChangeLog b/ChangeLog index 6daf618..3ae77c8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,7 @@ 7. Use max of 16 significant digits (trailing zeros stripped) when writing analysis results to file tags. 8. Strip symbols from release binaries. +9. Gracefully handle Ctrl-C. 0.3.0 ----- diff --git a/src/analyse.rs b/src/analyse.rs index 168e49c..0312a5c 100644 --- a/src/analyse.rs +++ b/src/analyse.rs @@ -44,6 +44,20 @@ const MAX_ERRORS_TO_SHOW: usize = 100; const MAX_TAG_ERRORS_TO_SHOW: usize = 50; const VALID_EXTENSIONS: [&str; 7] = ["m4a", "mp3", "ogg", "flac", "opus", "wv", "dsf"]; +static mut TERMINATE_ANALYSIS_FLAG: bool = false; + +fn terminate_analysis() -> bool { + unsafe { + return TERMINATE_ANALYSIS_FLAG + } +} + +fn handle_ctrl_c() { + unsafe { + TERMINATE_ANALYSIS_FLAG = true; + } +} + fn get_file_list(db: &mut db::Db, mpath: &Path, path: &Path, track_paths: &mut Vec, cue_tracks:&mut Vec, file_count:&mut usize, max_num_files: usize, tagged_file_count:&mut usize, dry_run: bool) { if !path.is_dir() { return; @@ -247,9 +261,16 @@ fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec, max if inc_progress { progress.inc(1); } + if terminate_analysis() { + break + } } - progress.finish_with_message("Finished!"); + if terminate_analysis() { + progress.abandon_with_message("Terminated!"); + } else { + progress.finish_with_message("Finished!"); + } log::info!("{} Analysed. {} Failed.", analysed, failed.len()); show_errors(&mut failed, &mut tag_error); Ok(()) @@ -298,6 +319,9 @@ fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec, max }; progress.inc(1); + if terminate_analysis() { + break + } } // Reset terminal, otherwise typed output does not show? Perhaps Linux only... @@ -308,7 +332,11 @@ fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec, max }; } - progress.finish_with_message("Finished!"); + if terminate_analysis() { + progress.abandon_with_message("Terminated!"); + } else { + progress.finish_with_message("Finished!"); + } log::info!("{} Analysed. {} Failed.", analysed, failed.len()); show_errors(&mut failed, &mut tag_error); Ok(()) @@ -395,8 +423,16 @@ fn analyse_new_cue_tracks(db:&db::Db, mpath: &PathBuf, cue_tracks:Vec, dry_run: bool, keep_old: bool, max_num_files: usize, max_threads: usize, ignore_path: &PathBuf, write_tags: bool, preserve_mod_times: bool) { let mut db = db::Db::new(&String::from(db_path)); + ctrlc::set_handler(move || { + handle_ctrl_c(); + }).expect("Error setting Ctrl-C handler"); + db.init(); if !keep_old { @@ -433,31 +473,33 @@ pub fn analyse_files(db_path: &str, mpaths: &Vec, dry_run: bool, keep_o } log::info!("New tagged files: {}", tagged_file_count); - if dry_run { - if !track_paths.is_empty() || !cue_tracks.is_empty() { - log::info!("The following need to be analysed:"); - for track in track_paths { - log::info!(" {}", track); - } - for track in cue_tracks { - log::info!(" {}", track.track_path.to_string_lossy()); - } - } - } else { - if !track_paths.is_empty() { - match analyse_new_files(&db, &mpath, track_paths, max_threads, write_tags, preserve_mod_times) { - Ok(_) => { changes_made = true; } - Err(e) => { log::error!("Analysis returned error: {}", e); } + if !terminate_analysis() { + if dry_run { + if !track_paths.is_empty() || !cue_tracks.is_empty() { + log::info!("The following need to be analysed:"); + for track in track_paths { + log::info!(" {}", track); + } + for track in cue_tracks { + log::info!(" {}", track.track_path.to_string_lossy()); + } } } else { - log::info!("No new files to analyse"); - } + if !track_paths.is_empty() { + match analyse_new_files(&db, &mpath, track_paths, max_threads, write_tags, preserve_mod_times) { + Ok(_) => { changes_made = true; } + Err(e) => { log::error!("Analysis returned error: {}", e); } + } + } else { + log::info!("No new files to analyse"); + } - #[cfg(feature = "ffmpeg")] - if !cue_tracks.is_empty() { - match analyse_new_cue_tracks(&db, &mpath, cue_tracks) { - Ok(_) => { changes_made = true; }, - Err(e) => { log::error!("Cue analysis returned error: {}", e); } + #[cfg(feature = "ffmpeg")] + if !cue_tracks.is_empty() && !terminate_analysis() { + match analyse_new_cue_tracks(&db, &mpath, cue_tracks) { + Ok(_) => { changes_made = true; }, + Err(e) => { log::error!("Cue analysis returned error: {}", e); } + } } } } diff --git a/src/db.rs b/src/db.rs index 350d5a3..e1b0229 100644 --- a/src/db.rs +++ b/src/db.rs @@ -62,6 +62,20 @@ impl Metadata { } } +static mut TERMINATE_EXPORT_FLAG: bool = false; + +fn terminate_export() -> bool { + unsafe { + return TERMINATE_EXPORT_FLAG + } +} + +fn handle_ctrl_c() { + unsafe { + TERMINATE_EXPORT_FLAG = true; + } +} + pub struct Db { pub conn: Connection, } @@ -352,6 +366,10 @@ impl Db { } pub fn export(&self, mpaths: &Vec, max_threads: usize, preserve_mod_times: bool) { + ctrlc::set_handler(move || { + handle_ctrl_c(); + }).expect("Error setting Ctrl-C handler"); + log::info!("Querying database"); let mut tracks:Vec = Vec::new(); let mut stmt = self.conn.prepare("SELECT File, Tempo, Zcr, MeanSpectralCentroid, StdDevSpectralCentroid, MeanSpectralRolloff, StdDevSpectralRolloff, MeanSpectralFlatness, StdDevSpectralFlatness, MeanLoudness, StdDevLoudness, Chroma1, Chroma2, Chroma3, Chroma4, Chroma5, Chroma6, Chroma7, Chroma8, Chroma9, Chroma10 FROM Tracks ORDER BY File ASC;").unwrap(); @@ -416,8 +434,15 @@ impl Db { if processed == total { break; } + if terminate_export() { + break + } + } + if terminate_export() { + progress.abandon_with_message("Terminated!"); + } else { + progress.finish_with_message(format!("Finished!")); } - progress.finish_with_message(format!("Finished!")); log::info!("{} Exported. {} Existing. {} Failed.", exported, had_tags, failed_to_write); }); threads.push(reporting_thread); @@ -438,6 +463,9 @@ impl Db { } } sndr.send(updated).unwrap(); + if terminate_export() { + break + } } })); }