feat: real EXIF extraction via adapters-exif crate

- MetadataExtractorPort in domain (bytes → StructuredData)
- adapters-exif: NomExifExtractor using nom-exif, handles EXIF + TrackInfo
- Worker's MetadataExtractorPlugin delegates to port, no longer knows nom-exif
- Filters noisy binary tags (U8Array, Undefined, Unknown)
This commit is contained in:
2026-05-31 20:28:50 +02:00
parent d1c7243f5b
commit 45669ec848
10 changed files with 212 additions and 8 deletions

View File

@@ -2,7 +2,10 @@ use async_trait::async_trait;
use domain::{
entities::{AssetMetadata, MetadataSource},
errors::DomainError,
ports::{AssetMetadataRepository, AssetRepository, FileStoragePort, PluginExecutor},
ports::{
AssetMetadataRepository, AssetRepository, FileStoragePort, MetadataExtractorPort,
PluginExecutor,
},
value_objects::{MetadataValue, StructuredData, SystemId},
};
use std::sync::Arc;
@@ -12,6 +15,7 @@ pub struct MetadataExtractorPlugin {
asset_repo: Arc<dyn AssetRepository>,
file_storage: Arc<dyn FileStoragePort>,
metadata_repo: Arc<dyn AssetMetadataRepository>,
extractor: Arc<dyn MetadataExtractorPort>,
}
impl MetadataExtractorPlugin {
@@ -19,11 +23,13 @@ impl MetadataExtractorPlugin {
asset_repo: Arc<dyn AssetRepository>,
file_storage: Arc<dyn FileStoragePort>,
metadata_repo: Arc<dyn AssetMetadataRepository>,
extractor: Arc<dyn MetadataExtractorPort>,
) -> Self {
Self {
asset_repo,
file_storage,
metadata_repo,
extractor,
}
}
}
@@ -52,17 +58,16 @@ impl PluginExecutor for MetadataExtractorPlugin {
let path = &asset.source_reference.relative_path;
let data = self.file_storage.read_file(path).await?;
let file_size = data.len() as i64;
let mut extracted = StructuredData::new();
extracted.insert("file_size_bytes", MetadataValue::Integer(file_size));
let mut extracted = self.extractor.extract(&data)?;
extracted.insert("file_size_bytes", MetadataValue::Integer(data.len() as i64));
extracted.insert("mime_type", MetadataValue::String(asset.mime_type.clone()));
let metadata =
AssetMetadata::new(asset_id, MetadataSource::ExifExtracted, extracted.clone());
self.metadata_repo.save(&metadata).await?;
info!(asset_id = %asset_id, file_size, "extracted basic metadata");
info!(asset_id = %asset_id, tags = extracted.len(), "extracted metadata");
Ok(extracted)
}
}