Move code to lib.rs, update testdata.

This commit is contained in:
2019-10-28 18:03:14 -07:00
parent 30b91e0323
commit 86fbf78a73
42 changed files with 168 additions and 71 deletions

140
src/lib.rs Normal file
View File

@@ -0,0 +1,140 @@
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::path::PathBuf;
use glob::glob;
use serde::Deserialize;
#[derive(Clone, Deserialize, Debug)]
pub struct Resolution(usize, usize);
impl fmt::Display for Resolution {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let v = format!("{}x{}", self.0, self.1);
f.pad(&v)
}
}
#[derive(Clone, Deserialize, Debug)]
pub struct Metadata {
pub size: usize,
pub dimension: Resolution,
pub duration_text: String,
pub duration: f32,
}
#[derive(Deserialize, Debug)]
pub struct MetadataFile {
#[serde(flatten)]
pub metadata: HashMap<String, Metadata>,
}
pub struct MovieLibrary {
root: String,
}
impl MovieLibrary {
pub fn new<S: Into<String>>(root: S) -> MovieLibrary {
MovieLibrary { root: root.into() }
}
pub fn movies(
&self,
include_stale: bool,
) -> Result<(HashMap<String, Metadata>), Box<dyn Error>> {
let mut movies = HashMap::new();
for md in glob(&format!("{}/*/metadata.json", self.root))? {
match md {
Ok(path) => {
let mdf = read_metadata_from_file(&path)?;
for (name, md) in mdf.metadata {
if include_stale {
movies.insert(name, md);
} else {
// Filter out files that don't exist
dbg!(&self.root, &name);
let mut p = PathBuf::from(&self.root);
p.push(&name);
dbg!(&p);
if p.is_file() {
movies.insert(name, md);
}
}
}
}
Err(e) => {
return Err(e.into());
}
}
}
Ok(movies)
}
}
fn read_metadata_from_file<P: AsRef<Path>>(path: P) -> Result<MetadataFile, Box<dyn Error>> {
// Open the file in read-only mode with buffer.
let file = File::open(path)?;
let reader = BufReader::new(file);
// Read the JSON contents of the file as an instance of `User`.
let md = serde_json::from_reader(reader)?;
// Return the `User`.
Ok(md)
}
#[cfg(test)]
mod tests {
use super::*;
fn testdata_dir() -> String {
format!("{}/testdata", env::var("CARGO_MANIFEST_DIR").unwrap())
}
#[test]
fn test_movies() {
let lib = MovieLibrary::new(format!("{}/Movies", testdata_dir()));
let movies = lib.movies(true).expect("failed to get movies");
let mut got = movies.keys().collect::<Vec<_>>();
got.sort();
let want = [
"Aladdin (1992)/Aladdin.1992.720p.BRrip.x264.GAZ.YIFY.mp4",
"Aladdin (2019)/4fe12adfdf4b4e9daa4f1366452d3431.mkv",
"Higher Learning/Higher Learning CD1.avi",
"Higher Learning/Higher Learning CD2.avi",
"J0hn W1ck (2014)/J0hn W1ck (2014) m720p x264 aac.m4v",
"J0hn W1ck (2014)/J0hn W1ck (2014) m720p x264 aac.sample.m4v",
"Stale Sample (2019)/Stale Sample (2019) m720p x264 aac.sample.m4v",
"The Hudsucker Proxy (1994)/54151c3b9a2a4773958f848efecefc3b.mkv",
"The Hudsucker Proxy (1994)/The Hudsucker Proxy CD1.avi",
"The Hudsucker Proxy (1994)/The Hudsucker Proxy CD2.avi",
];
assert_eq!(got, want);
}
#[test]
fn test_filter_stale() {
let lib = MovieLibrary::new(format!("{}/Movies", testdata_dir()));
let movies = lib.movies(false).expect("failed to get movies");
let mut got = movies.keys().collect::<Vec<_>>();
got.sort();
let want = [
"Aladdin (1992)/Aladdin.1992.720p.BRrip.x264.GAZ.YIFY.mp4",
"Aladdin (2019)/4fe12adfdf4b4e9daa4f1366452d3431.mkv",
"Higher Learning/Higher Learning CD1.avi",
"Higher Learning/Higher Learning CD2.avi",
"J0hn W1ck (2014)/J0hn W1ck (2014) m720p x264 aac.m4v",
"J0hn W1ck (2014)/J0hn W1ck (2014) m720p x264 aac.sample.m4v",
"The Hudsucker Proxy (1994)/54151c3b9a2a4773958f848efecefc3b.mkv",
"The Hudsucker Proxy (1994)/The Hudsucker Proxy CD1.avi",
"The Hudsucker Proxy (1994)/The Hudsucker Proxy CD2.avi",
];
assert_eq!(got, want);
}
}

View File

@@ -1,68 +1,41 @@
use std::collections::HashMap;
use std::error::Error;
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;
use superdeduper::Metadata;
use superdeduper::MovieLibrary;
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);
impl fmt::Display for Resolution {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let v = format!("{}x{}", self.0, self.1);
f.pad(&v)
}
}
#[derive(Deserialize, Debug)]
struct Metadata {
size: usize,
dimension: Resolution,
duration_text: String,
duration: f32,
}
#[derive(Deserialize, Debug)]
struct MetadataFile {
#[serde(flatten)]
metadata: HashMap<String, Metadata>,
}
fn read_metadata_from_file<P: AsRef<Path>>(path: P) -> Result<MetadataFile, Box<dyn Error>> {
// Open the file in read-only mode with buffer.
let file = File::open(path)?;
let reader = BufReader::new(file);
// Read the JSON contents of the file as an instance of `User`.
let md = serde_json::from_reader(reader)?;
// Return the `User`.
Ok(md)
}
fn clean_path_parent<P: AsRef<Path>>(path: P) -> PathBuf {
let path: &Path = path.as_ref();
path.parent().unwrap().into()
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<PathBuf, Vec<String>>) {
for (name, paths) in movie_groups {
let mut names = movie_groups.keys().collect::<Vec<_>>();
names.sort();
for name in names {
let paths = &movie_groups[name];
if paths.len() < 2 {
continue;
}
println!("{}:", name.display());
let dir = name.to_str().unwrap();
println!("{}:", &dir[MOVIE_DIR.len() + 1..]);
for p in paths {
println!(" {}", &p[p.rfind("/").unwrap() + 1..]);
}
@@ -117,24 +90,6 @@ struct SuperDeduper {
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()
@@ -145,13 +100,15 @@ fn main() -> Result<(), Box<dyn Error>> {
match app.cmd {
Command::Samples => {
let movies = get_movies()?;
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 movies = get_movies()?;
let lib = MovieLibrary::new(MOVIE_DIR);
let movies = lib.movies(false)?;
let mut movie_groups: HashMap<PathBuf, Vec<String>> = HashMap::new();
for name in movies.keys() {