Skip to content

Repository files navigation

immich-timeline-fixer

Fixes Immich timeline dates by looping through every asset in a library and, for each one:

  1. Downloads the original file and reads its real EXIF date with exiftool. Immich's own reported EXIF date (exifInfo.dateTimeOriginal) has been observed in the wild to silently diverge from what's actually embedded in the file — one confirmed case had correct camera fields (make/model/lens/ ISO/etc.) but a dateTimeOriginal that matched neither the file's real DateTimeOriginal nor any other visible timestamp, and re-running Immich's own "refresh-metadata" job did not correct it. So this tool never trusts Immich's stored EXIF data — it always checks the actual file.
  2. If that real EXIF date differs from the asset's current timeline date, updates it.
  3. If the file has no usable EXIF date at all (e.g. WhatsApp exports, screenshots), falls back to guessing a date from the filename (e.g. IMG_20230115_143022.jpg, PXL_..., Unix-epoch filenames) or, failing that, the containing folder path (e.g. .../2023-01-15 vacation/photo.jpg). This fallback can be disabled with --no-filename-fallback.
  4. If neither yields a plausible date, the asset is left untouched and logged as unresolved.

Every asset that's already correct is left alone (no-op writes are skipped). Every processed asset — exif, filename-guess, skipped, unresolved, or failed — is written to a CSV audit report, along with a live progress line on stdout.

Because every asset requires downloading the original file, a full run is bandwidth- and time-heavier than a metadata-only tool. Use --limit to test on a small batch first, and --concurrency to process several assets in parallel on a large library.

Install

Requires uv and the exiftool binary on your PATH (e.g. brew install exiftool).

uv sync

Usage

uv run immich-timeline-fixer --host https://immich.example.com --api-key <key>

By default this applies changes immediately. Use --dry-run to preview what would change first, optionally combined with --limit to test on a small batch:

uv run immich-timeline-fixer --host https://immich.example.com --api-key <key> --dry-run --limit 20

Then inspect the printed report / CSV before running for real without --dry-run.

Options

Flag Env var Description
--host IMMICH_HOST Immich server URL
--api-key IMMICH_API_KEY Immich API key
--timezone IMMICH_TIMEZONE IANA timezone applied to dates that don't carry their own offset (default: system timezone)
--config Path to a TOML config file
--dry-run Report changes without writing them
--limit Only process the first N assets
--min-year Reject implausible dates before this year (default: 1990)
--filename-fallback / --no-filename-fallback Guess a date from the filename/path when the file has no EXIF date (default: enabled)
--report-path Where to write the CSV audit report (default: timestamped file in the current directory)
--concurrency Number of assets to download+process in parallel (default: 1, sequential)

Settings are resolved with precedence CLI flags > environment variables > config file > built-in defaults.

Config file

# immich-timeline-fixer.toml
host = "https://immich.example.com"
api_key = "..."
timezone = "Europe/Amsterdam"
min_year = 1990
filename_fallback = true
concurrency = 4
uv run immich-timeline-fixer --config immich-timeline-fixer.toml

Report format

The CSV audit report has one row per processed asset:

Column Description
asset_id Immich asset ID
filename Original filename
action One of exif, filename-guess, skipped-unchanged, unresolved, failed
old_date The asset's timeline date before processing
new_date The date it was updated to (empty unless action is exif or filename-guess)
error Error message (only set when action is failed)

The same information is also printed live to stdout as each asset is processed, followed by a summary count per action when the run finishes.

Development

uv run pytest

License

GNU Affero General Public License v3.0 (AGPL-3.0).

About

FIxes your Immich timeline

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages