mirror of
https://github.com/crunchy-labs/crunchy-cli.git
synced 2026-01-21 04:02:00 -06:00
Add --special-output and --skip-specials flag
This commit is contained in:
parent
5a3a304443
commit
787d8ab02c
5 changed files with 106 additions and 10 deletions
|
|
@ -54,6 +54,11 @@ pub struct Archive {
|
|||
{episode_id} → ID of the episode")]
|
||||
#[arg(short, long, default_value = "{title}.mkv")]
|
||||
pub(crate) output: String,
|
||||
#[arg(help = "Name of the output file if the episode is a special")]
|
||||
#[arg(long_help = "Name of the output file if the episode is a special. \
|
||||
If not set, the '-o'/'--output' flag will be used as name template")]
|
||||
#[arg(long)]
|
||||
pub(crate) special_output: Option<String>,
|
||||
|
||||
#[arg(help = "Video resolution")]
|
||||
#[arg(long_help = "The video resolution.\
|
||||
|
|
@ -95,6 +100,9 @@ pub struct Archive {
|
|||
#[arg(help = "Skip files which are already existing")]
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub(crate) skip_existing: bool,
|
||||
#[arg(help = "Skip special episodes")]
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub(crate) skip_specials: bool,
|
||||
|
||||
#[arg(help = "Skip any interactive input")]
|
||||
#[arg(short, long, default_value_t = false)]
|
||||
|
|
@ -123,6 +131,17 @@ impl Execute for Archive {
|
|||
&& self.output != "-"
|
||||
{
|
||||
bail!("File extension is not '.mkv'. Currently only matroska / '.mkv' files are supported")
|
||||
} else if let Some(special_output) = &self.special_output {
|
||||
if PathBuf::from(special_output)
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
!= "mkv"
|
||||
&& !is_special_file(special_output)
|
||||
&& special_output != "-"
|
||||
{
|
||||
bail!("File extension for special episodes is not '.mkv'. Currently only matroska / '.mkv' files are supported")
|
||||
}
|
||||
}
|
||||
|
||||
self.audio = all_locale_in_locales(self.audio.clone());
|
||||
|
|
@ -147,7 +166,8 @@ impl Execute for Archive {
|
|||
|
||||
for (i, (media_collection, url_filter)) in parsed_urls.into_iter().enumerate() {
|
||||
let progress_handler = progress!("Fetching series details");
|
||||
let single_format_collection = ArchiveFilter::new(url_filter, self.clone(), !self.yes)
|
||||
let single_format_collection =
|
||||
ArchiveFilter::new(url_filter, self.clone(), !self.yes, self.skip_specials)
|
||||
.visit(media_collection)
|
||||
.await?;
|
||||
|
||||
|
|
@ -175,7 +195,15 @@ impl Execute for Archive {
|
|||
downloader.add_format(download_format)
|
||||
}
|
||||
|
||||
let formatted_path = format.format_path((&self.output).into());
|
||||
let formatted_path = if format.is_special() {
|
||||
format.format_path(
|
||||
self.special_output
|
||||
.as_ref()
|
||||
.map_or((&self.output).into(), |so| so.into()),
|
||||
)
|
||||
} else {
|
||||
format.format_path((&self.output).into())
|
||||
};
|
||||
let (path, changed) = free_file(formatted_path.clone());
|
||||
|
||||
if changed && self.skip_existing {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ pub(crate) struct ArchiveFilter {
|
|||
url_filter: UrlFilter,
|
||||
archive: Archive,
|
||||
interactive_input: bool,
|
||||
skip_special: bool,
|
||||
season_episodes: HashMap<String, Vec<Episode>>,
|
||||
season_subtitles_missing: Vec<u32>,
|
||||
season_sorting: Vec<String>,
|
||||
|
|
@ -25,11 +26,17 @@ pub(crate) struct ArchiveFilter {
|
|||
}
|
||||
|
||||
impl ArchiveFilter {
|
||||
pub(crate) fn new(url_filter: UrlFilter, archive: Archive, interactive_input: bool) -> Self {
|
||||
pub(crate) fn new(
|
||||
url_filter: UrlFilter,
|
||||
archive: Archive,
|
||||
interactive_input: bool,
|
||||
skip_special: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
url_filter,
|
||||
archive,
|
||||
interactive_input,
|
||||
skip_special,
|
||||
season_episodes: HashMap::new(),
|
||||
season_subtitles_missing: vec![],
|
||||
season_sorting: vec![],
|
||||
|
|
@ -246,6 +253,13 @@ impl Filter for ArchiveFilter {
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
// skip the episode if it's a special
|
||||
if self.skip_special
|
||||
&& (episode.sequence_number == 0.0 || episode.sequence_number.fract() != 0.0)
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut episodes = vec![];
|
||||
if !matches!(self.visited, Visited::Series) && !matches!(self.visited, Visited::Season) {
|
||||
if self.archive.audio.contains(&episode.audio_locale) {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@ pub struct Download {
|
|||
{episode_id} → ID of the episode")]
|
||||
#[arg(short, long, default_value = "{title}.mp4")]
|
||||
pub(crate) output: String,
|
||||
#[arg(help = "Name of the output file if the episode is a special")]
|
||||
#[arg(long_help = "Name of the output file if the episode is a special. \
|
||||
If not set, the '-o'/'--output' flag will be used as name template")]
|
||||
#[arg(long)]
|
||||
pub(crate) special_output: Option<String>,
|
||||
|
||||
#[arg(help = "Video resolution")]
|
||||
#[arg(long_help = "The video resolution.\
|
||||
|
|
@ -73,6 +78,9 @@ pub struct Download {
|
|||
#[arg(help = "Skip files which are already existing")]
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub(crate) skip_existing: bool,
|
||||
#[arg(help = "Skip special episodes")]
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub(crate) skip_specials: bool,
|
||||
|
||||
#[arg(help = "Skip any interactive input")]
|
||||
#[arg(short, long, default_value_t = false)]
|
||||
|
|
@ -116,6 +124,25 @@ impl Execute for Download {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(special_output) = &self.special_output {
|
||||
if Path::new(special_output)
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.is_empty()
|
||||
&& !is_special_file(special_output)
|
||||
&& special_output != "-"
|
||||
{
|
||||
bail!("No file extension found. Please specify a file extension (via `--special-output`) for the output file")
|
||||
}
|
||||
if let Some(ext) = Path::new(special_output).extension() {
|
||||
if self.force_hardsub {
|
||||
warn!("Hardsubs are forced for special episodes. Adding subtitles may take a while")
|
||||
} else if !["mkv", "mov", "mp4"].contains(&ext.to_string_lossy().as_ref()) {
|
||||
warn!("Detected a container which does not support softsubs. Adding subtitles for special episodes may take a while")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +162,8 @@ impl Execute for Download {
|
|||
|
||||
for (i, (media_collection, url_filter)) in parsed_urls.into_iter().enumerate() {
|
||||
let progress_handler = progress!("Fetching series details");
|
||||
let single_format_collection = DownloadFilter::new(url_filter, self.clone(), !self.yes)
|
||||
let single_format_collection =
|
||||
DownloadFilter::new(url_filter, self.clone(), !self.yes, self.skip_specials)
|
||||
.visit(media_collection)
|
||||
.await?;
|
||||
|
||||
|
|
@ -167,7 +195,15 @@ impl Execute for Download {
|
|||
let mut downloader = download_builder.clone().build();
|
||||
downloader.add_format(download_format);
|
||||
|
||||
let formatted_path = format.format_path((&self.output).into());
|
||||
let formatted_path = if format.is_special() {
|
||||
format.format_path(
|
||||
self.special_output
|
||||
.as_ref()
|
||||
.map_or((&self.output).into(), |so| so.into()),
|
||||
)
|
||||
} else {
|
||||
format.format_path((&self.output).into())
|
||||
};
|
||||
let (path, changed) = free_file(formatted_path.clone());
|
||||
|
||||
if changed && self.skip_existing {
|
||||
|
|
|
|||
|
|
@ -12,17 +12,24 @@ pub(crate) struct DownloadFilter {
|
|||
url_filter: UrlFilter,
|
||||
download: Download,
|
||||
interactive_input: bool,
|
||||
skip_special: bool,
|
||||
season_episodes: HashMap<u32, Vec<Episode>>,
|
||||
season_subtitles_missing: Vec<u32>,
|
||||
season_visited: bool,
|
||||
}
|
||||
|
||||
impl DownloadFilter {
|
||||
pub(crate) fn new(url_filter: UrlFilter, download: Download, interactive_input: bool) -> Self {
|
||||
pub(crate) fn new(
|
||||
url_filter: UrlFilter,
|
||||
download: Download,
|
||||
interactive_input: bool,
|
||||
skip_special: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
url_filter,
|
||||
download,
|
||||
interactive_input,
|
||||
skip_special,
|
||||
season_episodes: HashMap::new(),
|
||||
season_subtitles_missing: vec![],
|
||||
season_visited: false,
|
||||
|
|
@ -132,6 +139,13 @@ impl Filter for DownloadFilter {
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
// skip the episode if it's a special
|
||||
if self.skip_special
|
||||
&& (episode.sequence_number == 0.0 || episode.sequence_number.fract() != 0.0)
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// check if the audio locale is correct.
|
||||
// should only be incorrect if the console input was a episode url. otherwise
|
||||
// `DownloadFilter::visit_season` returns the correct episodes with matching audio
|
||||
|
|
|
|||
|
|
@ -473,6 +473,10 @@ impl Format {
|
|||
tab_info!("FPS: {:.2}", self.fps)
|
||||
}
|
||||
|
||||
pub fn is_special(&self) -> bool {
|
||||
self.sequence_number == 0.0 || self.sequence_number.fract() != 0.0
|
||||
}
|
||||
|
||||
pub fn has_relative_fmt<S: AsRef<str>>(s: S) -> bool {
|
||||
return s.as_ref().contains("{relative_episode_number}")
|
||||
|| s.as_ref().contains("{relative_sequence_number}");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue