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

@@ -6,3 +6,4 @@ pub mod plugins;
pub mod repositories;
pub mod schema;
pub mod services;
pub mod media_utils;

View File

@@ -0,0 +1,71 @@
use std::path::Path;
use chrono::{DateTime, NaiveDateTime, Utc};
use nom_exif::{AsyncMediaParser, AsyncMediaSource, Exif, ExifIter, ExifTag};
use crate::error::{CoreError, CoreResult};
#[derive(Default, Debug)]
pub struct ExtractedExif {
pub width: Option<i32>,
pub height: Option<i32>,
pub location: Option<String>,
pub date_taken: Option<DateTime<Utc>>,
}
fn parse_exif_datetime(s: &str) -> Option<DateTime<Utc>> {
NaiveDateTime::parse_from_str(s, "%Y:%m:%d %H:%M:%S")
.ok()
.map(|ndt| ndt.and_local_timezone(Utc).unwrap())
}
pub async fn extract_exif_data(file_path: &Path) -> CoreResult<ExtractedExif> {
let ms = AsyncMediaSource::file_path(file_path)
.await
.map_err(|e| CoreError::Unknown(format!("Failed to open file for EXIF: {}", e)))?;
if !ms.has_exif() {
return Ok(ExtractedExif::default());
}
let mut parser = AsyncMediaParser::new();
let iter: ExifIter = match parser.parse(ms).await {
Ok(iter) => iter,
Err(e) => {
println!("Could not parse EXIF: {}", e);
return Ok(ExtractedExif::default());
}
};
let location = iter.parse_gps_info().ok().flatten().map(|g| g.format_iso6709());
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 dt_original = exif
.get(ExifTag::DateTimeOriginal)
.and_then(|f| f.as_str())
.and_then(parse_exif_datetime);
let dt_modify = exif
.get(ExifTag::ModifyDate)
.and_then(|f| f.as_str())
.and_then(parse_exif_datetime);
let date_taken = dt_original.or(dt_modify);
Ok(ExtractedExif {
width,
height,
location,
date_taken,
})
}