Skip to main content

Organisation Logo

Photo Renamer

lupaxa-photo-renamer safely copies or moves photographs and videos into consistent, date-based filenames. The console command is photo-renamer.

It reads image EXIF or video metadata when available, falls back to filesystem modification time in the default mode, detects common source apps from filenames, and reports progress with Rich.

Installation

Python 3.13 or newer is required.

pip install lupaxa-photo-renamer

Using a virtual environment is recommended:

python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
python -m pip install lupaxa-photo-renamer

For local development:

git clone https://github.com/lupaxa-miscellaneous-toolbox/photo-renamer.git
cd photo-renamer
python -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"

Video timestamp extraction uses MediaInfo. If it is missing, install it with your operating system's package manager (brew install mediainfo on macOS, apt install mediainfo on Debian/Ubuntu). auto still falls back to filesystem modification time.

Quick Start

PATH is the directory to scan. Preview the work first:

photo-renamer --dry-run --recursive ~/Pictures

Review the startup panel (root, output, operation, format, timestamp mode, file count). Then run the same command without --dry-run:

photo-renamer --recursive ~/Pictures

By default, files are copied, originals remain untouched, and results are written below PATH/renamed/. Only files directly inside PATH are scanned unless you add --recursive. Existing relative directories are preserved:

Pictures/holiday/day-1/IMG-20260801-WA0001.JPG
→ Pictures/renamed/holiday/day-1/2026-08-01_14-55-22.jpg

Use --move only when you intentionally want to remove each successfully processed source.

Common Examples

# Include a detected source label in each filename
photo-renamer --recursive --format source ~/Pictures

# --preserve-source is an alias for --format source
photo-renamer --preserve-source ~/Pictures

# Put a source folder inside each preserved relative path
photo-renamer --recursive --organise ~/Pictures
# → renamed/holiday/day-1/WhatsApp/2026-08-01_14-55-22.jpg

# Drop relative paths, retaining only source folders
photo-renamer --recursive --flatten --organise ~/Pictures
# → renamed/WhatsApp/2026-08-01_14-55-22.jpg

# Choose another output root and move instead of copy
photo-renamer --recursive --output /Volumes/Archive --move ~/Pictures

# Limit extensions and always use modification time
photo-renamer --recursive --include jpg,jpeg,heic --timestamp filesystem ~/Pictures

A relative --output resolves below PATH. --verbose and --quiet cannot be used together.

Flag Summary

Flag Purpose
--output DIR Output root; defaults to renamed under PATH
--recursive Scan subdirectories; off by default
--dry-run Plan and report without writing
--move Move files instead of copying
--timestamp auto|exif|filesystem Select the timestamp strategy
--format datetime|source|source-first Select the filename format
--preserve-source Alias for --format source
--organise Nest the detected source inside the relative path
--flatten Remove relative path segments from destinations
--include EXT,... Process only listed supported extensions
--exclude EXT,... Exclude listed extensions
--skip-existing Skip files already matching a target naming pattern
--force Process matching names even with --skip-existing
--timezone ZONE Convert timestamps to an IANA zone before naming
--log-file PATH Append a tab-separated audit log on real runs
--workers N Concurrent copy/move workers (default 1)
--yes / -y Assume yes for over-cap workers confirmation
--verbose / --quiet Increase or suppress terminal output

If both --format and --preserve-source are supplied, the explicit --format value wins.

--workers only parallelises copy or move. Planning stays sequential so collision-safe names stay stable. If --workers is greater than twice the detected CPU count, photo-renamer asks for confirmation (use --yes in CI).

--log-file is not written during --dry-run. Each line is timestamp, action, source, destination, source label, timestamp origin, and message.

Run photo-renamer --help for the authoritative command syntax.

Filename Formats

Format Example
datetime 2026-08-01_14-55-22.jpg
source 2026-08-01_14-55-22_WhatsApp.jpg
source-first WhatsApp_2026-08-01_14-55-22.jpg

Extensions are preserved and lowercased. datetime is the default.

For PATH/vacation/day-1/IMG-20260801-WA0001.jpg:

Options Destination below output
none vacation/day-1/2026-08-01_14-55-22.jpg
--organise vacation/day-1/WhatsApp/2026-08-01_14-55-22.jpg
--flatten 2026-08-01_14-55-22.jpg
--flatten --organise WhatsApp/2026-08-01_14-55-22.jpg

--organise nests the detected source inside the preserved relative path.

Supported Formats

Kind Extensions
Images JPG, JPEG, PNG, HEIC, HEIF, WebP, TIF, TIFF
Videos MP4, MOV, AVI, MKV, M4V, 3GP

