From d546caa3f5053ade763430490911fefd6257af9f Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 30 Sep 2023 22:19:19 +0200 Subject: make cache async and fix parallel write bug --- transcoder/src/image.rs | 89 ++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 41 deletions(-) (limited to 'transcoder/src/image.rs') diff --git a/transcoder/src/image.rs b/transcoder/src/image.rs index 6da1be7..3865348 100644 --- a/transcoder/src/image.rs +++ b/transcoder/src/image.rs @@ -1,53 +1,60 @@ use anyhow::Context; use image::{imageops::FilterType, ImageFormat}; -use jellybase::{cache_file, AssetLocationExt}; +use jellybase::{async_cache_file, AssetLocationExt}; use jellycommon::AssetLocation; use log::{debug, info}; use rgb::FromSlice; -use std::{ - fs::File, - io::{BufReader, Write}, - path::PathBuf, -}; +use std::{fs::File, io::BufReader}; +use tokio::io::AsyncWriteExt; -pub fn transcode( +pub async fn transcode( asset: AssetLocation, quality: f32, speed: u8, width: usize, -) -> anyhow::Result { +) -> anyhow::Result { let original_path = asset.path(); - let path = cache_file(&[ - original_path.as_os_str().to_str().unwrap(), - &format!("{width} {quality} {speed}"), - ]) - .path(); - if !path.exists() { - info!("encoding {path:?} (speed={speed}, quality={quality}, width={width})"); - // TODO shouldn't be neccessary with guessed format. - let file = BufReader::new(File::open(&original_path).context("opening source")?); - let mut reader = image::io::Reader::new(file); - reader.set_format(ImageFormat::Avif); - let reader = reader.with_guessed_format().context("guessing format")?; - debug!("guessed format (or fallback): {:?}", reader.format()); - let original = reader.decode().context("decoding image")?.to_rgba8(); - let image = image::imageops::resize( - &original, - width as u32, - width as u32 * original.height() / original.width(), - FilterType::Lanczos3, - ); - let pixels = image.to_vec(); - let encoded = ravif::Encoder::new() - .with_speed(speed.clamp(1, 10)) - .with_quality(quality.clamp(1., 100.)) - .encode_rgba(imgref::Img::new( - pixels.as_rgba(), - image.width() as usize, - image.height() as usize, - ))?; - info!("transcode finished"); - File::create(&path)?.write_all(&encoded.avif_file)?; - } - Ok(path) + let asset = asset.clone(); + Ok(async_cache_file( + &[ + original_path.as_os_str().to_str().unwrap(), + &format!("{width} {quality} {speed}"), + ], + move |mut output| async move { + let encoded = tokio::task::spawn_blocking(move || { + let original_path = asset.path(); + info!( + "encoding {original_path:?} (speed={speed}, quality={quality}, width={width})" + ); + // TODO shouldn't be neccessary with guessed format. + let file = BufReader::new(File::open(&original_path).context("opening source")?); + let mut reader = image::io::Reader::new(file); + reader.set_format(ImageFormat::Avif); + let reader = reader.with_guessed_format().context("guessing format")?; + debug!("guessed format (or fallback): {:?}", reader.format()); + let original = reader.decode().context("decoding image")?.to_rgba8(); + let image = image::imageops::resize( + &original, + width as u32, + width as u32 * original.height() / original.width(), + FilterType::Lanczos3, + ); + let pixels = image.to_vec(); + let encoded = ravif::Encoder::new() + .with_speed(speed.clamp(1, 10)) + .with_quality(quality.clamp(1., 100.)) + .encode_rgba(imgref::Img::new( + pixels.as_rgba(), + image.width() as usize, + image.height() as usize, + ))?; + info!("transcode finished"); + Ok::<_, anyhow::Error>(encoded) + }) + .await??; + output.write_all(&encoded.avif_file).await?; + Ok(()) + }, + ) + .await?) } -- cgit v1.2.3-70-g09d2