use std::path::PathBuf; use async_trait::async_trait; use libertas_core::{ error::{CoreError, CoreResult}, models::Media, plugins::{MediaProcessorPlugin, PluginContext, PluginData}, }; use tokio::fs; use xmp_toolkit::{XmpMeta, XmpValue}; pub struct XmpWriterPlugin; #[async_trait] impl MediaProcessorPlugin for XmpWriterPlugin { fn name(&self) -> &'static str { "xmp_writer" } async fn process(&self, media: &Media, context: &PluginContext) -> CoreResult { let fresh_media = context .media_repo .find_by_id(media.id) .await? .ok_or(CoreError::NotFound("Media".to_string(), media.id))?; let file_path = PathBuf::from(&context.media_library_path).join(&fresh_media.storage_path); let xmp_path = format!("{}.xmp", file_path.to_string_lossy()); let mut xmp = XmpMeta::new() .map_err(|e| CoreError::Unknown(format!("Failed to create new XMP metadata: {}", e)))?; xmp.set_property( "http://purl.org/dc/elements/1.1/", "description", &fresh_media.original_filename.into(), ) .map_err(|e| { CoreError::Unknown(format!("Failed to set description property in XMP: {}", e)) })?; if let Some(date_taken) = &fresh_media.date_taken { let date_str = date_taken.to_rfc3339(); xmp.set_property( "http://ns.adobe.com/exif/1.0/", "DateTimeOriginal", &XmpValue::from(date_str), ) .map_err(|e| { CoreError::Unknown(format!("Failed to set DateTimeOriginal in XMP: {}", e)) })?; } if let Some(_location) = &fresh_media.extracted_location { // TODO: Set location properties in XMP } let xmp_str = xmp.to_string(); fs::write(&xmp_path, xmp_str).await?; Ok(PluginData { message: "XMP sidecar written successfully.".to_string(), }) } }