use std::collections::HashMap; use std::error::Error; use std::path::Path; use std::path::PathBuf; use human_format::Formatter; use human_format::Scales; use log::info; use regex::Regex; use structopt::StructOpt; use superdeduper::Metadata; use superdeduper::MovieLibrary; const MOVIE_DIR: &str = "/home/wathiede/Movies"; const TO_BE_REMOVED_DIR: &str = "/storage/media/to-be-deleted/"; fn clean_path_parent>(path: P) -> PathBuf { let path = path.as_ref(); let path = path.parent().unwrap(); let mut path = path.to_str().unwrap(); if path.ends_with(')') { path = &path[..path.len() - 7]; } PathBuf::from(path) } fn print_movie_groups(movie_groups: &HashMap>) { let mut names = movie_groups.keys().collect::>(); names.sort(); for name in names { let paths = &movie_groups[name]; if paths.len() < 2 { continue; } let dir = name.to_str().unwrap(); println!("{}:", &dir[MOVIE_DIR.len() + 1..]); for p in paths { println!(" {}", &p[p.rfind("/").unwrap() + 1..]); } } } fn print_movies(movies: &HashMap, filter: Option<&Regex>) { let mut names = movies.keys().collect::>(); names.sort(); let mut fmtr = Formatter::new(); fmtr.with_separator(""); fmtr.with_scales(Scales::Binary()); for name in names { if let Some(re) = filter { if !re.is_match(name) { continue; } } let md = &movies[name]; info!( "{:>9} {:>8} {} {}", md.dimension, fmtr.format(md.size as f64), md.duration_text, &name[MOVIE_DIR.len() + 1..] ); println!("mv '{}' '{}'", name, TO_BE_REMOVED_DIR); } } #[derive(StructOpt)] enum Command { #[structopt(name = "samples", about = "Print movie files deemed to be samples")] Samples, #[structopt(name = "groups", about = "Print movies grouped by root name")] Groups, } #[derive(StructOpt)] #[structopt( name = "superdeduper", about = "Tool for pruning extra movies in collection" )] struct SuperDeduper { #[structopt( short = "v", help = "Sets the level of verbosity", parse(from_occurrences) )] verbose: usize, #[structopt(subcommand)] // Note that we mark a field as a subcommand cmd: Command, } fn main() -> Result<(), Box> { let app = SuperDeduper::from_args(); stderrlog::new() .verbosity(app.verbose) .timestamp(stderrlog::Timestamp::Millisecond) .init() .unwrap(); match app.cmd { Command::Samples => { let lib = MovieLibrary::new(MOVIE_DIR); let movies = lib.movies(false)?; let samples_re = Regex::new(r"(?i).*sample.*").unwrap(); print_movies(&movies, Some(&samples_re)); } Command::Groups => { let lib = MovieLibrary::new(MOVIE_DIR); let movies = lib.movies(false)?; let mut movie_groups: HashMap> = HashMap::new(); for name in movies.keys() { let clean_name = clean_path_parent(&name); let paths = movie_groups.entry(clean_name).or_insert(Vec::new()); paths.push(name.to_string()) } print_movie_groups(&movie_groups); } } Ok(()) }