- 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)
74 lines
2.2 KiB
Rust
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)
|
|
}
|
|
}
|