Add Exif storing and update to Metadata endpoint

This commit is contained in:
Cameron
2025-12-17 16:55:48 -05:00
parent 4851f64229
commit 4082f1fdb8
13 changed files with 851 additions and 8 deletions

120
src/bin/migrate_exif.rs Normal file
View File

@@ -0,0 +1,120 @@
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use chrono::Utc;
use walkdir::WalkDir;
use rayon::prelude::*;
use image_api::database::{ExifDao, SqliteExifDao};
use image_api::database::models::InsertImageExif;
use image_api::exif;
fn main() -> anyhow::Result<()> {
env_logger::init();
dotenv::dotenv()?;
let base_path = dotenv::var("BASE_PATH")?;
let base = PathBuf::from(&base_path);
println!("EXIF Migration Tool");
println!("===================");
println!("Base path: {}", base.display());
println!();
// Collect all image files that support EXIF
println!("Scanning for images...");
let image_files: Vec<PathBuf> = WalkDir::new(&base)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.filter(|e| exif::supports_exif(&e.path()))
.map(|e| e.path().to_path_buf())
.collect();
println!("Found {} images to process", image_files.len());
if image_files.is_empty() {
println!("No EXIF-supporting images found. Exiting.");
return Ok(());
}
println!();
println!("Extracting EXIF data...");
// Create a thread-safe DAO
let dao = Arc::new(Mutex::new(SqliteExifDao::new()));
// Process in parallel using rayon
let results: Vec<_> = image_files
.par_iter()
.map(|path| {
let relative_path = match path.strip_prefix(&base) {
Ok(p) => p.to_str().unwrap().to_string(),
Err(_) => {
eprintln!("Error: Could not create relative path for {}", path.display());
return Err(anyhow::anyhow!("Path error"));
}
};
match exif::extract_exif_from_path(path) {
Ok(exif_data) => {
let timestamp = Utc::now().timestamp();
let insert_exif = InsertImageExif {
file_path: relative_path.clone(),
camera_make: exif_data.camera_make,
camera_model: exif_data.camera_model,
lens_model: exif_data.lens_model,
width: exif_data.width,
height: exif_data.height,
orientation: exif_data.orientation,
gps_latitude: exif_data.gps_latitude,
gps_longitude: exif_data.gps_longitude,
gps_altitude: exif_data.gps_altitude,
focal_length: exif_data.focal_length,
aperture: exif_data.aperture,
shutter_speed: exif_data.shutter_speed,
iso: exif_data.iso,
date_taken: exif_data.date_taken,
created_time: timestamp,
last_modified: timestamp,
};
// Store in database
if let Ok(mut dao_lock) = dao.lock() {
match dao_lock.store_exif(insert_exif) {
Ok(_) => {
println!("{}", relative_path);
Ok(relative_path)
}
Err(e) => {
eprintln!("{} - Database error: {:?}", relative_path, e);
Err(anyhow::anyhow!("Database error"))
}
}
} else {
eprintln!("{} - Failed to acquire database lock", relative_path);
Err(anyhow::anyhow!("Lock error"))
}
}
Err(e) => {
eprintln!("{} - No EXIF data: {:?}", relative_path, e);
Err(e)
}
}
})
.collect();
let success_count = results.iter().filter(|r| r.is_ok()).count();
let error_count = results.len() - success_count;
println!();
println!("===================");
println!("Migration complete!");
println!("Successfully extracted EXIF from {}/{} images", success_count, image_files.len());
if error_count > 0 {
println!("{} images had no EXIF data or encountered errors", error_count);
}
Ok(())
}