Add new option x264-profile

This commit is contained in:
2022-07-23 02:51:50 +00:00
committed by GitHub
parent 259f48087e
commit e85fc0911c
5 changed files with 166 additions and 2 deletions

View File

@@ -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(

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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();

View File

@@ -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,