feat: async image conversion service (avif/webp) with backfill

This commit is contained in:
2026-05-12 15:05:28 +02:00
parent 4269eca582
commit 696e3e170c
22 changed files with 1286 additions and 16 deletions

View File

@@ -1,5 +1,6 @@
use domain::{
errors::DomainError,
events::DomainEvent,
value_objects::{ExternalMetadataId, MovieId, PosterPath},
};
@@ -39,6 +40,14 @@ pub async fn execute(ctx: &AppContext, cmd: SyncPosterCommand) -> Result<(), Dom
.image_storage
.store(&movie_id.value().to_string(), &image_bytes)
.await?;
if let Err(e) = ctx.event_publisher
.publish(&DomainEvent::ImageStored { key: stored_path.clone() })
.await
{
tracing::warn!("failed to emit ImageStored for {stored_path}: {e}");
}
let poster_path = PosterPath::new(stored_path)?;
movie.update_poster(poster_path);

View File

@@ -27,6 +27,14 @@ pub async fn execute(ctx: &AppContext, cmd: UpdateProfileCommand) -> Result<(),
}
let key = format!("avatars/{}", user_id.value());
let stored = ctx.image_storage.store(&key, &bytes).await?;
if let Err(e) = ctx.event_publisher
.publish(&DomainEvent::ImageStored { key: stored.clone() })
.await
{
tracing::warn!("failed to emit ImageStored for {stored}: {e}");
}
Some(stored)
} else {
user.avatar_path().map(|s| s.to_string())

View File

@@ -97,6 +97,7 @@ mod tests {
DomainEvent::MovieDeleted { .. } => "movie_deleted",
DomainEvent::UserUpdated { .. } => "user_updated",
DomainEvent::MovieEnrichmentRequested { .. } => "movie_enrichment_requested",
DomainEvent::ImageStored { .. } => "image_stored",
};
self.calls.lock().unwrap().push(label);
Ok(())