ingest: stamp canonical date_taken on every InsertImageExif
Wires `date_resolver::resolve_date_taken` into the three call sites that build `InsertImageExif`: - `process_new_files` (file watcher) — every newly-registered file gets the resolver's verdict so videos and EXIF-stripped images land with a real date instead of NULL. - Upload handler — same waterfall on the post-multipart-write path. - GPS-write handler — re-runs the waterfall after exiftool writes GPS and re-reads the EXIF, in case a previously fs_time-sourced row now has a real EXIF date to upgrade to. This is a behavior change vs. the pre-rewrite `/memories` request-time priority: EXIF now beats filename when both are present. A photo named `Screenshot_2014-06-01.png` whose EXIF `DateTime` is 2021 now appears under 2021. The reverse case (no EXIF, parseable filename) is unchanged and continues to surface the filename date with `date_taken_source = 'filename'`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
35
src/main.rs
35
src/main.rs
@@ -504,6 +504,11 @@ async fn set_image_gps(
|
||||
};
|
||||
let now = Utc::now().timestamp();
|
||||
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 {
|
||||
library_id: resolved_library.id,
|
||||
file_path: normalized_path.clone(),
|
||||
@@ -520,7 +525,7 @@ async fn set_image_gps(
|
||||
aperture: extracted.aperture.map(|v| v as f32),
|
||||
shutter_speed: extracted.shutter_speed,
|
||||
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
|
||||
// column); pass any int — it's ignored in the UPDATE statement.
|
||||
created_time: now,
|
||||
@@ -538,8 +543,7 @@ async fn set_image_gps(
|
||||
// with a usable signal; failure just leaves prior values in place.
|
||||
phash_64: perceptual_hash::compute(&full_path).map(|h| h.phash_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: None,
|
||||
date_taken_source: resolved_date.map(|r| r.source.as_str().to_string()),
|
||||
};
|
||||
|
||||
let updated = {
|
||||
@@ -752,6 +756,10 @@ async fn upload_image(
|
||||
}
|
||||
};
|
||||
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 {
|
||||
library_id: target_library.id,
|
||||
file_path: relative_path.clone(),
|
||||
@@ -768,15 +776,15 @@ async fn upload_image(
|
||||
aperture: exif_data.aperture.map(|v| v as f32),
|
||||
shutter_speed: exif_data.shutter_speed,
|
||||
iso: exif_data.iso,
|
||||
date_taken: exif_data.date_taken,
|
||||
date_taken: resolved_date.map(|r| r.timestamp),
|
||||
created_time: timestamp,
|
||||
last_modified: timestamp,
|
||||
content_hash,
|
||||
size_bytes,
|
||||
phash_64: perceptual.map(|h| h.phash_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: None,
|
||||
date_taken_source: resolved_date
|
||||
.map(|r| r.source.as_str().to_string()),
|
||||
};
|
||||
|
||||
if let Ok(mut dao) = exif_dao.lock() {
|
||||
@@ -2382,6 +2390,16 @@ fn process_new_files(
|
||||
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 {
|
||||
library_id: library.id,
|
||||
file_path: relative_path.clone(),
|
||||
@@ -2408,15 +2426,14 @@ fn process_new_files(
|
||||
.and_then(|e| e.aperture.map(|v| v as f32)),
|
||||
shutter_speed: exif_fields.as_ref().and_then(|e| e.shutter_speed.clone()),
|
||||
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,
|
||||
last_modified: timestamp,
|
||||
content_hash,
|
||||
size_bytes,
|
||||
phash_64: perceptual.map(|h| h.phash_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: None,
|
||||
date_taken_source: resolved_date.map(|r| r.source.as_str().to_string()),
|
||||
};
|
||||
|
||||
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
|
||||
|
||||
Reference in New Issue
Block a user