fix: support AVIF thumbnail processing
github/actions/ci GitHub Actions CI finished: success

Route AVIF thumbnail generation through the bundled FFmpeg path instead of the Rust image decoder, avoiding unsupported-format failures without requiring system dav1d dependencies.

Requeue existing AVIF jobs that previously failed with unsupported-format errors and feed generated JPEG derivatives to embedding/tagging preprocessing while leaving lightbox display on the original AVIF file.
This commit is contained in:
2026-06-22 20:43:46 +01:00
parent a06e76c7a7
commit 4f9ab0b821
5 changed files with 183 additions and 17 deletions
+26 -3
View File
@@ -663,10 +663,13 @@ fn process_thumbnail_batch(
let (image_jobs, video_jobs): (Vec<_>, Vec<_>) =
jobs.into_iter().partition(|job| job.media_kind == "image");
let (avif_jobs, raster_jobs): (Vec<_>, Vec<_>) = image_jobs
.into_iter()
.partition(|job| is_avif_path(Path::new(&job.path)));
// Images: parallel decode, committed as one batch.
if !image_jobs.is_empty() {
let results = image_jobs
if !raster_jobs.is_empty() {
let results = raster_jobs
.par_iter()
.map(|job| {
(
@@ -678,6 +681,15 @@ fn process_thumbnail_batch(
persist_thumbnail_results(app, pool, results)?;
}
// AVIF: FFmpeg-backed decode, like videos. Keep this off the shared rayon
// pool because each subprocess blocks its worker thread.
for job in &avif_jobs {
let result =
thumbnail::generate_avif_thumbnail(media_tools, Path::new(&job.path), cache_dir)
.map(Some);
persist_thumbnail_results(app, pool, vec![(job.image_id, result)])?;
}
// Videos: sequential, off the rayon pool — each ffmpeg call blocks its
// thread, and a video-heavy batch on the shared pool would starve image
// decoding across all workers. Committed per item so progress keeps
@@ -749,6 +761,12 @@ fn persist_thumbnail_results(
Ok(())
}
fn is_avif_path(path: &Path) -> bool {
path.extension()
.and_then(|ext| ext.to_str())
.is_some_and(|ext| ext.eq_ignore_ascii_case("avif"))
}
/// Returns `Ok(true)` if a batch was claimed and processed, `Ok(false)` if
/// the queue was empty.
fn process_metadata_batch(
@@ -1142,9 +1160,14 @@ fn process_tagging_batch(
let tag_results = jobs
.iter()
.map(|job| {
let source_path = if is_avif_path(Path::new(&job.path)) {
job.thumbnail_path.as_deref().unwrap_or(&job.path)
} else {
&job.path
};
(
job.clone(),
tagger_ref.run(Path::new(&job.path), tagger::DEFAULT_MAX_TAGS),
tagger_ref.run(Path::new(source_path), tagger::DEFAULT_MAX_TAGS),
)
})
.collect::<Vec<_>>();