pict-rs/src/blurhash.rs

107 lines
2.6 KiB
Rust

use std::ffi::{OsStr, OsString};
use tokio::io::AsyncReadExt;
use crate::{
details::Details,
error::{Error, UploadError},
formats::ProcessableFormat,
magick::{MagickError, MAGICK_CONFIGURE_PATH, MAGICK_TEMPORARY_PATH},
process::Process,
repo::Alias,
state::State,
store::Store,
};
pub(crate) async fn generate<S>(
state: &State<S>,
alias: &Alias,
original_details: &Details,
) -> Result<String, Error>
where
S: Store + 'static,
{
let hash = state
.repo
.hash(alias)
.await?
.ok_or(UploadError::MissingIdentifier)?;
let identifier = if original_details.is_video() {
crate::generate::ensure_motion_identifier(state, hash, original_details).await?
} else {
state
.repo
.identifier(hash)
.await?
.ok_or(UploadError::MissingIdentifier)?
};
let input_details = crate::ensure_details_identifier(state, &identifier).await?;
let stream = state.store.to_stream(&identifier, None, None).await?;
let blurhash = read_rgba_command(
state,
input_details
.internal_format()
.processable_format()
.expect("not a video"),
)
.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 mut buf = [0u8; 1024 * 8];
loop {
let n = stdout.read(&mut buf).await?;
if n == 0 {
break;
}
encoder.update(&buf[..n]);
}
Ok(encoder.finalize()) as std::io::Result<String>
})
.await??;
Ok(blurhash)
}
async fn read_rgba_command<S>(
state: &State<S>,
input_format: ProcessableFormat,
) -> Result<Process, MagickError> {
let temporary_path = state
.tmp_dir
.tmp_folder()
.await
.map_err(MagickError::CreateTemporaryDirectory)?;
let mut input_arg = OsString::from(input_format.magick_format());
input_arg.push(":-");
if input_format.coalesce() {
input_arg.push("[0]");
}
let args: [&OsStr; 3] = ["convert".as_ref(), &input_arg, "RGBA:-".as_ref()];
let envs = [
(MAGICK_TEMPORARY_PATH, temporary_path.as_os_str()),
(MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()),
];
let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)
.await?
.add_extras(temporary_path);
Ok(process)
}