Add EXIF update support

This commit is contained in:
Cameron
2025-12-18 21:20:45 -05:00
parent b4c5a38c9d
commit e3ccc123d0

View File

@@ -2,6 +2,7 @@ use std::path::PathBuf;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use chrono::Utc; use chrono::Utc;
use clap::Parser;
use rayon::prelude::*; use rayon::prelude::*;
use walkdir::WalkDir; use walkdir::WalkDir;
@@ -9,16 +10,30 @@ use image_api::database::models::InsertImageExif;
use image_api::database::{ExifDao, SqliteExifDao}; use image_api::database::{ExifDao, SqliteExifDao};
use image_api::exif; use image_api::exif;
#[derive(Parser, Debug)]
#[command(name = "migrate_exif")]
#[command(about = "Extract and store EXIF data from images", long_about = None)]
struct Args {
#[arg(long, help = "Skip files that already have EXIF data in database")]
skip_existing: bool,
}
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
env_logger::init(); env_logger::init();
dotenv::dotenv()?; dotenv::dotenv()?;
let args = Args::parse();
let base_path = dotenv::var("BASE_PATH")?; let base_path = dotenv::var("BASE_PATH")?;
let base = PathBuf::from(&base_path); let base = PathBuf::from(&base_path);
println!("EXIF Migration Tool"); println!("EXIF Migration Tool");
println!("==================="); println!("===================");
println!("Base path: {}", base.display()); println!("Base path: {}", base.display());
if args.skip_existing {
println!("Mode: Skip existing (incremental)");
} else {
println!("Mode: Upsert (insert new, update existing)");
}
println!(); println!();
// Collect all image files that support EXIF // Collect all image files that support EXIF
@@ -59,6 +74,19 @@ fn main() -> anyhow::Result<()> {
} }
}; };
// Check if EXIF data already exists
let existing = if let Ok(mut dao_lock) = dao.lock() {
dao_lock.get_exif(&relative_path).ok().flatten()
} else {
eprintln!("{} - Failed to acquire database lock", relative_path);
return Err(anyhow::anyhow!("Lock error"));
};
// Skip if exists and skip_existing flag is set
if args.skip_existing && existing.is_some() {
return Ok(("skip".to_string(), relative_path));
}
match exif::extract_exif_from_path(path) { match exif::extract_exif_from_path(path) {
Ok(exif_data) => { Ok(exif_data) => {
let timestamp = Utc::now().timestamp(); let timestamp = Utc::now().timestamp();
@@ -78,16 +106,31 @@ fn main() -> anyhow::Result<()> {
shutter_speed: exif_data.shutter_speed, shutter_speed: exif_data.shutter_speed,
iso: exif_data.iso, iso: exif_data.iso,
date_taken: exif_data.date_taken, date_taken: exif_data.date_taken,
created_time: timestamp, created_time: existing
.as_ref()
.map(|e| e.created_time)
.unwrap_or(timestamp),
last_modified: timestamp, last_modified: timestamp,
}; };
// Store in database // Store or update in database
if let Ok(mut dao_lock) = dao.lock() { if let Ok(mut dao_lock) = dao.lock() {
match dao_lock.store_exif(insert_exif) { let result = if existing.is_some() {
Ok(_) => { // Update existing record
println!("{}", relative_path); dao_lock.update_exif(insert_exif).map(|_| "update")
Ok(relative_path) } else {
// Insert new record
dao_lock.store_exif(insert_exif).map(|_| "insert")
};
match result {
Ok(action) => {
if action == "update" {
println!("{} (updated)", relative_path);
} else {
println!("{} (inserted)", relative_path);
}
Ok((action.to_string(), relative_path))
} }
Err(e) => { Err(e) => {
eprintln!("{} - Database error: {:?}", relative_path, e); eprintln!("{} - Database error: {:?}", relative_path, e);
@@ -107,23 +150,42 @@ fn main() -> anyhow::Result<()> {
}) })
.collect(); .collect();
let success_count = results.iter().filter(|r| r.is_ok()).count(); // Count results
let error_count = results.len() - success_count; let mut success_count = 0;
let mut inserted_count = 0;
let mut updated_count = 0;
let mut skipped_count = 0;
for result in &results {
if let Ok((action, _)) = result {
success_count += 1;
match action.as_str() {
"insert" => inserted_count += 1,
"update" => updated_count += 1,
"skip" => skipped_count += 1,
_ => {}
}
}
}
let error_count = results.len() - success_count - skipped_count;
println!(); println!();
println!("==================="); println!("===================");
println!("Migration complete!"); println!("Migration complete!");
println!( println!("Total images processed: {}", image_files.len());
"Successfully extracted EXIF from {}/{} images",
success_count,
image_files.len()
);
if inserted_count > 0 {
println!(" New EXIF records inserted: {}", inserted_count);
}
if updated_count > 0 {
println!(" Existing records updated: {}", updated_count);
}
if skipped_count > 0 {
println!(" Skipped (already exists): {}", skipped_count);
}
if error_count > 0 { if error_count > 0 {
println!( println!(" Errors (no EXIF data or failures): {}", error_count);
"{} images had no EXIF data or encountered errors",
error_count
);
} }
Ok(()) Ok(())