Fix interactive season choosing false-positive triggering

This commit is contained in:
ByteDream 2023-01-09 16:55:10 +01:00
parent 4b33ef02c6
commit 12be16417f
8 changed files with 157 additions and 197 deletions

View file

@ -1,7 +1,10 @@
use crate::cli::log::tab_info;
use crate::cli::utils::{download_segments, find_resolution, FFmpegPreset, find_multiple_seasons_with_same_number, interactive_season_choosing};
use crate::cli::utils::{
download_segments, find_multiple_seasons_with_same_number, find_resolution,
interactive_season_choosing, FFmpegPreset,
};
use crate::utils::context::Context;
use crate::utils::format::{Format, format_path};
use crate::utils::format::{format_path, Format};
use crate::utils::log::progress;
use crate::utils::os::{free_file, has_ffmpeg, is_special_file, tempfile};
use crate::utils::parse::{parse_url, UrlFilter};
@ -240,11 +243,16 @@ impl Execute for Archive {
for (formats, mut subtitles) in archive_formats {
let (primary, additionally) = formats.split_first().unwrap();
let path = free_file(format_path(if self.output.is_empty() {
"{title}.mkv"
} else {
&self.output
}.into(), &primary, true));
let path = free_file(format_path(
if self.output.is_empty() {
"{title}.mkv"
} else {
&self.output
}
.into(),
&primary,
true,
));
info!(
"Downloading {} to '{}'",
@ -387,15 +395,6 @@ async fn formats_from_series(
let mut result: BTreeMap<u32, BTreeMap<u32, (Vec<Format>, Vec<Subtitle>)>> = BTreeMap::new();
let mut primary_season = true;
for season in seasons {
if !url_filter.is_season_valid(season.metadata.season_number)
|| !archive
.locale
.iter()
.any(|l| season.metadata.audio_locales.contains(l))
{
continue;
}
for episode in season.episodes().await? {
if !url_filter.is_episode_valid(
episode.metadata.episode_number,

View file

@ -1,7 +1,10 @@
use crate::cli::log::tab_info;
use crate::cli::utils::{download_segments, find_resolution, FFmpegPreset, interactive_season_choosing, find_multiple_seasons_with_same_number};
use crate::cli::utils::{
download_segments, find_multiple_seasons_with_same_number, find_resolution,
interactive_season_choosing, FFmpegPreset,
};
use crate::utils::context::Context;
use crate::utils::format::{Format, format_path};
use crate::utils::format::{format_path, Format};
use crate::utils::log::progress;
use crate::utils::os::{free_file, has_ffmpeg, is_special_file};
use crate::utils::parse::{parse_url, UrlFilter};
@ -206,11 +209,16 @@ impl Execute for Download {
}
for format in formats {
let path = free_file(format_path(if self.output.is_empty() {
"{title}.mkv"
} else {
&self.output
}.into(), &format, true));
let path = free_file(format_path(
if self.output.is_empty() {
"{title}.mkv"
} else {
&self.output
}
.into(),
&format,
true,
));
info!(
"Downloading {} to '{}'",

View file

@ -1,15 +1,16 @@
use crate::utils::context::Context;
use anyhow::{bail, Result};
use crunchyroll_rs::media::{Resolution, VariantData, VariantSegment};
use crunchyroll_rs::{Media, Season};
use indicatif::{ProgressBar, ProgressFinish, ProgressStyle};
use lazy_static::lazy_static;
use log::{debug, LevelFilter};
use regex::Regex;
use std::borrow::{Borrow, BorrowMut};
use std::collections::BTreeMap;
use std::io::{BufRead, Write};
use std::sync::{mpsc, Arc, Mutex};
use std::time::Duration;
use crunchyroll_rs::{Media, Season};
use regex::Regex;
use tokio::task::JoinSet;
pub fn find_resolution(
@ -111,7 +112,7 @@ pub async fn download_segments(
};
buf = VariantSegment::decrypt(buf.borrow_mut(), segment.key)?.to_vec();
let mut c = thread_count.lock().unwrap();
debug!(
"Downloaded and decrypted segment [{}/{} {:.2}%] {}",
@ -120,14 +121,14 @@ pub async fn download_segments(
((*c + 1) as f64 / total_segments as f64) * 100f64,
segment.url
);
thread_sender.send((num as i32 + (i * cpus) as i32, buf))?;
*c += 1;
}
Ok(())
};
let result = download().await;
if result.is_err() {
@ -149,7 +150,7 @@ pub async fn download_segments(
for (pos, bytes) in receiver.iter() {
// if the position is lower than 0, an error occured in the sending download thread
if pos < 0 {
break
break;
}
if let Some(p) = &progress {
@ -330,6 +331,10 @@ impl FFmpegPreset {
}
}
lazy_static! {
static ref DUPLICATED_SEASONS_MULTILANG_REGEX: Regex = Regex::new(r"(-castilian|-english|-english-in|-french|-german|-hindi|-italian|-portuguese|-russian|-spanish)$").unwrap();
}
pub(crate) fn find_multiple_seasons_with_same_number(seasons: &Vec<Media<Season>>) -> Vec<u32> {
let mut seasons_map: BTreeMap<u32, u32> = BTreeMap::new();
for season in seasons {
@ -342,7 +347,25 @@ pub(crate) fn find_multiple_seasons_with_same_number(seasons: &Vec<Media<Season>
seasons_map
.into_iter()
.filter_map(|(k, v)| if v > 1 { Some(k) } else { None })
.filter_map(|(k, v)| {
if v > 1 {
// check if the different seasons are actual the same but with different dub languages
let mut multilang_season_vec: Vec<String> = seasons
.iter()
.map(|s| {
DUPLICATED_SEASONS_MULTILANG_REGEX
.replace(s.slug_title.trim_end_matches("-dub"), "")
.to_string()
})
.collect();
multilang_season_vec.dedup();
if multilang_season_vec.len() > 1 {
return Some(k);
}
}
None
})
.collect()
}
@ -363,6 +386,22 @@ pub(crate) fn interactive_season_choosing(seasons: Vec<Media<Season>>) -> Vec<Me
if season_vec.len() == 1 {
continue;
}
// check if the different seasons are actual the same but with different dub languages
let mut multilang_season_vec: Vec<String> = season_vec
.iter()
.map(|s| {
DUPLICATED_SEASONS_MULTILANG_REGEX
.replace(s.slug_title.trim_end_matches("-dub"), "")
.to_string()
})
.collect();
multilang_season_vec.dedup();
if multilang_season_vec.len() == 1 {
continue;
}
println!(":: Found multiple seasons for season number {}", num);
println!(":: Select the number of the seasons you want to download (eg \"1 2 4\", \"1-3\", \"1-3 5\"):");
for (i, season) in season_vec.iter().enumerate() {
@ -372,7 +411,8 @@ pub(crate) fn interactive_season_choosing(seasons: Vec<Media<Season>>) -> Vec<Me
let _ = write!(stdout, ":: => ");
let _ = stdout.flush();
let mut user_input = String::new();
std::io::stdin().lock()
std::io::stdin()
.lock()
.read_line(&mut user_input)
.expect("cannot open stdin");
@ -400,13 +440,7 @@ pub(crate) fn interactive_season_choosing(seasons: Vec<Media<Season>>) -> Vec<Me
season_vec
.iter()
.enumerate()
.filter_map(|(i, _)| {
if i >= from && i <= to {
Some(i)
} else {
None
}
})
.filter_map(|(i, _)| if i >= from && i <= to { Some(i) } else { None })
.collect::<Vec<usize>>(),
)
}
@ -430,4 +464,3 @@ pub(crate) fn interactive_season_choosing(seasons: Vec<Media<Season>>) -> Vec<Me
.flatten()
.collect::<Vec<Media<Season>>>()
}

View file

@ -1,6 +1,6 @@
use std::path::PathBuf;
use crunchyroll_rs::media::VariantData;
use crunchyroll_rs::{Episode, Locale, Media, Movie};
use std::path::PathBuf;
use std::time::Duration;
#[derive(Clone)]
@ -76,31 +76,34 @@ pub fn format_path(path: PathBuf, format: &Format, sanitize: bool) -> PathBuf {
let as_string = path.to_string_lossy().to_string();
PathBuf::from(as_string.replace("{title}", &sanitize_func(&format.title))
.replace("{series_name}", &sanitize_func(&format.series_name))
.replace("{season_name}", &sanitize_func(&format.season_title))
.replace("{audio}", &sanitize_func(&format.audio.to_string()))
.replace(
"{resolution}",
&sanitize_func(&format.stream.resolution.to_string()),
)
.replace(
"{padded_season_number}",
&sanitize_func(&format!("{:0>2}", format.season_number.to_string())),
)
.replace(
"{season_number}",
&sanitize_func(&format.season_number.to_string()),
)
.replace(
"{padded_episode_number}",
&sanitize_func(&format!("{:0>2}", format.number.to_string())),
)
.replace(
"{episode_number}",
&sanitize_func(&format.number.to_string()),
)
.replace("{series_id}", &sanitize_func(&format.series_id))
.replace("{season_id}", &sanitize_func(&format.season_id))
.replace("{episode_id}", &sanitize_func(&format.id)))
PathBuf::from(
as_string
.replace("{title}", &sanitize_func(&format.title))
.replace("{series_name}", &sanitize_func(&format.series_name))
.replace("{season_name}", &sanitize_func(&format.season_title))
.replace("{audio}", &sanitize_func(&format.audio.to_string()))
.replace(
"{resolution}",
&sanitize_func(&format.stream.resolution.to_string()),
)
.replace(
"{padded_season_number}",
&sanitize_func(&format!("{:0>2}", format.season_number.to_string())),
)
.replace(
"{season_number}",
&sanitize_func(&format.season_number.to_string()),
)
.replace(
"{padded_episode_number}",
&sanitize_func(&format!("{:0>2}", format.number.to_string())),
)
.replace(
"{episode_number}",
&sanitize_func(&format.number.to_string()),
)
.replace("{series_id}", &sanitize_func(&format.series_id))
.replace("{season_id}", &sanitize_func(&format.season_id))
.replace("{episode_id}", &sanitize_func(&format.id)),
)
}

View file

@ -33,7 +33,9 @@ pub fn sort_formats_after_seasons(formats: Vec<Format>) -> Vec<Vec<Format>> {
// the season title is used as key instead of season number to distinguish duplicated season
// numbers which are actually two different seasons; season id is not used as this somehow
// messes up ordering when duplicated seasons exist
as_map.entry(format.season_title.clone()).or_insert_with(Vec::new);
as_map
.entry(format.season_title.clone())
.or_insert_with(Vec::new);
as_map.get_mut(&format.season_title).unwrap().push(format);
}