exif-heal
A Python CLI that repairs missing EXIF timestamps and GPS data in photo libraries before import into Immich or Lightroom.
The problem
Photo libraries accumulate missing and incorrect metadata in predictable ways. Photos received through Messenger have their EXIF stripped entirely. Android screenshots inherit the OS file-creation date rather than a meaningful capture time. Batch-copied directories often end up with every file sharing the same modification time, making mtime useless for ordering.
Photo management tools like Immich and Lightroom sort, group, and display photos by EXIF DateTimeOriginal. When that field is absent or wrong, photos appear at the wrong time or not at all. Fixing this manually at scale is impractical.
What it does
exif-heal applies a layered inference strategy to reconstruct timestamps and GPS data, working from most reliable to least:
- Filename parsing — extracts dates from Android IMG_* patterns, Messenger received_* filenames, and screenshot naming conventions
- Neighbor interpolation — estimates timestamp from surrounding photos in the same camera session, with awareness of session boundaries
- Bulk-copy detection — identifies directories where more than 80% of files share an mtime, flagging the mtime as unreliable
- GPS propagation — copies GPS coordinates from nearby photos taken within a configurable time window, validated by haversine distance
- Confidence gating — assigns HIGH, MED, or LOW confidence to each inference; changes below the configured threshold are skipped
- Provenance tags — writes XMP metadata documenting the inference source and confidence level for every change made
Operations are dry-run by default. Applying changes requires an explicit flag. A SQLite cache tracks what has been processed, making repeated runs idempotent — running twice produces no additional changes.
Technical details
Python 3.11+, Click for the CLI interface, ExifTool as the metadata read/write backend (required binary). All ExifTool interaction uses JSON — no text output parsing. SQLite is used for the persistent metadata cache. Backups are optional and configurable.