diff options
| author | metamuffin <metamuffin@disroot.org> | 2025-11-30 12:32:44 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2025-11-30 12:32:44 +0100 |
| commit | 8174d129fbabd2d39323678d11d868893ddb429a (patch) | |
| tree | 7979a528114cd5fb827f748f678a916e8e8eeddc /transcoder/src/image.rs | |
| parent | 5db15c323d76dca9ae71b0204d63dcb09fbbcbc5 (diff) | |
| download | jellything-8174d129fbabd2d39323678d11d868893ddb429a.tar jellything-8174d129fbabd2d39323678d11d868893ddb429a.tar.bz2 jellything-8174d129fbabd2d39323678d11d868893ddb429a.tar.zst | |
new sync cache
Diffstat (limited to 'transcoder/src/image.rs')
| -rw-r--r-- | transcoder/src/image.rs | 140 |
1 files changed, 55 insertions, 85 deletions
diff --git a/transcoder/src/image.rs b/transcoder/src/image.rs index 6a7f693..8366ece 100644 --- a/transcoder/src/image.rs +++ b/transcoder/src/image.rs @@ -3,96 +3,66 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin <metamuffin.org> */ -use crate::LOCAL_IMAGE_TRANSCODING_TASKS; -use anyhow::Context; +use anyhow::{anyhow, Context, Result}; use image::imageops::FilterType; -use jellycache::{async_cache_file, CachePath}; +use jellycache::{cache, cache_read, CacheKey}; use log::{debug, info}; use rgb::FromSlice; -use std::{ - fs::File, - io::{BufReader, Read, Seek, SeekFrom}, - path::Path, -}; -use tokio::io::AsyncWriteExt; +use std::io::Cursor; -pub async fn transcode( - path: &Path, - quality: f32, - speed: u8, - width: usize, -) -> anyhow::Result<CachePath> { - async_cache_file( - "image-tc", - (path, width, quality as i32, speed), - |mut output| async move { - let _permit = LOCAL_IMAGE_TRANSCODING_TASKS.acquire().await?; - info!("encoding {path:?} (speed={speed}, quality={quality}, width={width})"); - let path = path.to_owned(); - let encoded = tokio::task::spawn_blocking(move || { - let mut file = BufReader::new(File::open(&path).context("opening source")?); +pub fn transcode(key: CacheKey, quality: f32, speed: u8, width: usize) -> Result<Vec<u8>> { + cache( + CacheKey::new_image(("image-tc", key, width, quality as i32, speed)), + move || { + let input = cache_read(key)?.ok_or(anyhow!("transcode cache key missing"))?; + info!("encoding image (speed={speed}, quality={quality}, width={width})"); - // TODO: use better image library that supports AVIF - let is_avif = { - let mut magic = [0u8; 12]; - let _ = file.read_exact(&mut magic); - file.seek(SeekFrom::Start(0)) - .context("seeking back to start")?; - // TODO: magic experimentally found and probably not working in all cases but fine as long as our avif enc uses that - matches!( - magic, - [ - 0x00, - 0x00, - 0x00, - _, - b'f', - b't', - b'y', - b'p', - b'a', - b'v', - b'i', - b'f', - ] - ) - }; - let original = if is_avif { - let mut buf = Vec::new(); - file.read_to_end(&mut buf).context("reading image")?; - libavif_image::read(&buf).unwrap().to_rgba8() - } else { - let reader = image::ImageReader::new(file); - let reader = reader.with_guessed_format().context("guessing format")?; - debug!("guessed format (or fallback): {:?}", reader.format()); - 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, - )) - .context("avif encoding")?; - info!("transcode finished"); - Ok::<_, anyhow::Error>(encoded) - }) - .await??; - output - .write_all(&encoded.avif_file) - .await - .context("writing encoded image")?; - Ok(()) + // TODO: use better image library that supports AVIF + // TODO: magic experimentally found and probably not working in all cases but fine as long as our avif enc uses that + let is_avif = matches!( + input[0..input.len().min(12)], + [ + 0x00, + 0x00, + 0x00, + _, + b'f', + b't', + b'y', + b'p', + b'a', + b'v', + b'i', + b'f', + ] + ); + let original = if is_avif { + libavif_image::read(&input).unwrap().to_rgba8() + } else { + let reader = image::ImageReader::new(Cursor::new(input)); + let reader = reader.with_guessed_format().context("guessing format")?; + debug!("guessed format (or fallback): {:?}", reader.format()); + 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, + )) + .context("avif encoding")?; + + info!("transcode finished"); + Ok(encoded.avif_file) }, ) - .await } |