feat: Enhance XMP writing capabilities with face region and tag support

This commit is contained in:
2025-11-15 14:43:59 +01:00
parent 8d05bdfd63
commit f7f1547592
4 changed files with 166 additions and 37 deletions

View File

@@ -1,9 +1,15 @@
use std::{io::Cursor, path::{Path, PathBuf}};
use std::{
io::Cursor,
path::{Path, PathBuf},
};
use chrono::{DateTime, Datelike, NaiveDateTime, Utc};
use nom_exif::{AsyncMediaParser, AsyncMediaSource, ExifIter, MediaParser, MediaSource, TrackInfo};
use crate::{error::{CoreError, CoreResult}, models::MediaMetadataSource};
use crate::{
error::{CoreError, CoreResult},
models::MediaMetadataSource,
};
#[derive(Default, Debug)]
pub struct ExtractedExif {
@@ -26,9 +32,7 @@ pub fn parse_exif_datetime(s: &str) -> Option<DateTime<Utc>> {
None
}
pub fn extract_exif_data_from_bytes(
bytes: &[u8],
) -> CoreResult<ExtractedExif> {
pub fn extract_exif_data_from_bytes(bytes: &[u8]) -> CoreResult<ExtractedExif> {
let ms = MediaSource::seekable(Cursor::new(bytes))
.map_err(|e| CoreError::Unknown(format!("Failed to open bytes for EXIF: {}", e)))?;
@@ -54,7 +58,11 @@ pub fn extract_exif_data_from_bytes(
v.to_string(),
)),
Err(e) => {
println!("!! EXIF parsing error for tag 0x{:04x}: {}", x.tag_code(), e);
println!(
"!! EXIF parsing error for tag 0x{:04x}: {}",
x.tag_code(),
e
);
None
}
}
@@ -83,7 +91,7 @@ pub fn extract_exif_data_from_bytes(
}
pub async fn extract_exif_data(file_path: &Path) -> CoreResult<ExtractedExif> {
let ms = AsyncMediaSource::file_path(file_path)
let ms = AsyncMediaSource::file_path(file_path)
.await
.map_err(|e| CoreError::Unknown(format!("Failed to open file for EXIF: {}", e)))?;
@@ -146,10 +154,12 @@ pub fn get_storage_path_and_date(
extracted_data: &ExtractedExif,
filename: &str,
) -> (PathBuf, Option<DateTime<Utc>>) {
let date_taken_str = extracted_data.all_tags.iter()
let date_taken_str = extracted_data
.all_tags
.iter()
.find(|(source, tag_name, _)| {
*source == MediaMetadataSource::Exif &&
(tag_name == "DateTimeOriginal" || tag_name == "ModifyDate")
*source == MediaMetadataSource::Exif
&& (tag_name == "DateTimeOriginal" || tag_name == "ModifyDate")
})
.map(|(_, _, tag_value)| tag_value);
@@ -158,8 +168,25 @@ pub fn get_storage_path_and_date(
let year = file_date.year().to_string();
let month = format!("{:02}", file_date.month());
let storage_path = PathBuf::from(&year).join(&month).join(filename);
(storage_path, date_taken)
}
}
pub fn is_invalid_exif_tag(tag_name: &str, tag_value: &str) -> bool {
if tag_name.starts_with("Unknown(") {
return true;
}
if tag_value.starts_with("U16Array")
|| tag_value.starts_with("U32Array")
|| tag_value.starts_with("U8Array")
|| tag_value.starts_with("URationalArray")
|| tag_value.starts_with("Undefined")
{
return true;
}
false
}

View File

@@ -3,7 +3,13 @@ use std::sync::Arc;
use async_trait::async_trait;
use crate::{
config::AppConfig, error::CoreResult, models::Media, repositories::{AlbumRepository, MediaMetadataRepository, MediaRepository, UserRepository}
config::AppConfig,
error::CoreResult,
models::Media,
repositories::{
AlbumRepository, FaceRegionRepository, MediaMetadataRepository, MediaRepository,
PersonRepository, TagRepository, UserRepository,
},
};
pub struct PluginData {
@@ -15,6 +21,9 @@ pub struct PluginContext {
pub album_repo: Arc<dyn AlbumRepository>,
pub user_repo: Arc<dyn UserRepository>,
pub metadata_repo: Arc<dyn MediaMetadataRepository>,
pub tag_repo: Arc<dyn TagRepository>,
pub person_repo: Arc<dyn PersonRepository>,
pub face_region_repo: Arc<dyn FaceRegionRepository>,
pub media_library_path: String,
pub config: Arc<AppConfig>,
}