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( state: &State, alias: &Alias, original_details: &Details, ) -> Result 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 }) .await??; Ok(blurhash) } async fn read_rgba_command( state: &State, input_format: ProcessableFormat, ) -> Result { 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) }