Files
k-photos/crates/worker/src/plugins/metadata_extractor.rs
Gabriel Kaszewski 45669ec848 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)
2026-05-31 20:28:50 +02:00

74 lines
2.2 KiB
Rust

use async_trait::async_trait;
use domain::{
entities::{AssetMetadata, MetadataSource},
errors::DomainError,
ports::{
AssetMetadataRepository, AssetRepository, FileStoragePort, MetadataExtractorPort,
PluginExecutor,
},
value_objects::{MetadataValue, StructuredData, SystemId},
};
use std::sync::Arc;
use tracing::info;
pub struct MetadataExtractorPlugin {
asset_repo: Arc<dyn AssetRepository>,
file_storage: Arc<dyn FileStoragePort>,
metadata_repo: Arc<dyn AssetMetadataRepository>,
extractor: Arc<dyn MetadataExtractorPort>,
}
impl MetadataExtractorPlugin {
pub fn new(
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,
}
}
}
#[async_trait]
impl PluginExecutor for MetadataExtractorPlugin {
fn plugin_name(&self) -> &str {
"metadata_extractor"
}
async fn execute(
&self,
asset_id: Option<SystemId>,
_payload: &StructuredData,
_config: &StructuredData,
) -> Result<StructuredData, DomainError> {
let asset_id = asset_id.ok_or_else(|| {
DomainError::Validation("metadata_extractor requires asset_id".into())
})?;
let asset = self
.asset_repo
.find_by_id(&asset_id)
.await?
.ok_or_else(|| DomainError::NotFound(format!("Asset {} not found", asset_id)))?;
let path = &asset.source_reference.relative_path;
let data = self.file_storage.read_file(path).await?;
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, tags = extracted.len(), "extracted metadata");
Ok(extracted)
}
}