date_resolver: drop -fast2 so MP4 moov-at-end files resolve

For QuickTime/MP4 files whose `moov` atom sits at the end of the
file (non-faststart — common for Snapchat exports and any MP4
muxed without `-movflags +faststart`), `-fast2` causes exiftool
to skip the trailer and return no `CreateDate` /
`MediaCreateDate`, dropping the resolver to the `fs_time`
fallback for files that actually have a real capture date.

Reported cases:
  Snapchat-477624257.mp4
    fs_time: 2026-05-04 (today, file was just modified)
    real:    QuickTime CreateDate 2018-09-02
  action_compound_cc92e65b709d1deb895b4c2a9484fc6a.mp4
    fs_time: 2026-05-04
    real:    MediaCreateDate 2018-03-01

The waterfall pre-filters to files kamadak-exif couldn't read, so
the JPEG fast-path is already covered without `-fast2`. Paying
full-scan cost on the residual is the right trade. The per-tick
drain re-resolves `source = 'fs_time'` rows, so existing rows
recover automatically on the next watcher tick after deploy — no
SQL migration needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cameron Cordes
2026-05-07 12:40:50 -04:00
parent ac8d17fb22
commit c128596470

View File

@@ -230,12 +230,21 @@ fn exiftool_available() -> bool {
/// One-file exiftool invocation. Used by the upload + GPS-write paths,
/// which deal with one file at a time. The batch path uses
/// `exiftool_dates_batch` so we don't pay subprocess startup per row.
///
/// Notably absent: `-fast` / `-fast2`. For QuickTime/MP4 files whose
/// `moov` atom sits at the end (non-faststart, common for Snapchat
/// exports and any MP4 muxed without `-movflags +faststart`), `-fast2`
/// causes exiftool to skip the trailer and return no `CreateDate` /
/// `MediaCreateDate`, dropping us to the `fs_time` fallback for files
/// that actually have a real capture date. We pre-filter to files that
/// kamadak-exif couldn't read, so the JPEG fast-path is already covered
/// — paying full-scan cost on the residual is the right trade.
fn exiftool_date_single(path: &Path) -> Option<i64> {
if !exiftool_available() {
return None;
}
let mut cmd = Command::new("exiftool");
cmd.arg("-j").arg("-q").arg("-d").arg("%s").arg("-fast2");
cmd.arg("-j").arg("-q").arg("-d").arg("%s");
for tag in EXIFTOOL_DATE_TAGS {
cmd.arg(format!("-{}", tag));
}
@@ -261,7 +270,10 @@ fn exiftool_dates_batch(paths: &[&Path]) -> HashMap<PathBuf, i64> {
}
let mut cmd = Command::new("exiftool");
cmd.arg("-j").arg("-q").arg("-d").arg("%s").arg("-fast2");
// No `-fast2` — see exiftool_date_single for the rationale (QuickTime
// moov-at-end files miss CreateDate / MediaCreateDate when the trailer
// is skipped).
cmd.arg("-j").arg("-q").arg("-d").arg("%s");
for tag in EXIFTOOL_DATE_TAGS {
cmd.arg(format!("-{}", tag));
}