- Launches local HTTP server with dark-themed review UI - Side-by-side image comparison per duplicate group - Checkbox selection + delete confirmation - Shows file size and path per image - Exact/similar badges per group - Shutdown endpoint for clean exit - Magic byte format detection (fixes misnamed screenshots) - 23 tests passing
80 lines
2.0 KiB
Rust
80 lines
2.0 KiB
Rust
use deduper::{find_duplicate_groups, scan_images, DuplicateKind};
|
|
use std::env;
|
|
use std::path::Path;
|
|
|
|
mod review;
|
|
|
|
struct Config {
|
|
root: String,
|
|
threshold: u32,
|
|
review: bool,
|
|
}
|
|
|
|
fn parse_args() -> Option<Config> {
|
|
let args: Vec<String> = env::args().collect();
|
|
let mut root = None;
|
|
let mut threshold = 8u32;
|
|
let mut review = false;
|
|
|
|
let mut i = 1;
|
|
while i < args.len() {
|
|
match args[i].as_str() {
|
|
"--review" => review = true,
|
|
arg if arg.starts_with('-') => {
|
|
eprintln!("unknown flag: {arg}");
|
|
return None;
|
|
}
|
|
_ => {
|
|
if root.is_none() {
|
|
root = Some(args[i].clone());
|
|
} else {
|
|
threshold = args[i].parse().unwrap_or(8);
|
|
}
|
|
}
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
root.map(|r| Config { root: r, threshold, review })
|
|
}
|
|
|
|
fn main() {
|
|
let config = match parse_args() {
|
|
Some(c) => c,
|
|
None => {
|
|
eprintln!("usage: deduper <folder> [hamming-threshold] [--review]");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
|
|
let root = Path::new(&config.root);
|
|
let entries = match scan_images(root) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
eprintln!("scan error: {e}");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
|
|
let groups = find_duplicate_groups(&entries, config.threshold);
|
|
if groups.is_empty() {
|
|
println!("no image duplicates found");
|
|
return;
|
|
}
|
|
|
|
if config.review {
|
|
review::launch_review(&groups);
|
|
} else {
|
|
for (idx, group) in groups.iter().enumerate() {
|
|
let kind = match group.kind {
|
|
DuplicateKind::Exact => "exact",
|
|
DuplicateKind::Similar => "similar",
|
|
};
|
|
println!("group {} [{}]", idx + 1, kind);
|
|
for path in &group.paths {
|
|
println!(" {}", path.display());
|
|
}
|
|
}
|
|
}
|
|
}
|