feature/canonical-date-taken #76

Merged
cameron merged 6 commits from feature/canonical-date-taken into master 2026-05-06 21:15:58 +00:00
Showing only changes of commit 2d14291733 - Show all commits

View File

@@ -504,6 +504,11 @@ async fn set_image_gps(
}; };
let now = Utc::now().timestamp(); let now = Utc::now().timestamp();
let normalized_path = body.path.replace('\\', "/"); let normalized_path = body.path.replace('\\', "/");
// Re-run the canonical-date waterfall on every GPS write — exiftool
// writing GPS doesn't change the capture date, but if the row was
// previously sourced from `fs_time` the re-read may have given us a
// real EXIF date this time, and we want to upgrade the source.
let resolved_date = date_resolver::resolve_date_taken(&full_path, extracted.date_taken);
let insert_exif = InsertImageExif { let insert_exif = InsertImageExif {
library_id: resolved_library.id, library_id: resolved_library.id,
file_path: normalized_path.clone(), file_path: normalized_path.clone(),
@@ -520,7 +525,7 @@ async fn set_image_gps(
aperture: extracted.aperture.map(|v| v as f32), aperture: extracted.aperture.map(|v| v as f32),
shutter_speed: extracted.shutter_speed, shutter_speed: extracted.shutter_speed,
iso: extracted.iso, iso: extracted.iso,
date_taken: extracted.date_taken, date_taken: resolved_date.map(|r| r.timestamp),
// Created_time is preserved by update_exif (it doesn't touch the // Created_time is preserved by update_exif (it doesn't touch the
// column); pass any int — it's ignored in the UPDATE statement. // column); pass any int — it's ignored in the UPDATE statement.
created_time: now, created_time: now,
@@ -538,8 +543,7 @@ async fn set_image_gps(
// with a usable signal; failure just leaves prior values in place. // with a usable signal; failure just leaves prior values in place.
phash_64: perceptual_hash::compute(&full_path).map(|h| h.phash_64), phash_64: perceptual_hash::compute(&full_path).map(|h| h.phash_64),
dhash_64: perceptual_hash::compute(&full_path).map(|h| h.dhash_64), dhash_64: perceptual_hash::compute(&full_path).map(|h| h.dhash_64),
// Replaced in a follow-up commit with the canonical-date resolver's output. date_taken_source: resolved_date.map(|r| r.source.as_str().to_string()),
date_taken_source: None,
}; };
let updated = { let updated = {
@@ -752,6 +756,10 @@ async fn upload_image(
} }
}; };
let perceptual = perceptual_hash::compute(&uploaded_path); let perceptual = perceptual_hash::compute(&uploaded_path);
let resolved_date = date_resolver::resolve_date_taken(
&uploaded_path,
exif_data.date_taken,
);
let insert_exif = InsertImageExif { let insert_exif = InsertImageExif {
library_id: target_library.id, library_id: target_library.id,
file_path: relative_path.clone(), file_path: relative_path.clone(),
@@ -768,15 +776,15 @@ async fn upload_image(
aperture: exif_data.aperture.map(|v| v as f32), aperture: exif_data.aperture.map(|v| v as f32),
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: resolved_date.map(|r| r.timestamp),
created_time: timestamp, created_time: timestamp,
last_modified: timestamp, last_modified: timestamp,
content_hash, content_hash,
size_bytes, size_bytes,
phash_64: perceptual.map(|h| h.phash_64), phash_64: perceptual.map(|h| h.phash_64),
dhash_64: perceptual.map(|h| h.dhash_64), dhash_64: perceptual.map(|h| h.dhash_64),
// Replaced in a follow-up commit with the canonical-date resolver's output. date_taken_source: resolved_date
date_taken_source: None, .map(|r| r.source.as_str().to_string()),
}; };
if let Ok(mut dao) = exif_dao.lock() { if let Ok(mut dao) = exif_dao.lock() {
@@ -2382,6 +2390,16 @@ fn process_new_files(
None None
}; };
// Canonical date_taken via the waterfall — kamadak-exif (already
// computed above) → exiftool fallback for videos / MakerNote /
// QuickTime → filename regex → earliest_fs_time. Source is
// recorded so the per-tick backfill drain can re-run weak
// resolutions later.
let resolved_date = date_resolver::resolve_date_taken(
&file_path,
exif_fields.as_ref().and_then(|e| e.date_taken),
);
let insert_exif = InsertImageExif { let insert_exif = InsertImageExif {
library_id: library.id, library_id: library.id,
file_path: relative_path.clone(), file_path: relative_path.clone(),
@@ -2408,15 +2426,14 @@ fn process_new_files(
.and_then(|e| e.aperture.map(|v| v as f32)), .and_then(|e| e.aperture.map(|v| v as f32)),
shutter_speed: exif_fields.as_ref().and_then(|e| e.shutter_speed.clone()), shutter_speed: exif_fields.as_ref().and_then(|e| e.shutter_speed.clone()),
iso: exif_fields.as_ref().and_then(|e| e.iso), iso: exif_fields.as_ref().and_then(|e| e.iso),
date_taken: exif_fields.as_ref().and_then(|e| e.date_taken), date_taken: resolved_date.map(|r| r.timestamp),
created_time: timestamp, created_time: timestamp,
last_modified: timestamp, last_modified: timestamp,
content_hash, content_hash,
size_bytes, size_bytes,
phash_64: perceptual.map(|h| h.phash_64), phash_64: perceptual.map(|h| h.phash_64),
dhash_64: perceptual.map(|h| h.dhash_64), dhash_64: perceptual.map(|h| h.dhash_64),
// Replaced in a follow-up commit with the canonical-date resolver's output. date_taken_source: resolved_date.map(|r| r.source.as_str().to_string()),
date_taken_source: None,
}; };
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao"); let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");