2021-08-28 22:15:14 +00:00
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
pub(crate) enum MagickError {
|
|
|
|
#[error("{0}")]
|
|
|
|
IO(#[from] std::io::Error),
|
|
|
|
|
|
|
|
#[error("Magick command failed")]
|
|
|
|
Status,
|
|
|
|
|
|
|
|
#[error("Magick semaphore is closed")]
|
|
|
|
Closed,
|
|
|
|
|
|
|
|
#[error("Invalid format")]
|
|
|
|
Format,
|
|
|
|
}
|
|
|
|
|
2021-08-29 01:37:53 +00:00
|
|
|
pub(crate) enum ValidInputType {
|
|
|
|
Mp4,
|
|
|
|
Gif,
|
2021-08-28 22:15:14 +00:00
|
|
|
Png,
|
2021-08-29 01:37:53 +00:00
|
|
|
Jpeg,
|
2021-08-28 22:15:14 +00:00
|
|
|
Webp,
|
|
|
|
}
|
|
|
|
|
|
|
|
static MAX_CONVERSIONS: once_cell::sync::OnceCell<tokio::sync::Semaphore> =
|
|
|
|
once_cell::sync::OnceCell::new();
|
|
|
|
|
|
|
|
fn semaphore() -> &'static tokio::sync::Semaphore {
|
|
|
|
MAX_CONVERSIONS
|
|
|
|
.get_or_init(|| tokio::sync::Semaphore::new(num_cpus::get().saturating_sub(1).max(1)))
|
2021-08-26 02:46:11 +00:00
|
|
|
}
|
|
|
|
|
2021-08-28 22:15:14 +00:00
|
|
|
pub(crate) async fn convert_file<P1, P2>(
|
|
|
|
from: P1,
|
|
|
|
to: P2,
|
|
|
|
format: crate::config::Format,
|
|
|
|
) -> Result<(), MagickError>
|
|
|
|
where
|
|
|
|
P1: AsRef<std::path::Path>,
|
|
|
|
P2: AsRef<std::path::Path>,
|
|
|
|
{
|
|
|
|
let mut output_file = std::ffi::OsString::new();
|
|
|
|
output_file.extend([format.to_magick_format().as_ref(), ":".as_ref()]);
|
|
|
|
output_file.extend([to.as_ref().as_ref()]);
|
|
|
|
|
|
|
|
let permit = semaphore().acquire().await?;
|
|
|
|
|
|
|
|
let status = tokio::process::Command::new("magick")
|
|
|
|
.arg("convert")
|
|
|
|
.arg(&from.as_ref())
|
|
|
|
.arg(&output_file)
|
|
|
|
.spawn()?
|
|
|
|
.wait()
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
drop(permit);
|
|
|
|
|
|
|
|
if !status.success() {
|
|
|
|
return Err(MagickError::Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-08-29 01:37:53 +00:00
|
|
|
pub(crate) async fn input_type<P>(file: &P) -> Result<ValidInputType, MagickError>
|
2021-08-28 22:15:14 +00:00
|
|
|
where
|
|
|
|
P: AsRef<std::path::Path>,
|
|
|
|
{
|
|
|
|
let permit = semaphore().acquire().await?;
|
|
|
|
|
|
|
|
let output = tokio::process::Command::new("magick")
|
|
|
|
.args([&"identify", &"-ping", &"-format", &"%m\n"])
|
|
|
|
.arg(&file.as_ref())
|
|
|
|
.output()
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
drop(permit);
|
|
|
|
|
|
|
|
let s = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
2021-08-29 01:37:53 +00:00
|
|
|
let mut lines = s.lines();
|
|
|
|
let first = lines.next();
|
|
|
|
|
|
|
|
let opt = lines.fold(first, |acc, item| match acc {
|
|
|
|
Some(prev) if prev == item => Some(prev),
|
|
|
|
_ => None,
|
|
|
|
});
|
|
|
|
|
|
|
|
match opt {
|
|
|
|
Some("MP4") => Ok(ValidInputType::Mp4),
|
|
|
|
Some("GIF") => Ok(ValidInputType::Gif),
|
|
|
|
Some("PNG") => Ok(ValidInputType::Png),
|
|
|
|
Some("JPEG") => Ok(ValidInputType::Jpeg),
|
|
|
|
Some("WEBP") => Ok(ValidInputType::Webp),
|
|
|
|
_ => Err(MagickError::Format),
|
2021-08-28 22:15:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) async fn process_image<P1, P2>(
|
|
|
|
input: P1,
|
|
|
|
output: P2,
|
|
|
|
args: Vec<String>,
|
|
|
|
format: crate::config::Format,
|
|
|
|
) -> Result<(), MagickError>
|
|
|
|
where
|
|
|
|
P1: AsRef<std::path::Path>,
|
|
|
|
P2: AsRef<std::path::Path>,
|
|
|
|
{
|
|
|
|
let mut output_file = std::ffi::OsString::new();
|
|
|
|
output_file.extend([format!("{}:", format.to_magick_format()).as_ref()]);
|
|
|
|
output_file.extend([output.as_ref().as_ref()]);
|
|
|
|
|
|
|
|
let permit = semaphore().acquire().await?;
|
|
|
|
|
|
|
|
let status = tokio::process::Command::new("magick")
|
|
|
|
.arg(&"convert")
|
|
|
|
.arg(&input.as_ref())
|
|
|
|
.args(args)
|
|
|
|
.arg(&output.as_ref())
|
|
|
|
.spawn()?
|
|
|
|
.wait()
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
drop(permit);
|
|
|
|
|
|
|
|
if !status.success() {
|
|
|
|
return Err(MagickError::Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2021-08-26 02:46:11 +00:00
|
|
|
}
|
|
|
|
|
2021-08-28 22:15:14 +00:00
|
|
|
impl From<tokio::sync::AcquireError> for MagickError {
|
|
|
|
fn from(_: tokio::sync::AcquireError) -> MagickError {
|
|
|
|
MagickError::Closed
|
|
|
|
}
|
2021-08-26 02:46:11 +00:00
|
|
|
}
|