photo-organizer
v0.1.0
Published
Organize, deduplicate, and rename photos by EXIF metadata. Flat storage + SQLite + dynamic views.
Maintainers
Readme
photo-organizer
Organize, deduplicate, and rename photos by EXIF metadata. Flat storage + SQLite + dynamic views, with native HEIC support.
A local-first photo management CLI built for Mac/Linux. Imports photos from a folder (or directly from a connected iPhone), extracts EXIF metadata, detects duplicates, and stores everything in a SQLite database — so you can query by date, city, device, or perceptual similarity.
Features
- Flat storage + SQLite index — no folder hierarchy lock-in; query any view dynamically
- Native HEIC support — works with iPhone photos out of the box (via bundled ExifTool)
- iPhone USB pull —
po pullreads photos directly from a connected iPhone (requireslibimobiledevice) - Deduplication — exact match (SHA-256) + similar match (perceptual hash)
- Reverse geocoding — GPS coordinates → city/country, fully offline (no API key)
- Smart naming —
YYYYMMDD_HHMMSS_iPhone15Pro.jpgwith collision handling - Claude Code Skill — built-in
SKILL.mdfor conversational photo organization
Install
# Try without installing
npx photo-organizer --help
# Or install globally
npm install -g photo-organizerNote: Requires Node.js ≥ 20. Native dependencies (
better-sqlite3,sharp,exiftool-vendored) are downloaded as prebuilt binaries.
Quick Start
# 1. Initialize a photo library
po init ~/Pictures/library
# 2. Import photos (always dry-run first)
cd ~/Pictures/library
po import ~/Downloads/phone-photos --dry-run
po import ~/Downloads/phone-photos
# 3. See what you have
po report
# 4. Query by metadata
po query --year 2024 --city Tokyo
po query --device iPhone
# 5. Find duplicates
po dedup --strategy bothCommands
| Command | Description |
|---------|-------------|
| po init [dir] | Initialize a photo library with metadata database |
| po import <source> | Import photos from a directory |
| po pull | Pull photos directly from a connected iPhone (USB) |
| po scan | Re-scan library and refresh metadata |
| po dedup | Find and mark duplicate photos |
| po rename | Batch rename to standard format |
| po report | Generate library statistics |
| po query | Query photos by metadata |
Run po <command> --help for full options.
Example Workflows
Import from iPhone via USB:
po pull --dry-run # See what's on the phone
po pull --folder 105APPLE # Pull a specific DCIM folder
po pull --limit 100 # Pull a sampleFind duplicates:
po dedup --strategy exact # SHA-256 only (fast)
po dedup --strategy similar # Perceptual hash
po dedup --strategy both # BothQuery the library:
po query --city Tokyo --year 2024
po query --device iPhone --limit 50
po query --no-exif # Photos missing EXIF
po query --duplicates # Marked duplicates
po query --format json # JSON output for pipingDirect database access (advanced):
sqlite3 .photo-organizer/metadata.db \
"SELECT city, COUNT(*) FROM photos GROUP BY city ORDER BY COUNT(*) DESC LIMIT 10"File Naming
Photos are renamed using EXIF metadata:
- With date + device:
20240315_143022_iPhone15Pro.jpg - Without device:
20240315_143022.jpg - Without EXIF:
NODATE_a1b2c3d4.jpg - Collisions:
..._2.jpg,..._3.jpg
iPhone USB Pull (Optional)
To use po pull, install libimobiledevice:
brew install libimobiledeviceThen unlock your iPhone, connect via USB, and trust the computer when prompted.
Architecture
photo-organizer/
├── library-root/ # Your photos (flat storage)
│ ├── 20240315_143022_iPhone15Pro.jpg
│ ├── 20240316_091505_iPhone15Pro.heic
│ └── .photo-organizer/
│ ├── metadata.db # SQLite index
│ └── config.jsonThe database holds all metadata (date, GPS, city, device, EXIF, perceptual hash). Files on disk are flat — no nested folders. All "views" (by year, by city, by device) are dynamic queries against the database.
Use as a Claude Code Skill
This package ships with a SKILL.md file. When installed, Claude Code can use it to organize your photos through conversation:
"Pull my latest photos from iPhone and find any duplicates"
"How many photos did I take in Tokyo last year?"
"Rename all my photos to use the standard naming format"
Tech Stack
- TypeScript (ESM, Node 20+)
- better-sqlite3 — synchronous SQLite, WAL mode
- exiftool-vendored — bundled ExifTool, supports all formats including HEIC
- sharp + sharp-phash — image processing and perceptual hashing
- commander — CLI framework
- offline-geocode-city — 217KB offline reverse geocoder
Development
git clone https://github.com/wuyuxiangX/photo-organizer
cd photo-organizer
npm install
npm test # 77 tests
npm run typecheck
npm run buildLicense
MIT © wuyuxiangX
