Use bit rate instead of resolution in making dedup decision.

Keep lower resolution movie if it is higher bit rate.
This commit is contained in:
Bill Thiede 2019-11-25 08:47:04 -08:00
parent 974d9386fb
commit 150bdfddef
2 changed files with 76 additions and 6 deletions

View File

@ -324,6 +324,16 @@ pub struct Movie {
} }
impl Movie { impl Movie {
fn min_bit_rate(&self) -> Option<usize> {
if self.files.is_empty() {
None
} else {
Some(self.files.iter().fold(usize::max_value(), |acc, (_, cmd)| {
std::cmp::min(acc, cmd.bit_rate)
}))
}
}
fn min_resolution(&self) -> Option<Resolution> { fn min_resolution(&self) -> Option<Resolution> {
if self.files.is_empty() { if self.files.is_empty() {
None None
@ -371,8 +381,8 @@ impl Movies {
let mut dupes = Vec::new(); let mut dupes = Vec::new();
for (_parent, mut movies) in movie_counter.into_iter() { for (_parent, mut movies) in movie_counter.into_iter() {
if movies.len() > 1 { if movies.len() > 1 {
// Sort, smallest movie first. // Sort, lowest bit_rate movie first
movies.sort_by(|a, b| a.min_resolution().cmp(&b.min_resolution())); movies.sort_by(|a, b| a.min_bit_rate().cmp(&b.min_bit_rate()));
// Flip order, we care about the largest. // Flip order, we care about the largest.
movies.reverse(); movies.reverse();
// Take the largest image, return the rest for removal. // Take the largest image, return the rest for removal.

View File

@ -49,7 +49,7 @@ fn test_simple_library() {
); );
} }
fn build_tuple<R>(path: &str, res: R) -> (String, CompactMetadata) fn build_tuple<R>(path: &str, res: R, bit_rate: usize) -> (String, CompactMetadata)
where where
R: Into<Resolution>, R: Into<Resolution>,
{ {
@ -58,7 +58,7 @@ where
path.to_string(), path.to_string(),
CompactMetadata { CompactMetadata {
filename: format!("./{}", path), filename: format!("./{}", path),
bit_rate: 1, bit_rate,
duration: 1.0, duration: 1.0,
format_name: "test_format".to_string(), format_name: "test_format".to_string(),
size: 1, size: 1,
@ -72,14 +72,14 @@ where
) )
} }
fn build_movie<R>(paths: Vec<(&str, R)>) -> Movie fn build_movie<R>(paths: Vec<(&str, R, usize)>) -> Movie
where where
R: Into<Resolution>, R: Into<Resolution>,
{ {
Movie { Movie {
files: paths files: paths
.into_iter() .into_iter()
.map(|(path, res)| build_tuple(path, res)) .map(|(path, res, bit_rate)| build_tuple(path, res, bit_rate))
.collect(), .collect(),
} }
} }
@ -89,26 +89,32 @@ fn build_complex_metadata() -> HashMap<String, CompactMetadata> {
build_tuple( build_tuple(
"One Movie With Year (2019)/abcdef123456789.mkv", "One Movie With Year (2019)/abcdef123456789.mkv",
(1920, 1080), (1920, 1080),
1,
), ),
build_tuple( build_tuple(
"One Movie With Two Parts (2019)/abcdef123456789 part 1.mkv", "One Movie With Two Parts (2019)/abcdef123456789 part 1.mkv",
(1280, 720), (1280, 720),
1,
), ),
build_tuple( build_tuple(
"One Movie With Two Parts (2019)/abcdef123456789 part 2.mkv", "One Movie With Two Parts (2019)/abcdef123456789 part 2.mkv",
(1280, 720), (1280, 720),
1,
), ),
build_tuple( build_tuple(
"Two Movies With Multi Parts (2019)/abcdef123456789 part 1.mkv", "Two Movies With Multi Parts (2019)/abcdef123456789 part 1.mkv",
(1280, 720), (1280, 720),
1000,
), ),
build_tuple( build_tuple(
"Two Movies With Multi Parts (2019)/abcdef123456789 part 2.mkv", "Two Movies With Multi Parts (2019)/abcdef123456789 part 2.mkv",
(1280, 720), (1280, 720),
1000,
), ),
build_tuple( build_tuple(
"Two Movies With Multi Parts (2019)/somethingelse.mkv", "Two Movies With Multi Parts (2019)/somethingelse.mkv",
(1920, 1080), (1920, 1080),
5000,
), ),
] ]
.into_iter() .into_iter()
@ -121,30 +127,36 @@ fn build_complex_movies() -> Movies {
build_movie(vec![( build_movie(vec![(
"One Movie With Year (2019)/abcdef123456789.mkv", "One Movie With Year (2019)/abcdef123456789.mkv",
(1920, 1080), (1920, 1080),
1,
)]), )]),
build_movie(vec![ build_movie(vec![
( (
"One Movie With Two Parts (2019)/abcdef123456789 part 1.mkv", "One Movie With Two Parts (2019)/abcdef123456789 part 1.mkv",
(1280, 720), (1280, 720),
1,
), ),
( (
"One Movie With Two Parts (2019)/abcdef123456789 part 2.mkv", "One Movie With Two Parts (2019)/abcdef123456789 part 2.mkv",
(1280, 720), (1280, 720),
1,
), ),
]), ]),
build_movie(vec![ build_movie(vec![
( (
"Two Movies With Multi Parts (2019)/abcdef123456789 part 1.mkv", "Two Movies With Multi Parts (2019)/abcdef123456789 part 1.mkv",
(1280, 720), (1280, 720),
1000,
), ),
( (
"Two Movies With Multi Parts (2019)/abcdef123456789 part 2.mkv", "Two Movies With Multi Parts (2019)/abcdef123456789 part 2.mkv",
(1280, 720), (1280, 720),
1000,
), ),
]), ]),
build_movie(vec![( build_movie(vec![(
"Two Movies With Multi Parts (2019)/somethingelse.mkv", "Two Movies With Multi Parts (2019)/somethingelse.mkv",
(1920, 1080), (1920, 1080),
5000,
)]), )]),
], ],
}; };
@ -192,15 +204,18 @@ fn test_duplicate_candidates() -> Result<(), Box<dyn Error>> {
build_movie(vec![( build_movie(vec![(
"Two Movies With Multi Parts (2019)/somethingelse.mkv", "Two Movies With Multi Parts (2019)/somethingelse.mkv",
(1920, 1080), (1920, 1080),
5000,
)]), )]),
vec![build_movie(vec![ vec![build_movie(vec![
( (
"Two Movies With Multi Parts (2019)/abcdef123456789 part 1.mkv", "Two Movies With Multi Parts (2019)/abcdef123456789 part 1.mkv",
(1280, 720), (1280, 720),
1000,
), ),
( (
"Two Movies With Multi Parts (2019)/abcdef123456789 part 2.mkv", "Two Movies With Multi Parts (2019)/abcdef123456789 part 2.mkv",
(1280, 720), (1280, 720),
1000,
), ),
])], ])],
)]; )];
@ -215,10 +230,12 @@ fn test_fullmetal() -> Result<(), Box<dyn Error>> {
build_movie(vec![( build_movie(vec![(
"Full Metal Jacket (1987)/Full Metal Jacket.mp4", "Full Metal Jacket (1987)/Full Metal Jacket.mp4",
(1280, 720), (1280, 720),
2581935,
)]), )]),
build_movie(vec![( build_movie(vec![(
"Full Metal Jacket (1987)/1776f8e2fb614a6fb77a66cde601bb45.mkv", "Full Metal Jacket (1987)/1776f8e2fb614a6fb77a66cde601bb45.mkv",
(1920, 1080), (1920, 1080),
5719802,
)]), )]),
], ],
}; };
@ -235,10 +252,53 @@ fn test_fullmetal() -> Result<(), Box<dyn Error>> {
build_movie(vec![( build_movie(vec![(
"Full Metal Jacket (1987)/1776f8e2fb614a6fb77a66cde601bb45.mkv", "Full Metal Jacket (1987)/1776f8e2fb614a6fb77a66cde601bb45.mkv",
(1920, 1080), (1920, 1080),
5719802,
)]), )]),
vec![build_movie(vec![( vec![build_movie(vec![(
"Full Metal Jacket (1987)/Full Metal Jacket.mp4", "Full Metal Jacket (1987)/Full Metal Jacket.mp4",
(1280, 720), (1280, 720),
2581935,
)])],
)];
validate_duplicates(got, want);
Ok(())
}
#[test]
fn test_keep_lower_res_higher_bit_rate() -> Result<(), Box<dyn Error>> {
let mut movies = Movies {
movies: vec![
build_movie(vec![(
"X Men The Last Stand (2006)/X.Men.The.Last.Stand.2006.1080p.BluRay.x264.DTS-ES.PRoDJi.mkv",
(1920, 800),
11349705,
)]),
build_movie(vec![(
"X Men The Last Stand (2006)/948f08a4ba784626ac13de77b77559dd.mkv",
(1920, 1080),
6574160,
)]),
],
};
movies.movies.sort_by(|a, b| {
a.files
.first()
.unwrap()
.0
.partial_cmp(&b.files.first().unwrap().0)
.unwrap()
});
let got = movies.duplicate_candidates();
let want = vec![(
build_movie(vec![(
"X Men The Last Stand (2006)/X.Men.The.Last.Stand.2006.1080p.BluRay.x264.DTS-ES.PRoDJi.mkv",
(1920, 800),
11349705,
)]),
vec![build_movie(vec![(
"X Men The Last Stand (2006)/948f08a4ba784626ac13de77b77559dd.mkv",
(1920, 1080),
6574160,
)])], )])],
)]; )];
validate_duplicates(got, want); validate_duplicates(got, want);