Turn into a structopt driven CLI. Functioning samples command.
This commit is contained in:
111
src/main.rs
111
src/main.rs
@@ -4,13 +4,18 @@ use std::fmt;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use glob::glob;
|
||||
use human_format::Formatter;
|
||||
use human_format::Scales;
|
||||
use log::info;
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use structopt::StructOpt;
|
||||
|
||||
const MOVIE_DIR: &str = "/home/wathiede/Movies";
|
||||
const TO_BE_REMOVED_DIR: &str = "/storage/media/to-be-deleted/";
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Resolution(usize, usize);
|
||||
@@ -47,34 +52,116 @@ fn read_metadata_from_file<P: AsRef<Path>>(path: P) -> Result<MetadataFile, Box<
|
||||
Ok(md)
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut movies = HashMap::new();
|
||||
for md in glob(&format!("{}/*/metadata.json", MOVIE_DIR))? {
|
||||
match md {
|
||||
Ok(path) => {
|
||||
let mdf = read_metadata_from_file(&path)?;
|
||||
for (name, md) in mdf.metadata {
|
||||
movies.insert(name, md);
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("{:?}", e),
|
||||
fn clean_path_parent<P: AsRef<Path>>(path: P) -> PathBuf {
|
||||
let path: &Path = path.as_ref();
|
||||
path.parent().unwrap().into()
|
||||
}
|
||||
|
||||
fn print_movie_groups(movie_groups: &HashMap<PathBuf, Vec<String>>) {
|
||||
for (name, paths) in movie_groups {
|
||||
if paths.len() < 2 {
|
||||
continue;
|
||||
}
|
||||
println!("{}:", name.display());
|
||||
for p in paths {
|
||||
println!(" {}", &p[p.rfind("/").unwrap() + 1..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_movies(movies: &HashMap<String, Metadata>, filter: Option<&Regex>) {
|
||||
let mut names = movies.keys().collect::<Vec<_>>();
|
||||
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];
|
||||
println!(
|
||||
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 get_movies() -> Result<(HashMap<String, Metadata>), Box<dyn Error>> {
|
||||
let mut movies = HashMap::new();
|
||||
for md in glob(&format!("{}/*/metadata.json", MOVIE_DIR))? {
|
||||
match md {
|
||||
Ok(path) => {
|
||||
let mdf = read_metadata_from_file(&path)?;
|
||||
for (name, md) in mdf.metadata {
|
||||
if Path::new(&name).is_file() {
|
||||
movies.insert(name, md);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
Ok(movies)
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let app = SuperDeduper::from_args();
|
||||
stderrlog::new()
|
||||
.verbosity(app.verbose)
|
||||
.timestamp(stderrlog::Timestamp::Millisecond)
|
||||
.init()
|
||||
.unwrap();
|
||||
|
||||
match app.cmd {
|
||||
Command::Samples => {
|
||||
let movies = get_movies()?;
|
||||
|
||||
let samples_re = Regex::new(r"(?i).*sample.*").unwrap();
|
||||
print_movies(&movies, Some(&samples_re));
|
||||
}
|
||||
Command::Groups => {
|
||||
let movies = get_movies()?;
|
||||
|
||||
let mut movie_groups: HashMap<PathBuf, Vec<String>> = 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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user