2021-10-23 19:14:12 +00:00
|
|
|
use crate::{
|
|
|
|
error::{Error, UploadError},
|
|
|
|
process::Process,
|
|
|
|
store::Store,
|
|
|
|
};
|
2021-09-04 00:53:53 +00:00
|
|
|
use actix_web::web::Bytes;
|
2021-10-23 04:48:56 +00:00
|
|
|
use tokio::io::AsyncRead;
|
2021-09-14 01:22:42 +00:00
|
|
|
use tracing::instrument;
|
2021-08-26 02:46:11 +00:00
|
|
|
|
2021-10-23 04:48:56 +00:00
|
|
|
#[derive(Debug)]
|
2021-08-31 02:19:47 +00:00
|
|
|
pub(crate) enum InputFormat {
|
|
|
|
Gif,
|
|
|
|
Mp4,
|
|
|
|
}
|
|
|
|
|
2021-09-14 01:22:42 +00:00
|
|
|
#[derive(Debug)]
|
2021-08-28 22:15:14 +00:00
|
|
|
pub(crate) enum ThumbnailFormat {
|
|
|
|
Jpeg,
|
2021-08-31 02:19:47 +00:00
|
|
|
// Webp,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl InputFormat {
|
2021-10-23 19:14:12 +00:00
|
|
|
fn to_ext(&self) -> &'static str {
|
2021-08-31 02:19:47 +00:00
|
|
|
match self {
|
2021-10-23 19:14:12 +00:00
|
|
|
InputFormat::Gif => ".gif",
|
|
|
|
InputFormat::Mp4 => ".mp4",
|
2021-08-31 02:19:47 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-28 22:15:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ThumbnailFormat {
|
|
|
|
fn as_codec(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
ThumbnailFormat::Jpeg => "mjpeg",
|
2021-08-31 02:19:47 +00:00
|
|
|
// ThumbnailFormat::Webp => "webp",
|
2021-08-28 22:15:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-23 19:14:12 +00:00
|
|
|
fn to_ext(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
ThumbnailFormat::Jpeg => ".jpeg",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-28 22:15:14 +00:00
|
|
|
fn as_format(&self) -> &'static str {
|
|
|
|
match self {
|
2022-03-27 01:45:12 +00:00
|
|
|
ThumbnailFormat::Jpeg => "image2",
|
2021-08-31 02:19:47 +00:00
|
|
|
// ThumbnailFormat::Webp => "webp",
|
2021-08-28 22:15:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-07 02:40:49 +00:00
|
|
|
#[tracing::instrument(name = "Convert to Mp4", skip(input))]
|
2021-10-23 19:14:12 +00:00
|
|
|
pub(crate) async fn to_mp4_bytes(
|
2021-09-04 00:53:53 +00:00
|
|
|
input: Bytes,
|
|
|
|
input_format: InputFormat,
|
2021-10-23 19:14:12 +00:00
|
|
|
) -> Result<impl AsyncRead + Unpin, Error> {
|
|
|
|
let input_file = crate::tmp_file::tmp_file(Some(input_format.to_ext()));
|
|
|
|
let input_file_str = input_file.to_str().ok_or(UploadError::Path)?;
|
|
|
|
crate::store::file_store::safe_create_parent(&input_file).await?;
|
|
|
|
|
|
|
|
let output_file = crate::tmp_file::tmp_file(Some(".mp4"));
|
|
|
|
let output_file_str = output_file.to_str().ok_or(UploadError::Path)?;
|
|
|
|
crate::store::file_store::safe_create_parent(&output_file).await?;
|
|
|
|
|
|
|
|
let mut tmp_one = crate::file::File::create(&input_file).await?;
|
|
|
|
tmp_one.write_from_bytes(input).await?;
|
|
|
|
tmp_one.close().await?;
|
|
|
|
|
2021-09-14 01:22:42 +00:00
|
|
|
let process = Process::run(
|
|
|
|
"ffmpeg",
|
|
|
|
&[
|
|
|
|
"-i",
|
2021-10-28 04:06:03 +00:00
|
|
|
input_file_str,
|
2021-09-14 01:22:42 +00:00
|
|
|
"-pix_fmt",
|
|
|
|
"yuv420p",
|
|
|
|
"-vf",
|
|
|
|
"scale=trunc(iw/2)*2:trunc(ih/2)*2",
|
|
|
|
"-an",
|
|
|
|
"-codec",
|
|
|
|
"h264",
|
|
|
|
"-f",
|
|
|
|
"mp4",
|
2021-10-28 04:06:03 +00:00
|
|
|
output_file_str,
|
2021-09-14 01:22:42 +00:00
|
|
|
],
|
|
|
|
)?;
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2021-10-23 19:14:12 +00:00
|
|
|
process.wait().await?;
|
|
|
|
tokio::fs::remove_file(input_file).await?;
|
|
|
|
|
|
|
|
let tmp_two = crate::file::File::open(&output_file).await?;
|
|
|
|
let stream = tmp_two.read_to_stream(None, None).await?;
|
|
|
|
let reader = tokio_util::io::StreamReader::new(stream);
|
|
|
|
let clean_reader = crate::tmp_file::cleanup_tmpfile(reader, output_file);
|
|
|
|
|
|
|
|
Ok(Box::pin(clean_reader))
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
|
|
|
|
2021-10-23 04:48:56 +00:00
|
|
|
#[instrument(name = "Create video thumbnail")]
|
|
|
|
pub(crate) async fn thumbnail<S: Store>(
|
|
|
|
store: S,
|
|
|
|
from: S::Identifier,
|
|
|
|
input_format: InputFormat,
|
2021-08-28 22:15:14 +00:00
|
|
|
format: ThumbnailFormat,
|
2022-03-27 01:45:12 +00:00
|
|
|
) -> Result<impl AsyncRead + Unpin, Error> {
|
2021-10-23 19:14:12 +00:00
|
|
|
let input_file = crate::tmp_file::tmp_file(Some(input_format.to_ext()));
|
|
|
|
let input_file_str = input_file.to_str().ok_or(UploadError::Path)?;
|
|
|
|
crate::store::file_store::safe_create_parent(&input_file).await?;
|
|
|
|
|
|
|
|
let output_file = crate::tmp_file::tmp_file(Some(format.to_ext()));
|
|
|
|
let output_file_str = output_file.to_str().ok_or(UploadError::Path)?;
|
|
|
|
crate::store::file_store::safe_create_parent(&output_file).await?;
|
|
|
|
|
|
|
|
let mut tmp_one = crate::file::File::create(&input_file).await?;
|
|
|
|
tmp_one
|
|
|
|
.write_from_stream(store.to_stream(&from, None, None).await?)
|
|
|
|
.await?;
|
|
|
|
tmp_one.close().await?;
|
|
|
|
|
2021-10-23 04:48:56 +00:00
|
|
|
let process = Process::run(
|
|
|
|
"ffmpeg",
|
|
|
|
&[
|
|
|
|
"-i",
|
2021-10-28 04:06:03 +00:00
|
|
|
input_file_str,
|
2021-10-23 04:48:56 +00:00
|
|
|
"-vframes",
|
|
|
|
"1",
|
|
|
|
"-codec",
|
|
|
|
format.as_codec(),
|
|
|
|
"-f",
|
|
|
|
format.as_format(),
|
2021-10-28 04:06:03 +00:00
|
|
|
output_file_str,
|
2021-10-23 04:48:56 +00:00
|
|
|
],
|
|
|
|
)?;
|
2021-08-26 02:46:11 +00:00
|
|
|
|
2021-10-23 19:14:12 +00:00
|
|
|
process.wait().await?;
|
|
|
|
tokio::fs::remove_file(input_file).await?;
|
|
|
|
|
|
|
|
let tmp_two = crate::file::File::open(&output_file).await?;
|
|
|
|
let stream = tmp_two.read_to_stream(None, None).await?;
|
|
|
|
let reader = tokio_util::io::StreamReader::new(stream);
|
|
|
|
let clean_reader = crate::tmp_file::cleanup_tmpfile(reader, output_file);
|
|
|
|
|
|
|
|
Ok(Box::pin(clean_reader))
|
2021-08-26 02:46:11 +00:00
|
|
|
}
|