Photocopier
Back up your photos and videos to external drives — organised by date, de-duplicated, and verified.
Photocopier copies your media onto a backup drive into a tidy TYPE/YYYY-MM/ structure,
never copies the same photo twice (even across runs and messy source folders), verifies
every copy byte-for-byte before trusting it, and can prove to you that everything made it
before you delete the originals.
dest/
├── JPG/
│ ├── 2024-05/2024-05-14_18-32-01_IMG_2041.jpg
│ └── 2024-06/…
├── HEIC/2024-06/…
├── CR3/2024-06/…
└── MP4/2024-06/…
Install
Requires Python 3.10+ and exiftool (for photo dates):
brew install exiftool pipx # macOS
pipx install photocopier-cli # the command is still `photocopier`
Usage
1. See what you have
photocopier stock ~/Pictures
Reports how many files photocopier recognises as media, and lists any extensions it
would skip (check those for anything you care about, e.g. .zip archives of photos).
2. Back up
photocopier copy ~/Pictures /Volumes/MyBackupDrive/photos
- Files are organised by the date the photo was taken (EXIF), falling back to the file's modification date.
- Duplicate content is skipped — a record of everything ever copied lives in
copied_hashes.txtinside the destination. Don't delete that file. - Every copy is verified: the destination bytes are re-read and compared to the source before the file is recorded as done.
- Interrupted runs (unplugged drive, Ctrl-C) are safe: in-flight files are cleaned up and re-copied next time.
- Add
--dry-runto preview what would be copied without writing anything, and-vto list each file as it's handled.
Run it again any time — already-copied files are skipped, so incremental backups are fast and safe.
3. Verify before deleting anything
photocopier verify ~/Pictures /Volumes/MyBackupDrive/photos
Exit code 0 means every media file in the source is present in the destination — it is safe to delete the source. Any missing or unreadable files are listed explicitly.
For a paranoid check that re-reads every file on the destination drive (catching files that were corrupted after they were copied):
photocopier verify --deep ~/Pictures /Volumes/MyBackupDrive/photos
Auditing two backup drives against each other (recommended if you keep two copies):
photocopier verify --deep /Volumes/DriveA/photos /Volumes/DriveB/photos
photocopier verify --deep /Volumes/DriveB/photos /Volumes/DriveA/photos
Both exiting 0 means the two archives hold identical content.
Supported formats
Photos: jpg, jpeg, png, gif, heic, heif, webp, avif, tif, tiff, bmp RAW: cr2, cr3, dng, nef, arw, orf, raf, rw2 Video: mp4, mov, m4v, mkv, avi, mpg, mpeg, 3gp, 3g2, mts, m2ts, vob, flv, wmv, webm
How de-duplication works
Each file's content is hashed (MD5, read in chunks). Hashes of everything ever copied
are stored in copied_hashes.txt in the destination. A file whose content hash is
already recorded is skipped, regardless of its name or folder — so re-organising your
source folders never causes duplicate copies.
The hash algorithm is MD5 for continuity with existing archives; it's used for de-duplication, not security.
Development
git clone https://github.com/dsmurrell/photocopier
cd photocopier
python -m venv .venv && ./.venv/bin/pip install -e ".[dev]"
./.venv/bin/pytest
Disclaimer
Photocopier is free, open-source software provided as is, without warranty of any
kind (see LICENSE). It is designed so you never have to take it on
trust — every copy is verified byte-for-byte, and the verify command lets you
prove a backup is complete before deleting anything. Please follow the golden
rules anyway: keep two backup copies on separate drives, and only delete
originals after photocopier verify reports ✓.
License
MIT
Metadata
Release files for photocopier-cli 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| photocopier_cli-0.9.0.tar.gz | 11.5 kB | Without provenance | Details for photocopier_cli-0.9.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| photocopier_cli-0.9.0-py3-none-any.whl | Python 3 | none | any | Details for photocopier_cli-0.9.0-py3-none-any.whl |
Total release size: 22.6 kB
Release files / photocopier_cli-0.9.0.tar.gz
| Download URL | photocopier_cli-0.9.0.tar.gz |
|---|---|
| Size | 11.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a53f7fceabf2548c5f7293bfc5bcacde7a722bf09289aca8f3123cc35e40a688
|
|
BLAKE2b-256 checksum How to use checksums |
42b825381dcd11f059ed0c4d803aeaedca7c992b351716dd71e7d068c2fefe74
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.4
|
Release files / photocopier_cli-0.9.0-py3-none-any.whl
| Download URL | photocopier_cli-0.9.0-py3-none-any.whl |
|---|---|
| Size | 11.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d96245ef6affbdfbdbb5c212d64aaa306c3ff8e63fc1f0441052704566bcf1a2
|
|
BLAKE2b-256 checksum How to use checksums |
7b1507a56014013a3b859cbe8ce206ff2177c6eb56a1a964db155cf2124b0539
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.4
|
Security for photocopier-cli 0.9.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.