Handle dateless movies when dated movie present.

This commit is contained in:
Bill Thiede 2019-12-14 10:26:54 -08:00
parent b3ca6e1cb3
commit 96819d2437
2 changed files with 89 additions and 23 deletions

View File

@ -36,12 +36,6 @@ pub fn clean_path_parent<P: AsRef<Path>>(path: P) -> PathBuf {
let path = path.as_ref(); let path = path.as_ref();
let path = path.parent().unwrap(); let path = path.parent().unwrap();
let path = path.to_str().unwrap(); let path = path.to_str().unwrap();
/*
// Strip year
if path.ends_with(')') {
path = &path[..path.len() - 7];
}
*/
PathBuf::from(path) PathBuf::from(path)
} }
@ -370,20 +364,39 @@ pub struct Movies {
impl Movies { impl Movies {
/// Find all movies with multiple copies. The returned vec contains a tuple of (Movie to keep, /// Find all movies with multiple copies. The returned vec contains a tuple of (Movie to keep,
/// One or more Movies to remove). The highest resolution movie is kept, TODO(wathiede): with /// One or more Movies to remove). The highest bit rate movie is kept.
/// higher bit rate breaking ties. /// Movies with differing years are considered distinct movies.
/// If there is a yearless movie and one or more movies with a year exist, then the yearless
/// movie will be removed
pub fn duplicate_candidates(&self) -> Vec<(&Movie, Vec<&Movie>)> { pub fn duplicate_candidates(&self) -> Vec<(&Movie, Vec<&Movie>)> {
let date_re = Regex::new(r"\(\d{4}\)$").unwrap();
let mut movie_counter = HashMap::new(); let mut movie_counter = HashMap::new();
let mut movies_without_date_counter = HashMap::new();
for m in &self.movies { for m in &self.movies {
let (path, _cmd) = m.files.first().unwrap(); let (path, _cmd) = m.files.first().unwrap();
let parent = clean_path_parent(path) let parent = clean_path_parent(path)
.to_string_lossy() .to_string_lossy()
.to_ascii_lowercase() .to_ascii_lowercase()
.to_string(); .to_string();
movie_counter.entry(parent).or_insert(Vec::new()).push(m); if date_re.is_match(&parent) {
movie_counter.entry(parent).or_insert(Vec::new()).push(m);
} else {
movies_without_date_counter
.entry(parent)
.or_insert(Vec::new())
.push(m);
}
} }
let mut dupes = Vec::new(); let mut dupes: Vec<(&Movie, Vec<&Movie>)> = Vec::new();
for (_parent, mut movies) in movie_counter.into_iter() { for (parent, movies) in movie_counter.iter() {
let dateless_parent = parent[..parent.len() - 7].to_string();
if let Some(movie) = movies_without_date_counter.remove(&dateless_parent) {
let tuple = (movies[0], movie);
dupes.push(tuple);
}
}
for (parent, mut movies) in movie_counter.into_iter() {
if movies.len() > 1 { if movies.len() > 1 {
// Sort, lowest bit_rate movie first // Sort, lowest bit_rate movie first
movies.sort_by(|a, b| a.min_bit_rate().cmp(&b.min_bit_rate())); movies.sort_by(|a, b| a.min_bit_rate().cmp(&b.min_bit_rate()));
@ -392,8 +405,26 @@ impl Movies {
// Take the largest image, return the rest for removal. // Take the largest image, return the rest for removal.
let tuple = (movies.remove(0), movies); let tuple = (movies.remove(0), movies);
dupes.push(tuple); dupes.push(tuple);
} else {
/*
let dateless_parent = if date_re.is_match(&parent) {
parent[..parent.len() - 7].to_string()
} else {
parent.to_string()
};
*/
} }
} }
// Sort to make this function deterministic.
dupes.sort_by(|(a_keep, _), (b_keep, _)| {
a_keep
.files
.first()
.unwrap()
.0
.partial_cmp(&b_keep.files.first().unwrap().0)
.unwrap()
});
for d in &dupes { for d in &dupes {
let (biggest, deletes) = d; let (biggest, deletes) = d;
eprintln!("biggest: {}", biggest); eprintln!("biggest: {}", biggest);

View File

@ -86,6 +86,8 @@ where
fn build_complex_metadata() -> HashMap<String, CompactMetadata> { fn build_complex_metadata() -> HashMap<String, CompactMetadata> {
vec![ vec![
build_tuple("Remake (2019)/new.mkv", (1920, 1080), 10),
build_tuple("Remake (1960)/old.mkv", (1920, 1080), 1),
build_tuple( build_tuple(
"One Movie With Year (2019)/abcdef123456789.mkv", "One Movie With Year (2019)/abcdef123456789.mkv",
(1920, 1080), (1920, 1080),
@ -117,15 +119,25 @@ fn build_complex_metadata() -> HashMap<String, CompactMetadata> {
5000, 5000,
), ),
build_tuple( build_tuple(
"two movies different caps (2019)/abcdef123456789.mkv", "two movies different caps (2019)/larger.mkv",
(1920, 1080), (1920, 1080),
100, 100,
), ),
build_tuple( build_tuple(
"Two Movies Different Caps (2019)/abcdef123456789.mkv", "Two Movies Different Caps (2019)/smaller.mkv",
(640, 480), (640, 480),
1, 1,
), ),
build_tuple(
"Movie with and without year (2019)/with year.mkv",
(1920, 1080),
10,
),
build_tuple(
"Movie with and without year/without year.mkv",
(1280, 720),
1,
),
] ]
.into_iter() .into_iter()
.collect() .collect()
@ -134,6 +146,8 @@ fn build_complex_metadata() -> HashMap<String, CompactMetadata> {
fn build_complex_movies() -> Movies { fn build_complex_movies() -> Movies {
let mut m = Movies { let mut m = Movies {
movies: vec![ movies: vec![
build_movie(vec![("Remake (2019)/new.mkv", (1920, 1080), 10)]),
build_movie(vec![("Remake (1960)/old.mkv", (1920, 1080), 1)]),
build_movie(vec![( build_movie(vec![(
"One Movie With Year (2019)/abcdef123456789.mkv", "One Movie With Year (2019)/abcdef123456789.mkv",
(1920, 1080), (1920, 1080),
@ -169,15 +183,25 @@ fn build_complex_movies() -> Movies {
5000, 5000,
)]), )]),
build_movie(vec![( build_movie(vec![(
"two movies different caps (2019)/abcdef123456789.mkv", "two movies different caps (2019)/larger.mkv",
(1920, 1080), (1920, 1080),
100, 100,
)]), )]),
build_movie(vec![( build_movie(vec![(
"Two Movies Different Caps (2019)/abcdef123456789.mkv", "Two Movies Different Caps (2019)/smaller.mkv",
(640, 480), (640, 480),
1, 1,
)]), )]),
build_movie(vec![(
"Movie with and without year (2019)/with year.mkv",
(1920, 1080),
10,
)]),
build_movie(vec![(
"Movie with and without year/without year.mkv",
(1280, 720),
1,
)]),
], ],
}; };
m.movies.sort_by(|a, b| { m.movies.sort_by(|a, b| {
@ -209,12 +233,11 @@ fn test_roundtrip_library() -> Result<(), Box<dyn Error>> {
} }
fn validate_duplicates(got: Vec<(&Movie, Vec<&Movie>)>, want: Vec<(Movie, Vec<Movie>)>) { fn validate_duplicates(got: Vec<(&Movie, Vec<&Movie>)>, want: Vec<(Movie, Vec<Movie>)>) {
assert_eq!(got.len(), want.len()); let want: Vec<(&Movie, Vec<&Movie>)> = want
for (g, w) in got.iter().zip(&want) { .iter()
assert_eq!(g.0, &w.0, "Got:\n{:?}\nWant:\n{:?}", g, w); .map(|(keep, delete)| (keep, delete.iter().map(|m| m).collect()))
let want = w.1.iter().map(|v| v).collect::<Vec<_>>(); .collect();
assert_eq!(g.1, want, "Got:\n{:?}\nWant:\n{:?}", g, w); assert_eq!(got, want);
}
} }
#[test] #[test]
@ -222,6 +245,18 @@ fn test_duplicate_candidates() -> Result<(), Box<dyn Error>> {
let movies = build_complex_movies(); let movies = build_complex_movies();
let got = movies.duplicate_candidates(); let got = movies.duplicate_candidates();
let want = vec![ let want = vec![
(
build_movie(vec![(
"Movie with and without year (2019)/with year.mkv",
(1920, 1080),
10,
)]),
vec![build_movie(vec![(
"Movie with and without year/without year.mkv",
(1280, 720),
1,
)])],
),
( (
build_movie(vec![( build_movie(vec![(
"Two Movies With Multi Parts (2019)/somethingelse.mkv", "Two Movies With Multi Parts (2019)/somethingelse.mkv",
@ -243,12 +278,12 @@ fn test_duplicate_candidates() -> Result<(), Box<dyn Error>> {
), ),
( (
build_movie(vec![( build_movie(vec![(
"two movies different caps (2019)/abcdef123456789.mkv", "two movies different caps (2019)/larger.mkv",
(1920, 1080), (1920, 1080),
100, 100,
)]), )]),
vec![build_movie(vec![( vec![build_movie(vec![(
"Two Movies Different Caps (2019)/abcdef123456789.mkv", "Two Movies Different Caps (2019)/smaller.mkv",
(640, 480), (640, 480),
1, 1,
)])], )])],