Matching is case-insensitive. --include and --exclude filter this supported set; they do not enable arbitrary formats.

Timestamps

Every output name uses a resolved timestamp as YYYY-MM-DD_HH-MM-SS.

Mode Behaviour
auto Embedded metadata when available, otherwise filesystem mtime
exif Embedded only (EXIF or MediaInfo); missing dates fail that file
filesystem Always use filesystem mtime

Image priority: EXIF DateTimeOriginal, then DateTimeDigitized, then DateTime, then filesystem mtime when the mode allows fallback.

Video priority: MediaInfo recorded, creation, encoded, then tagged dates, then filesystem mtime.

--timezone ZONE accepts IANA identifiers such as Europe/London. The tool reads metadata only; it never rewrites EXIF or video tags.

Source Detection

Rules are case-insensitive; the first match wins. Detection uses the original filename only — not pixels or maker tags.

Label Patterns
WhatsApp IMG-…-WA…, VID-…-WA…
Telegram Photo_…, Video_…
Signal signal-…, or a name ending with _signal before its extension
Pixel PXL_…
Samsung eight digits then _ (for example 20260801_…)
iPhone IMG_ followed by four digits
Screenshot Screenshot…, Screen Shot…, Screen_…
Camera DSC_…, DSCF…
Unknown anything else

Safety

Guarantee Behaviour
Copy by default Moving requires --move
Dry run --dry-run performs no file or log writes
Never overwrite Collisions receive _001, _002, and so on
Output excluded The resolved output tree is never scanned
Continue on errors Per-file I/O errors are reported; later files still process
Non-zero on failure The command exits non-zero if any file failed

Unreadable subdirectories are skipped without aborting the run. A mistyped PATH is a configuration error.

Exit Codes

Code Meaning
0 Every scanned file succeeded or was intentionally skipped
1 At least one file failed (including missing metadata in exif mode)
2 Invalid configuration (unknown timezone, quiet+verbose, bad PATH, …)

Troubleshooting

Symptom What to try
No files scanned Add --recursive; check --include / --exclude
Missing metadata Use --timestamp auto or --timestamp filesystem
Video dates not found Install MediaInfo; auto still falls back to mtime
Wrong wall-clock time Pass an IANA zone such as --timezone Europe/London
Unexpected _001 suffix Destination already existed or was reserved in the same run

Files inside the resolved output directory are ignored. Symlinks are not followed. --timestamp exif fails any file that has no embedded date.

FAQ

Question Answer
Does it change metadata? No. It copies or moves files and changes destination names only.
Does it find duplicate photos? No. Collisions prevent overwrites; they are not content hashes.
Can I undo a move? Not in v1. Prefer copy mode, --dry-run, and --log-file.
What does --preserve-source preserve? The source label in the filename (--format source), not originals.
Where do unknown sources go? They use the Unknown label when naming or organising by source.
Can it overwrite a file? No. Unique suffixes and exclusive destination creation prevent it.

Testing

make init
make python-install-dev
make python-check

Or without Make:

python -m pip install -e ".[test]"
pytest

Coverage for lupaxa.photo_renamer is reported by default.

This project is released under the MIT License; see LICENCE.

The Lupaxa Project Footer

Metadata

Release files for lupaxa-photo-renamer 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lupaxa-photo-renamer 0.1.6
File Size Uploaded
lupaxa_photo_renamer-0.1.6.tar.gz 18.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lupaxa-photo-renamer 0.1.6
File Interpreter ABI Platform
lupaxa_photo_renamer-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 42.4 kB

Release files / lupaxa_photo_renamer-0.1.6.tar.gz

Download URL lupaxa_photo_renamer-0.1.6.tar.gz
Size 18.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b855b0afcbadb45d179c9e290ca886c9d2ad55589eaab6cbeb55084e82834e45
BLAKE2b-256 checksum
How to use checksums
e94aea2bcbfe4e3ed13f0024cf0172f9ae9e41ae0e2762b014672336cecfea3e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / lupaxa_photo_renamer-0.1.6-py3-none-any.whl

Download URL lupaxa_photo_renamer-0.1.6-py3-none-any.whl
Size 24.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
87cac57b662ecf41c5237f042cf946eaa68c97c38d01500e4a3f1e1f58b1831f
BLAKE2b-256 checksum
How to use checksums
cd844af37415db8a471d687e2fb7ddf1e0a45fda4e6812e5b9a95c281ad9a172
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.7

2 release files

This release

0.1.6 This release

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page