feat: thumbnail generator plugin with configurable size/format

- ThumbnailGeneratorPort in domain (bytes + config → resized bytes)
- adapters-thumbnail: ImageThumbnailGenerator using image crate
- ThumbnailGeneratorPlugin reads width/height/format/profile from step config
- PostgresDerivativeRepository + 012_derivatives migration
- Seeded in extract_metadata pipeline as step 2 (300x300 webp)
- Standalone generate_derivative pipeline for on-demand use
This commit is contained in:
2026-05-31 20:44:55 +02:00
parent 45669ec848
commit 35d5baf7be
15 changed files with 1155 additions and 18 deletions

View File

@@ -1,6 +1,8 @@
use crate::plugin_registry::InMemoryPluginRegistry;
use crate::plugins::{MetadataExtractorPlugin, NoOpPlugin, SidecarSyncPlugin};
use domain::ports::{MetadataExtractorPort, SidecarWriterPort};
use crate::plugins::{
MetadataExtractorPlugin, NoOpPlugin, SidecarSyncPlugin, ThumbnailGeneratorPlugin,
};
use domain::ports::{MetadataExtractorPort, SidecarWriterPort, ThumbnailGeneratorPort};
use std::sync::Arc;
use super::Repos;
@@ -10,16 +12,23 @@ pub fn build_plugin_registry(
file_storage: Arc<dyn domain::ports::FileStoragePort>,
sidecar_writer: Arc<dyn SidecarWriterPort>,
extractor: Arc<dyn MetadataExtractorPort>,
thumbnail_gen: Arc<dyn ThumbnailGeneratorPort>,
) -> InMemoryPluginRegistry {
let mut registry = InMemoryPluginRegistry::new();
registry.register(Arc::new(NoOpPlugin));
registry.register(Arc::new(MetadataExtractorPlugin::new(
repos.asset.clone(),
file_storage,
file_storage.clone(),
repos.metadata.clone(),
extractor,
)));
registry.register(Arc::new(ThumbnailGeneratorPlugin::new(
repos.asset.clone(),
file_storage,
repos.derivative.clone(),
thumbnail_gen,
)));
let export_handler = Arc::new(application::sidecar::ExportSidecarHandler::new(
repos.metadata.clone(),