feat: CLI progress bar with indicatif
- Progress bar shows scan progress with file count and ETA - Split scan_images into collect_image_paths + process_image - Added indicatif crate
This commit is contained in:
42
src/lib.rs
42
src/lib.rs
@@ -76,6 +76,48 @@ pub fn scan_images(root: &Path) -> Result<Vec<ImageEntry>> {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub fn collect_image_paths(root: &Path) -> Result<Vec<PathBuf>> {
|
||||
let mut paths = Vec::new();
|
||||
for entry in WalkDir::new(root).follow_links(true) {
|
||||
let entry = entry?;
|
||||
if entry.file_type().is_file() && is_image_path(entry.path()) {
|
||||
paths.push(entry.path().to_path_buf());
|
||||
}
|
||||
}
|
||||
Ok(paths)
|
||||
}
|
||||
|
||||
pub fn process_image(path: &Path) -> Option<ImageEntry> {
|
||||
let bytes = match fs::read(path) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
eprintln!("warning: skipping {}: {e}", path.display());
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let sha256 = format!("{:x}", Sha256::digest(&bytes));
|
||||
let img = match ImageReader::new(Cursor::new(&bytes)).with_guessed_format() {
|
||||
Ok(reader) => match reader.decode() {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
eprintln!("warning: skipping {}: {e}", path.display());
|
||||
return None;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("warning: skipping {}: {e}", path.display());
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let dhash = compute_dhash(&img);
|
||||
Some(ImageEntry {
|
||||
path: path.to_path_buf(),
|
||||
sha256,
|
||||
dhash,
|
||||
file_size: bytes.len() as u64,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_dhash(img: &image::DynamicImage) -> u64 {
|
||||
let gray = img
|
||||
.grayscale()
|
||||
|
||||
21
src/main.rs
21
src/main.rs
@@ -1,5 +1,6 @@
|
||||
use deduper::{find_duplicate_groups, scan_images, DuplicateKind};
|
||||
use deduper::{find_duplicate_groups, collect_image_paths, process_image, DuplicateKind};
|
||||
use deduper::ignore_db;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -49,7 +50,8 @@ fn main() {
|
||||
};
|
||||
|
||||
let root = Path::new(&config.root);
|
||||
let entries = match scan_images(root) {
|
||||
|
||||
let paths = match collect_image_paths(root) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
eprintln!("scan error: {e}");
|
||||
@@ -57,6 +59,21 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let pb = ProgressBar::new(paths.len() as u64);
|
||||
pb.set_style(ProgressStyle::default_bar()
|
||||
.template("{spinner:.red} [{bar:40.cyan/blue}] {pos}/{len} images ({eta})")
|
||||
.unwrap()
|
||||
.progress_chars("=>-"));
|
||||
|
||||
let mut entries = Vec::with_capacity(paths.len());
|
||||
for path in &paths {
|
||||
if let Some(entry) = process_image(path) {
|
||||
entries.push(entry);
|
||||
}
|
||||
pb.inc(1);
|
||||
}
|
||||
pb.finish_with_message(format!("{} images processed", entries.len()));
|
||||
|
||||
let mut groups = find_duplicate_groups(&entries, config.threshold);
|
||||
|
||||
// Filter out ignored groups
|
||||
|
||||
Reference in New Issue
Block a user