mirror of
https://git.asonix.dog/asonix/pict-rs
synced 2024-12-22 11:21:24 +00:00
Switch more commands to be driven via STDIN
This commit is contained in:
parent
eabd7ea228
commit
2074334131
6 changed files with 108 additions and 113 deletions
|
@ -1,15 +1,13 @@
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
|
|
||||||
use futures_core::Stream;
|
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use tokio_util::bytes::Bytes;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
details::Details,
|
details::Details,
|
||||||
error::{Error, UploadError},
|
error::{Error, UploadError},
|
||||||
formats::ProcessableFormat,
|
formats::ProcessableFormat,
|
||||||
magick::{MagickError, MAGICK_CONFIGURE_PATH, MAGICK_TEMPORARY_PATH},
|
magick::{MagickError, MAGICK_CONFIGURE_PATH, MAGICK_TEMPORARY_PATH},
|
||||||
process::{Process, ProcessRead},
|
process::Process,
|
||||||
repo::Alias,
|
repo::Alias,
|
||||||
state::State,
|
state::State,
|
||||||
store::Store,
|
store::Store,
|
||||||
|
@ -43,47 +41,44 @@ where
|
||||||
|
|
||||||
let stream = state.store.to_stream(&identifier, None, None).await?;
|
let stream = state.store.to_stream(&identifier, None, None).await?;
|
||||||
|
|
||||||
let process = read_rgba(
|
let blurhash = read_rgba_command(
|
||||||
state,
|
state,
|
||||||
input_details
|
input_details
|
||||||
.internal_format()
|
.internal_format()
|
||||||
.processable_format()
|
.processable_format()
|
||||||
.expect("not a video"),
|
.expect("not a video"),
|
||||||
stream,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?
|
||||||
|
.drive_with_stream(stream)
|
||||||
|
.with_stdout(|mut stdout| async move {
|
||||||
|
let mut encoder = blurhash_update::Encoder::auto(blurhash_update::ImageBounds {
|
||||||
|
width: input_details.width() as _,
|
||||||
|
height: input_details.height() as _,
|
||||||
|
});
|
||||||
|
|
||||||
let blurhash = process
|
let mut buf = [0u8; 1024 * 8];
|
||||||
.with_stdout(|mut stdout| async move {
|
|
||||||
let mut encoder = blurhash_update::Encoder::auto(blurhash_update::ImageBounds {
|
|
||||||
width: input_details.width() as _,
|
|
||||||
height: input_details.height() as _,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut buf = [0u8; 1024 * 8];
|
loop {
|
||||||
|
let n = stdout.read(&mut buf).await?;
|
||||||
|
|
||||||
loop {
|
if n == 0 {
|
||||||
let n = stdout.read(&mut buf).await?;
|
break;
|
||||||
|
|
||||||
if n == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder.update(&buf[..n]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(encoder.finalize()) as std::io::Result<String>
|
encoder.update(&buf[..n]);
|
||||||
})
|
}
|
||||||
.await??;
|
|
||||||
|
Ok(encoder.finalize()) as std::io::Result<String>
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
Ok(blurhash)
|
Ok(blurhash)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_rgba<S>(
|
async fn read_rgba_command<S>(
|
||||||
state: &State<S>,
|
state: &State<S>,
|
||||||
input_format: ProcessableFormat,
|
input_format: ProcessableFormat,
|
||||||
stream: impl Stream<Item = std::io::Result<Bytes>> + 'static,
|
) -> Result<Process, MagickError> {
|
||||||
) -> Result<ProcessRead, MagickError> {
|
|
||||||
let temporary_path = state
|
let temporary_path = state
|
||||||
.tmp_dir
|
.tmp_dir
|
||||||
.tmp_folder()
|
.tmp_folder()
|
||||||
|
@ -104,7 +99,6 @@ async fn read_rgba<S>(
|
||||||
];
|
];
|
||||||
|
|
||||||
let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)?
|
let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)?
|
||||||
.stream_read(stream)
|
|
||||||
.add_extras(temporary_path);
|
.add_extras(temporary_path);
|
||||||
|
|
||||||
Ok(process)
|
Ok(process)
|
||||||
|
|
|
@ -41,7 +41,7 @@ pub(super) async fn check_reorient(
|
||||||
#[tracing::instrument(level = "trace", skip_all)]
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
async fn needs_reorienting(input: BytesStream, timeout: u64) -> Result<bool, ExifError> {
|
async fn needs_reorienting(input: BytesStream, timeout: u64) -> Result<bool, ExifError> {
|
||||||
let buf = Process::run("exiftool", &["-n", "-Orientation", "-"], &[], timeout)?
|
let buf = Process::run("exiftool", &["-n", "-Orientation", "-"], &[], timeout)?
|
||||||
.bytes_stream_read(input)
|
.drive_with_async_read(input.into_reader())
|
||||||
.into_string()
|
.into_string()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::{
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use streem::IntoStreamer;
|
use streem::IntoStreamer;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncReadExt, AsyncWriteExt},
|
io::{AsyncRead, AsyncReadExt, AsyncWriteExt},
|
||||||
process::{Child, ChildStdin, Command},
|
process::{Child, ChildStdin, Command},
|
||||||
};
|
};
|
||||||
use tokio_util::{bytes::Bytes, io::ReaderStream};
|
use tokio_util::{bytes::Bytes, io::ReaderStream};
|
||||||
|
@ -67,6 +67,7 @@ pub(crate) struct Process {
|
||||||
child: Child,
|
child: Child,
|
||||||
guard: MetricsGuard,
|
guard: MetricsGuard,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
|
extras: Box<dyn Extras>,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,11 +205,19 @@ impl Process {
|
||||||
command,
|
command,
|
||||||
guard,
|
guard,
|
||||||
timeout: Duration::from_secs(timeout),
|
timeout: Duration::from_secs(timeout),
|
||||||
|
extras: Box::new(()),
|
||||||
id: Uuid::now_v7(),
|
id: Uuid::now_v7(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_extras(self, extra: impl Extras + 'static) -> Self {
|
||||||
|
Self {
|
||||||
|
extras: Box::new((self.extras, extra)),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self), fields(command = %self.command, id = %self.id))]
|
#[tracing::instrument(skip(self), fields(command = %self.command, id = %self.id))]
|
||||||
pub(crate) async fn wait(self) -> Result<(), ProcessError> {
|
pub(crate) async fn wait(self) -> Result<(), ProcessError> {
|
||||||
let Process {
|
let Process {
|
||||||
|
@ -216,11 +225,17 @@ impl Process {
|
||||||
mut child,
|
mut child,
|
||||||
guard,
|
guard,
|
||||||
timeout,
|
timeout,
|
||||||
|
mut extras,
|
||||||
id: _,
|
id: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let res = child.wait().with_timeout(timeout).await;
|
let res = child.wait().with_timeout(timeout).await;
|
||||||
|
|
||||||
|
extras
|
||||||
|
.consume()
|
||||||
|
.await
|
||||||
|
.map_err(|e| ProcessError::Cleanup(command.clone(), e))?;
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(Ok(status)) if status.success() => {
|
Ok(Ok(status)) if status.success() => {
|
||||||
guard.disarm();
|
guard.disarm();
|
||||||
|
@ -236,10 +251,12 @@ impl Process {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn bytes_stream_read(self, input: BytesStream) -> ProcessRead {
|
pub(crate) fn drive_with_async_read(self, input: impl AsyncRead + 'static) -> ProcessRead {
|
||||||
self.spawn_fn(move |mut stdin| {
|
self.drive(move |mut stdin| {
|
||||||
async move {
|
async move {
|
||||||
match tokio::io::copy(&mut input.into_reader(), &mut stdin).await {
|
let mut input = std::pin::pin!(input);
|
||||||
|
|
||||||
|
match tokio::io::copy(&mut input, &mut stdin).await {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
// BrokenPipe means we finished reading from Stdout, so we don't need to write
|
// BrokenPipe means we finished reading from Stdout, so we don't need to write
|
||||||
// to stdin. We'll still error out if the command failed so treat this as a
|
// to stdin. We'll still error out if the command failed so treat this as a
|
||||||
|
@ -251,11 +268,11 @@ impl Process {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn stream_read<S>(self, input: S) -> ProcessRead
|
pub(crate) fn drive_with_stream<S>(self, input: S) -> ProcessRead
|
||||||
where
|
where
|
||||||
S: Stream<Item = std::io::Result<Bytes>> + 'static,
|
S: Stream<Item = std::io::Result<Bytes>> + 'static,
|
||||||
{
|
{
|
||||||
self.spawn_fn(move |mut stdin| async move {
|
self.drive(move |mut stdin| async move {
|
||||||
let stream = std::pin::pin!(input);
|
let stream = std::pin::pin!(input);
|
||||||
let mut stream = stream.into_streamer();
|
let mut stream = stream.into_streamer();
|
||||||
|
|
||||||
|
@ -272,13 +289,13 @@ impl Process {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read(self) -> ProcessRead {
|
pub(crate) fn read(self) -> ProcessRead {
|
||||||
self.spawn_fn(|_| async { Ok(()) })
|
self.drive(|_| async { Ok(()) })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unknown_lints)]
|
#[allow(unknown_lints)]
|
||||||
#[allow(clippy::let_with_type_underscore)]
|
#[allow(clippy::let_with_type_underscore)]
|
||||||
#[tracing::instrument(level = "trace", skip_all)]
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
fn spawn_fn<F, Fut>(self, f: F) -> ProcessRead
|
fn drive<F, Fut>(self, f: F) -> ProcessRead
|
||||||
where
|
where
|
||||||
F: FnOnce(ChildStdin) -> Fut + 'static,
|
F: FnOnce(ChildStdin) -> Fut + 'static,
|
||||||
Fut: Future<Output = std::io::Result<()>>,
|
Fut: Future<Output = std::io::Result<()>>,
|
||||||
|
@ -288,6 +305,7 @@ impl Process {
|
||||||
mut child,
|
mut child,
|
||||||
guard,
|
guard,
|
||||||
timeout,
|
timeout,
|
||||||
|
extras,
|
||||||
id,
|
id,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
@ -324,7 +342,7 @@ impl Process {
|
||||||
handle,
|
handle,
|
||||||
command,
|
command,
|
||||||
id,
|
id,
|
||||||
extras: Box::new(()),
|
extras,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,10 @@ use crate::{
|
||||||
AnimationFormat, AnimationOutput, ImageInput, ImageOutput, InputFile, InputVideoFormat,
|
AnimationFormat, AnimationOutput, ImageInput, ImageOutput, InputFile, InputVideoFormat,
|
||||||
InternalFormat,
|
InternalFormat,
|
||||||
},
|
},
|
||||||
process::ProcessRead,
|
process::{Process, ProcessRead},
|
||||||
state::State,
|
state::State,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub(crate) enum ValidationError {
|
pub(crate) enum ValidationError {
|
||||||
#[error("Too wide")]
|
#[error("Too wide")]
|
||||||
|
@ -74,15 +73,23 @@ pub(crate) async fn validate_bytes_stream<S>(
|
||||||
|
|
||||||
match &input {
|
match &input {
|
||||||
InputFile::Image(input) => {
|
InputFile::Image(input) => {
|
||||||
let (format, process_read) = process_image(state, bytes, *input, width, height).await?;
|
let (format, process) =
|
||||||
|
process_image_command(state, *input, bytes.len(), width, height).await?;
|
||||||
|
|
||||||
Ok((format, process_read))
|
Ok((format, process.drive_with_async_read(bytes.into_reader())))
|
||||||
}
|
}
|
||||||
InputFile::Animation(input) => {
|
InputFile::Animation(input) => {
|
||||||
let (format, process_read) =
|
let (format, process) = process_animation_command(
|
||||||
process_animation(state, bytes, *input, width, height, frames.unwrap_or(1)).await?;
|
state,
|
||||||
|
*input,
|
||||||
|
bytes.len(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
frames.unwrap_or(1),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok((format, process_read))
|
Ok((format, process.drive_with_async_read(bytes.into_reader())))
|
||||||
}
|
}
|
||||||
InputFile::Video(input) => {
|
InputFile::Video(input) => {
|
||||||
let (format, process_read) =
|
let (format, process_read) =
|
||||||
|
@ -93,14 +100,14 @@ pub(crate) async fn validate_bytes_stream<S>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(state, bytes), fields(len = bytes.len()))]
|
#[tracing::instrument(skip(state))]
|
||||||
async fn process_image<S>(
|
async fn process_image_command<S>(
|
||||||
state: &State<S>,
|
state: &State<S>,
|
||||||
bytes: BytesStream,
|
|
||||||
input: ImageInput,
|
input: ImageInput,
|
||||||
|
length: usize,
|
||||||
width: u16,
|
width: u16,
|
||||||
height: u16,
|
height: u16,
|
||||||
) -> Result<(InternalFormat, ProcessRead), Error> {
|
) -> Result<(InternalFormat, Process), Error> {
|
||||||
let validations = &state.config.media.image;
|
let validations = &state.config.media.image;
|
||||||
|
|
||||||
if width > validations.max_width {
|
if width > validations.max_width {
|
||||||
|
@ -112,7 +119,7 @@ async fn process_image<S>(
|
||||||
if u32::from(width) * u32::from(height) > validations.max_area {
|
if u32::from(width) * u32::from(height) > validations.max_area {
|
||||||
return Err(ValidationError::Area.into());
|
return Err(ValidationError::Area.into());
|
||||||
}
|
}
|
||||||
if bytes.len() > validations.max_file_size * MEGABYTES {
|
if length > validations.max_file_size * MEGABYTES {
|
||||||
return Err(ValidationError::Filesize.into());
|
return Err(ValidationError::Filesize.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,15 +128,15 @@ async fn process_image<S>(
|
||||||
needs_transcode,
|
needs_transcode,
|
||||||
} = input.build_output(validations.format);
|
} = input.build_output(validations.format);
|
||||||
|
|
||||||
let process_read = if needs_transcode {
|
let process = if needs_transcode {
|
||||||
let quality = validations.quality_for(format);
|
let quality = validations.quality_for(format);
|
||||||
|
|
||||||
magick::convert_image(state, input.format, format, quality, bytes).await?
|
magick::convert_image_command(state, input.format, format, quality).await?
|
||||||
} else {
|
} else {
|
||||||
exiftool::clear_metadata_bytes_read(bytes, state.config.media.process_timeout)?
|
exiftool::clear_metadata_command(state.config.media.process_timeout)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((InternalFormat::Image(format), process_read))
|
Ok((InternalFormat::Image(format), process))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_animation(
|
fn validate_animation(
|
||||||
|
@ -158,33 +165,33 @@ fn validate_animation(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(state, bytes), fields(len = bytes.len()))]
|
#[tracing::instrument(skip(state))]
|
||||||
async fn process_animation<S>(
|
async fn process_animation_command<S>(
|
||||||
state: &State<S>,
|
state: &State<S>,
|
||||||
bytes: BytesStream,
|
|
||||||
input: AnimationFormat,
|
input: AnimationFormat,
|
||||||
|
length: usize,
|
||||||
width: u16,
|
width: u16,
|
||||||
height: u16,
|
height: u16,
|
||||||
frames: u32,
|
frames: u32,
|
||||||
) -> Result<(InternalFormat, ProcessRead), Error> {
|
) -> Result<(InternalFormat, Process), Error> {
|
||||||
let validations = &state.config.media.animation;
|
let validations = &state.config.media.animation;
|
||||||
|
|
||||||
validate_animation(bytes.len(), width, height, frames, validations)?;
|
validate_animation(length, width, height, frames, validations)?;
|
||||||
|
|
||||||
let AnimationOutput {
|
let AnimationOutput {
|
||||||
format,
|
format,
|
||||||
needs_transcode,
|
needs_transcode,
|
||||||
} = input.build_output(validations.format);
|
} = input.build_output(validations.format);
|
||||||
|
|
||||||
let process_read = if needs_transcode {
|
let process = if needs_transcode {
|
||||||
let quality = validations.quality_for(format);
|
let quality = validations.quality_for(format);
|
||||||
|
|
||||||
magick::convert_animation(state, input, format, quality, bytes).await?
|
magick::convert_animation_command(state, input, format, quality).await?
|
||||||
} else {
|
} else {
|
||||||
exiftool::clear_metadata_bytes_read(bytes, state.config.media.process_timeout)?
|
exiftool::clear_metadata_command(state.config.media.process_timeout)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((InternalFormat::Animation(format), process_read))
|
Ok((InternalFormat::Animation(format), process))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_video(
|
fn validate_video(
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
use crate::{
|
use crate::{exiftool::ExifError, process::Process};
|
||||||
bytes_stream::BytesStream,
|
|
||||||
exiftool::ExifError,
|
|
||||||
process::{Process, ProcessRead},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip_all)]
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
pub(super) fn clear_metadata_bytes_read(
|
pub(super) fn clear_metadata_command(timeout: u64) -> Result<Process, ExifError> {
|
||||||
input: BytesStream,
|
Ok(Process::run(
|
||||||
timeout: u64,
|
"exiftool",
|
||||||
) -> Result<ProcessRead, ExifError> {
|
&["-all=", "-", "-out", "-"],
|
||||||
Ok(
|
&[],
|
||||||
Process::run("exiftool", &["-all=", "-", "-out", "-"], &[], timeout)?
|
timeout,
|
||||||
.bytes_stream_read(input),
|
)?)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,82 +1,60 @@
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bytes_stream::BytesStream,
|
|
||||||
formats::{AnimationFormat, ImageFormat},
|
formats::{AnimationFormat, ImageFormat},
|
||||||
magick::{MagickError, MAGICK_CONFIGURE_PATH, MAGICK_TEMPORARY_PATH},
|
magick::{MagickError, MAGICK_CONFIGURE_PATH, MAGICK_TEMPORARY_PATH},
|
||||||
process::{Process, ProcessRead},
|
process::Process,
|
||||||
state::State,
|
state::State,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) async fn convert_image<S>(
|
pub(super) async fn convert_image_command<S>(
|
||||||
state: &State<S>,
|
state: &State<S>,
|
||||||
input: ImageFormat,
|
input: ImageFormat,
|
||||||
output: ImageFormat,
|
output: ImageFormat,
|
||||||
quality: Option<u8>,
|
quality: Option<u8>,
|
||||||
bytes: BytesStream,
|
) -> Result<Process, MagickError> {
|
||||||
) -> Result<ProcessRead, MagickError> {
|
|
||||||
convert(
|
convert(
|
||||||
state,
|
state,
|
||||||
input.magick_format(),
|
input.magick_format(),
|
||||||
output.magick_format(),
|
output.magick_format(),
|
||||||
false,
|
false,
|
||||||
quality,
|
quality,
|
||||||
bytes,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn convert_animation<S>(
|
pub(super) async fn convert_animation_command<S>(
|
||||||
state: &State<S>,
|
state: &State<S>,
|
||||||
input: AnimationFormat,
|
input: AnimationFormat,
|
||||||
output: AnimationFormat,
|
output: AnimationFormat,
|
||||||
quality: Option<u8>,
|
quality: Option<u8>,
|
||||||
bytes: BytesStream,
|
) -> Result<Process, MagickError> {
|
||||||
) -> Result<ProcessRead, MagickError> {
|
|
||||||
convert(
|
convert(
|
||||||
state,
|
state,
|
||||||
input.magick_format(),
|
input.magick_format(),
|
||||||
output.magick_format(),
|
output.magick_format(),
|
||||||
true,
|
true,
|
||||||
quality,
|
quality,
|
||||||
bytes,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn convert<S>(
|
async fn convert<S>(
|
||||||
state: &State<S>,
|
state: &State<S>,
|
||||||
input: &'static str,
|
input_format: &'static str,
|
||||||
output: &'static str,
|
output_format: &'static str,
|
||||||
coalesce: bool,
|
coalesce: bool,
|
||||||
quality: Option<u8>,
|
quality: Option<u8>,
|
||||||
bytes: BytesStream,
|
) -> Result<Process, MagickError> {
|
||||||
) -> Result<ProcessRead, MagickError> {
|
|
||||||
let temporary_path = state
|
let temporary_path = state
|
||||||
.tmp_dir
|
.tmp_dir
|
||||||
.tmp_folder()
|
.tmp_folder()
|
||||||
.await
|
.await
|
||||||
.map_err(MagickError::CreateTemporaryDirectory)?;
|
.map_err(MagickError::CreateTemporaryDirectory)?;
|
||||||
|
|
||||||
let input_file = state.tmp_dir.tmp_file(None);
|
let input_arg = format!("{input_format}:-");
|
||||||
|
|
||||||
crate::store::file_store::safe_create_parent(&input_file)
|
let output_arg = format!("{output_format}:-");
|
||||||
.await
|
|
||||||
.map_err(MagickError::CreateDir)?;
|
|
||||||
|
|
||||||
let mut tmp_one = crate::file::File::create(&input_file)
|
|
||||||
.await
|
|
||||||
.map_err(MagickError::CreateFile)?;
|
|
||||||
tmp_one
|
|
||||||
.write_from_stream(bytes.into_io_stream())
|
|
||||||
.await
|
|
||||||
.map_err(MagickError::Write)?;
|
|
||||||
tmp_one.close().await.map_err(MagickError::CloseFile)?;
|
|
||||||
|
|
||||||
let input_arg = [input.as_ref(), input_file.as_os_str()].join(":".as_ref());
|
|
||||||
let output_arg = format!("{output}:-");
|
|
||||||
let quality = quality.map(|q| q.to_string());
|
let quality = quality.map(|q| q.to_string());
|
||||||
|
|
||||||
let mut args: Vec<&OsStr> = vec!["convert".as_ref()];
|
let mut args: Vec<&OsStr> = vec!["convert".as_ref()];
|
||||||
|
@ -85,7 +63,11 @@ async fn convert<S>(
|
||||||
args.push("-coalesce".as_ref());
|
args.push("-coalesce".as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
args.extend(["-strip".as_ref(), "-auto-orient".as_ref(), &input_arg] as [&OsStr; 3]);
|
args.extend([
|
||||||
|
"-strip".as_ref(),
|
||||||
|
"-auto-orient".as_ref(),
|
||||||
|
input_arg.as_ref(),
|
||||||
|
] as [&OsStr; 3]);
|
||||||
|
|
||||||
if let Some(quality) = &quality {
|
if let Some(quality) = &quality {
|
||||||
args.extend(["-quality".as_ref(), quality.as_ref()] as [&OsStr; 2]);
|
args.extend(["-quality".as_ref(), quality.as_ref()] as [&OsStr; 2]);
|
||||||
|
@ -98,9 +80,8 @@ async fn convert<S>(
|
||||||
(MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()),
|
(MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()),
|
||||||
];
|
];
|
||||||
|
|
||||||
let reader = Process::run("magick", &args, &envs, state.config.media.process_timeout)?.read();
|
let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)?
|
||||||
|
.add_extras(temporary_path);
|
||||||
|
|
||||||
let clean_reader = reader.add_extras(input_file).add_extras(temporary_path);
|
Ok(process)
|
||||||
|
|
||||||
Ok(clean_reader)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue