Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PhotoTool

A command-line tool for efficient photography workflow management. Organizes photos by capture date and synchronizes between drives.

Features

  • Import from SD: Copy (never move) photos from SD cards into organized YYYY.MM folders
  • Auto-organization: Uses EXIF capture date; falls back to file mtime, and routes truly-undated files to an Unknown-Date/ folder instead of guessing "today"
  • Edited versions & sidecars grouped: an original (DSF7942.RAF/.JPG), its edits (DSF7942-1.JPG, DSF7942-HDR.heic, DSF7942 edited.jpg, DSF7942(1).png) and its sidecars (.xmp, .fp2, ...) are all filed in the same date folder as the original
  • Integrity-first copies: Every file is copied to a temp file, checksum-verified, then atomically renamed into place — a partial/corrupt file is never left at the destination
  • No silent overwrites: A same-name file with different content is kept as name__dup1.ext, never clobbered
  • Single-copy guarantee: Each physical file is copied exactly once (deduped work-list + per-destination locking), so concurrent workers can't corrupt a shared destination
  • Verify before wipe: A read-only "Verify card against archive" mode confirms every file on a card has a byte-identical (checksum) copy in the archive — catching un-imported photos and silently corrupted archive copies — before you ever format the card
  • Drive sync: Hardened rsync integration — validates paths, refuses a --delete against an empty/unmounted source, and previews deletions with a dry-run before doing anything destructive
  • Parallel processing: Multi-threaded copy phase for faster imports

Installation

Using uv (recommended)

# Install uv if you don't have it
curl -LsSf https://astral.sh/uv/install.sh | sh

# Create the environment and install dependencies from pyproject.toml / uv.lock
uv sync

# Run the app
uv run myphotoscript.py

Using pip

python -m venv .venv
source .venv/bin/activate   # Linux/macOS  (.venv\Scripts\activate on Windows)
pip install .

Requirements

  • Python 3.12+
  • ExifTool installed on your system (brew install exiftool)
  • Rsync (for the sync functionality)

Usage

Run the script directly:

python myphotoscript.py

Import from SD

Copies and organizes files from an SD card to your archive:

  • Sorts each file into a YYYY.MM folder based on its EXIF capture date
  • Files with no usable EXIF date fall back to the file's modification time; if even that is implausible they go to Unknown-Date/ (reported at the end so you can sort them)
  • Sidecars (.xmp, ...) are filed alongside their same-stem photo
  • Each file is copied exactly once, checksum-verified, and written atomically
  • A same-name file whose content differs is preserved as name__dup1.ext — nothing is ever overwritten
  • Re-running an import is idempotent: identical files already present are skipped
  • Edited versions stay with the original: DSF7942.RAF, DSF7942-1.JPG, DSF7942-HDR.heic, DSF7942 edited.jpg, DSF7942(1).png and their sidecars are all filed in the same date folder as the original DSF7942 — each copied exactly once (no duplication across folders)

Verify card against archive

A read-only safety check to run before formatting a card:

  • Confirms every real file on the card has a byte-identical copy somewhere in the archive (checksum, not just size — a same-size corrupted copy is caught)
  • Flags MISSING (on the card but not imported), CONTENT MISMATCH (archive copy differs/corrupt), SIZE MISMATCH, and UNREADABLE (card read errors), then prints a clear SAFE TO WIPE / NOT SAFE TO WIPE verdict
  • Ignores hidden/AppleDouble files; nothing is ever written or deleted
  • Aborts gracefully if the card disconnects mid-check

Runs from the menu, or non-interactively (scriptable; exit code 0 = safe, 1 = not safe, 2 = bad usage):

uv run myphotoscript.py verify /Volumes/SDCARD/DCIM /Volumes/ssd/imgs
uv run myphotoscript.py verify /Volumes/SDCARD/DCIM /Volumes/ssd/imgs --size-only

Sync drives (rsync)

Synchronizes your photo library between drives, defensively:

  • Validates that source and destination exist (and that the source is non-empty before any --delete)
  • Option to exclude all video formats (case-insensitive), not just .mov
  • Optional --checksum for content-based sync (repairs corrupted same-size files)
  • --delete always runs a --dry-run first and lists what would be removed, then asks for confirmation

Testing

The project includes a test suite built with pytest to ensure reliability.

Running Tests

uv run --with pytest pytest -q

Test Coverage

The suite exercises the data-integrity behaviors directly (with real files, not mocks):

  • Conflict safety: a same-name/different-content file is kept as __dup1, the original is preserved
  • Duplicate detection: real size+checksum comparison; re-imports are idempotent
  • Atomic + verified copy: checksum mismatch fails closed and leaves no partial file
  • Date routing: EXIF → mtime → Unknown-Date/; invalid dates (0000:00:00, month 13) are rejected
  • Single-copy planning: every physical file is planned exactly once; skip_mov drops companion videos too
  • Verify-before-wipe: detects missing files and same-size corrupted archive copies; ignores hidden files
  • Rsync guardrails: refuses --delete against an empty source; previews deletions; passes --checksum

Adding Tests

When adding new features, please extend the test suite with appropriate test cases.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages