feat: integrate EXIF data extraction using nom-exif and refactor related components
This commit is contained in:
@@ -22,7 +22,6 @@ sqlx = { version = "0.8.6", features = [
|
||||
futures-util = "0.3.31"
|
||||
bytes = "1.10.1"
|
||||
uuid = { version = "1.18.1", features = ["v4", "serde"] }
|
||||
nom-exif = { version = "2.5.4", features = ["serde", "tokio", "async"] }
|
||||
async-trait = "0.1.89"
|
||||
xmp_toolkit = "1.11.0"
|
||||
chrono = "0.4.42"
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
Reference in New Issue
Block a user