Skip to content

linconvidal/phototool

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

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages