mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-07-08 01:32:41 +08:00
Add new option x264-profile
This commit is contained in:
@@ -318,6 +318,10 @@ pub async fn download_artwork(
|
||||
if helper.force_yuv420p() {
|
||||
options.set("force_yuv420p", "1", None)?;
|
||||
}
|
||||
let profile = helper.x264_profile();
|
||||
if !profile.is_auto() {
|
||||
options.set("profile", profile.as_str(), None)?;
|
||||
}
|
||||
let frames = UgoiraFrames::from_json(&ugoira_data["frames"])?;
|
||||
let output_file_name = base.join(format!("{}.mp4", id));
|
||||
convert_ugoira_to_mp4(
|
||||
|
||||
@@ -15,6 +15,8 @@ use crate::server::cors::parse_cors_entries;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::server::cors::CorsEntry;
|
||||
use crate::settings::SettingStore;
|
||||
#[cfg(feature = "ugoira")]
|
||||
use crate::ugoira::X264Profile;
|
||||
use std::convert::TryFrom;
|
||||
#[cfg(feature = "server")]
|
||||
use std::net::IpAddr;
|
||||
@@ -390,6 +392,22 @@ impl OptHelper {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "ugoira")]
|
||||
/// Return the x264 profile when converting ugoira(GIF) to video.
|
||||
pub fn x264_profile(&self) -> X264Profile {
|
||||
match self.opt.get_ref().x264_profile {
|
||||
Some(r) => {
|
||||
return r;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if self.settings.get_ref().have("x264-profile") {
|
||||
let v = self.settings.get_ref().get("server").unwrap();
|
||||
return X264Profile::from_str(v.as_str().unwrap()).unwrap();
|
||||
}
|
||||
X264Profile::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OptHelper {
|
||||
|
||||
54
src/opts.rs
54
src/opts.rs
@@ -3,6 +3,8 @@ use crate::gettext;
|
||||
use crate::list::NonTailList;
|
||||
use crate::pixiv_link::PixivID;
|
||||
use crate::retry_interval::parse_retry_interval_from_str;
|
||||
#[cfg(feature = "ugoira")]
|
||||
use crate::ugoira::X264Profile;
|
||||
use crate::utils::check_file_exists;
|
||||
use crate::utils::get_exe_path_else_current;
|
||||
use getopts::HasArg;
|
||||
@@ -101,6 +103,9 @@ pub struct CommandOpts {
|
||||
#[cfg(feature = "ugoira")]
|
||||
/// Whether to force yuv420p as output pixel format when converting ugoira(GIF) to video.
|
||||
pub force_yuv420p: Option<bool>,
|
||||
#[cfg(feature = "ugoira")]
|
||||
/// The x264 profile when converting ugoira(GIF) to video.
|
||||
pub x264_profile: Option<X264Profile>,
|
||||
}
|
||||
|
||||
impl CommandOpts {
|
||||
@@ -134,6 +139,8 @@ impl CommandOpts {
|
||||
max_download_post_tasks: None,
|
||||
#[cfg(feature = "ugoira")]
|
||||
force_yuv420p: None,
|
||||
#[cfg(feature = "ugoira")]
|
||||
x264_profile: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +293,19 @@ pub fn parse_nonempty_usize<T: AsRef<str>>(s: Option<T>) -> Result<Option<usize>
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ugoira")]
|
||||
pub fn parse_x264_profile<S: AsRef<str>>(
|
||||
s: Option<S>,
|
||||
) -> Result<Option<X264Profile>, &'static str> {
|
||||
match s {
|
||||
Some(s) => {
|
||||
let s = s.as_ref();
|
||||
Ok(Some(X264Profile::from_str(s)?))
|
||||
}
|
||||
None => Ok(Some(X264Profile::default())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse optional option
|
||||
/// * `opts` - The result of options. See [getopts::Matches].
|
||||
/// * `key` - The key of the option.
|
||||
@@ -475,6 +495,21 @@ pub fn parse_cmd() -> Option<CommandOpts> {
|
||||
HasArg::Maybe,
|
||||
getopts::Occur::Optional,
|
||||
);
|
||||
#[cfg(feature = "ugoira")]
|
||||
opts.opt(
|
||||
"",
|
||||
"x264-profile",
|
||||
format!(
|
||||
"{} ({} {})",
|
||||
gettext("The x264 profile when converting ugoira(GIF) to video."),
|
||||
gettext("Default:"),
|
||||
"auto"
|
||||
)
|
||||
.as_str(),
|
||||
"PROFILE",
|
||||
HasArg::Maybe,
|
||||
getopts::Occur::Optional,
|
||||
);
|
||||
let result = match opts.parse(&argv[1..]) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
@@ -739,6 +774,25 @@ pub fn parse_cmd() -> Option<CommandOpts> {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ugoira")]
|
||||
match parse_optional_opt(
|
||||
&result,
|
||||
"x264-profile",
|
||||
X264Profile::default(),
|
||||
parse_x264_profile,
|
||||
) {
|
||||
Ok(r) => re.as_mut().unwrap().x264_profile = r,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"{} {}",
|
||||
gettext("Failed to parse <opt>:")
|
||||
.replace("<opt>", "x264-profile")
|
||||
.as_str(),
|
||||
e
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
re
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,12 @@ use crate::opt::proxy::check_proxy;
|
||||
use crate::opt::size::parse_u32_size;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::server::cors::parse_cors_entries;
|
||||
#[cfg(feature = "ugoira")]
|
||||
use crate::ugoira::X264Profile;
|
||||
use json::JsonValue;
|
||||
#[cfg(feature = "server")]
|
||||
use std::net::SocketAddr;
|
||||
#[cfg(feature = "server")]
|
||||
#[cfg(any(feature = "server", feature = "ugoira"))]
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn get_settings_list() -> Vec<SettingDes> {
|
||||
@@ -43,8 +45,10 @@ pub fn get_settings_list() -> Vec<SettingDes> {
|
||||
SettingDes::new("max-download-tasks", gettext("The maximum number of tasks to download files at the same time."), JsonValueType::Number, Some(check_nozero_usize)).unwrap(),
|
||||
SettingDes::new("download-multiple-posts", gettext("Download multiple posts/artworks at the same time."), JsonValueType::Boolean, None).unwrap(),
|
||||
SettingDes::new("max-download-post-tasks", gettext("The maximum number of tasks to download posts/artworks at the same time."), JsonValueType::Number, Some(check_nozero_usize)).unwrap(),
|
||||
#[cfg(feature = "ugora")]
|
||||
#[cfg(feature = "ugoira")]
|
||||
SettingDes::new("force-yuv420p", gettext("Force yuv420p as output pixel format when converting ugoira(GIF) to video."), JsonValueType::Boolean, None).unwrap(),
|
||||
#[cfg(feature = "ugoira")]
|
||||
SettingDes::new("x264-profile", gettext("The x264 profile when converting ugoira(GIF) to video."), JsonValueType::Str, Some(check_x264_profile)).unwrap(),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -100,6 +104,14 @@ fn check_user_or_not(obj: &JsonValue) -> bool {
|
||||
r.is_ok()
|
||||
}
|
||||
|
||||
#[cfg(feature = "ugoira")]
|
||||
fn check_x264_profile(obj: &JsonValue) -> bool {
|
||||
match obj.as_str() {
|
||||
Some(profile) => X264Profile::from_str(profile).is_ok(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_settings_list() {
|
||||
get_settings_list();
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::ext::rawhandle::ToRawHandle;
|
||||
use crate::ext::try_err::TryErr;
|
||||
use crate::gettext;
|
||||
use std::convert::AsRef;
|
||||
use std::convert::TryFrom;
|
||||
use std::default::Default;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::OsStr;
|
||||
@@ -22,6 +23,7 @@ use std::os::raw::c_int;
|
||||
use std::os::raw::c_void;
|
||||
#[cfg(test)]
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
const UGOIRA_OK: c_int = _ugoira::UGOIRA_OK as c_int;
|
||||
@@ -369,6 +371,80 @@ impl ToRawHandle<_ugoira::AVDictionary> for AVDict {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// H.264 profile
|
||||
pub enum X264Profile {
|
||||
/// Selected by x264.
|
||||
Auto,
|
||||
/// No interlaced, No lossless.
|
||||
Baseline,
|
||||
/// No lossless.
|
||||
Main,
|
||||
/// No lossless.
|
||||
High,
|
||||
/// No lossless. Support for bit depth 8-10.
|
||||
High10,
|
||||
/// No lossless. Support for bit depth 8-10. Support for 4:2:0/4:2:2 chroma subsampling.
|
||||
High422,
|
||||
/// No lossless. Support for bit depth 8-10. Support for 4:2:0/4:2:2/4:4:4 chroma subsampling.
|
||||
High444,
|
||||
}
|
||||
|
||||
impl X264Profile {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
X264Profile::Auto => "",
|
||||
X264Profile::Baseline => "baseline",
|
||||
X264Profile::Main => "main",
|
||||
X264Profile::High => "high",
|
||||
X264Profile::High10 => "high10",
|
||||
X264Profile::High422 => "high422",
|
||||
X264Profile::High444 => "high444",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_auto(&self) -> bool {
|
||||
matches!(self, X264Profile::Auto)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for X264Profile {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for X264Profile {
|
||||
fn default() -> Self {
|
||||
X264Profile::Auto
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for X264Profile {
|
||||
type Err = &'static str;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.to_ascii_lowercase();
|
||||
match s.as_str() {
|
||||
"auto" => Ok(X264Profile::Auto),
|
||||
"baseline" => Ok(X264Profile::Baseline),
|
||||
"main" => Ok(X264Profile::Main),
|
||||
"high" => Ok(X264Profile::High),
|
||||
"high10" => Ok(X264Profile::High10),
|
||||
"high422" => Ok(X264Profile::High422),
|
||||
"high444" => Ok(X264Profile::High444),
|
||||
_ => Err(gettext("Unknown H.264 profile.")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for X264Profile {
|
||||
type Error = &'static str;
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
X264Profile::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_ugoira_to_mp4<
|
||||
S: AsRef<OsStr> + ?Sized,
|
||||
D: AsRef<OsStr> + ?Sized,
|
||||
|
||||
Reference in New Issue
Block a user