aboutsummaryrefslogtreecommitdiff
path: root/transcoder/src/image.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-11-30 12:32:44 +0100
committermetamuffin <metamuffin@disroot.org>2025-11-30 12:32:44 +0100
commit8174d129fbabd2d39323678d11d868893ddb429a (patch)
tree7979a528114cd5fb827f748f678a916e8e8eeddc /transcoder/src/image.rs
parent5db15c323d76dca9ae71b0204d63dcb09fbbcbc5 (diff)
downloadjellything-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.rs140
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
}