mirror of
https://github.com/crunchy-labs/crunchy-cli.git
synced 2026-01-21 12:12:00 -06:00
Fix interactive season choosing false-positive triggering
This commit is contained in:
parent
4b33ef02c6
commit
12be16417f
8 changed files with 157 additions and 197 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 '{}'",
|
||||
|
|
|
|||
|
|
@ -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>>>()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue