feat: integrate EXIF data extraction using nom-exif and refactor related components

This commit is contained in:
2025-11-14 06:35:08 +01:00
parent 60860cf508
commit ea95c2255f
7 changed files with 155 additions and 105 deletions

View File

@@ -1,13 +1,9 @@
use async_trait::async_trait;
use chrono::{DateTime, NaiveDateTime, Utc};
use std::path::PathBuf;
use libertas_core::{
error::{CoreError, CoreResult},
models::Media,
plugins::{MediaProcessorPlugin, PluginContext, PluginData},
error::CoreResult, media_utils::extract_exif_data, models::Media, plugins::{MediaProcessorPlugin, PluginContext, PluginData}
};
use nom_exif::{AsyncMediaParser, AsyncMediaSource, Exif, ExifIter, ExifTag};
pub struct ExifReaderPlugin;
@@ -20,51 +16,15 @@ impl MediaProcessorPlugin for ExifReaderPlugin {
async fn process(&self, media: &Media, context: &PluginContext) -> CoreResult<PluginData> {
let file_path = PathBuf::from(&context.media_library_path).join(&media.storage_path);
let ms = match AsyncMediaSource::file_path(file_path).await {
Ok(ms) => ms,
Err(e) => return Err(CoreError::Unknown(format!("Failed to open a file: {}", e))),
};
if !ms.has_exif() {
return Ok(PluginData {
message: "No EXIF data found in file.".to_string(),
});
}
let mut parser = AsyncMediaParser::new();
let iter: ExifIter = match parser.parse(ms).await {
Ok(iter) => iter,
let (width, height, location, date_taken) = match extract_exif_data(&file_path).await {
Ok(data) => (data.width, data.height, data.location, data.date_taken),
Err(e) => {
// It's not a fatal error, just means parsing failed (e.g., corrupt data)
return Ok(PluginData {
message: format!("Could not parse EXIF: {}", e),
});
}
};
let location: Option<String> = match iter.parse_gps_info() {
Ok(Some(gps_info)) => Some(gps_info.format_iso6709()),
Ok(None) => None,
Err(_) => None,
};
let exif: Exif = iter.into();
let width = exif
.get(ExifTag::ExifImageWidth)
.and_then(|f| f.as_u32())
.map(|v| v as i32);
let height = exif
.get(ExifTag::ExifImageHeight)
.and_then(|f| f.as_u32())
.map(|v| v as i32);
let date_taken = exif
.get(ExifTag::DateTimeOriginal)
.and_then(|f| f.as_str())
.and_then(parse_exif_datetime);
if width.is_some() || height.is_some() || location.is_some() || date_taken.is_some() {
context
.media_repo
@@ -82,11 +42,4 @@ impl MediaProcessorPlugin for ExifReaderPlugin {
})
}
}
}
fn parse_exif_datetime(s: &str) -> Option<DateTime<Utc>> {
// EXIF datetime format is 'YYYY:MM:DD HH:MM:SS'
NaiveDateTime::parse_from_str(s, "%Y:%m:%d %H:%M:%S")
.ok()
.map(|ndt| ndt.and_local_timezone(Utc).unwrap())
}