neatpic
v0.2.0
Published
CLI to organize Fujifilm trip photos into a central library
Downloads
232
Readme
neatpic
CLI to organize Fujifilm photo collections into a central library.
Install
npm install
npm run buildRun in dev
npm run dev -- status --library /path/to/PhotosLibrary layout
Photos/
library.toml
2026/
collections.toml
May Shanghai/
Capture/
Selected/
Output/
Trash/library.toml
timezone = "Asia/Shanghai"2026/collections.toml
[[collection]]
folder = "May Shanghai"
start = "2026-05-10"
end = "2026-05-18"
timezone = "Asia/Shanghai"User config
Default library resolution order:
--library <path>NEATPIC_LIBRARY$XDG_CONFIG_HOME/neatpic/config.toml~/.config/neatpic/config.toml- current working directory
User config format:
library = "/path/to/Photos"Commands
Create a new collection folder and config entry:
neatpic collection new --library /path/to/PhotosReorganize a year to match the current collections.toml mapping:
cd /path/to/Photos/2026
neatpic collection sort --dry-run
neatpic collection sort
neatpic collection sort --allOr resolve the library root first, then target a year explicitly:
NEATPIC_LIBRARY=/path/to/Photos neatpic collection sort --year 2026 --dry-runcollection sort workflow
- Update
YEAR/collections.tomlto reflect the new collection date ranges or folder names. - Run a preview first:
cd /path/to/Photos/2026
neatpic collection sort --dry-run- Review the planned moves.
- Apply the changes:
neatpic collection sort- Use
--allwhen you want to reorganize the full year, including files already inside named collections:
neatpic collection sort --all- Use
--yearwhen your current directory is outside the year folder and the library root comes from normal neatpic resolution:
NEATPIC_LIBRARY=/path/to/Photos neatpic collection sort --year 2026Behavior:
- default scope:
Unsorted/only --allscope:Unsorted/plus all named collections in the same year- bucket-preserving moves:
Selectedstays inSelected,Capturestays inCapture,Trashstays inTrash,Videostays inVideo - existing
.XMPsidecars move with the shot group - preview runs before apply
- apply confirmation defaults to
no - empty-dir pruning asks for confirmation and sends pruned dirs to
trash
Import files into collection Capture/ folders:
neatpic import ~/Imports ~/BackupDrive/DCIM --library /path/to/Photos --mode copyImport now runs in stages:
- scan sources
- read metadata and build an import plan
- show a preview with per-collection counts,
Unsortedcounts, and rough size - ask for confirmation with default
no - render a live import progress bar during file copy
Use --yes to skip the confirmation prompt for scripted runs:
neatpic import ~/Imports --library /path/to/Photos --yesSync Lightroom picks/ratings into Selected/ and Trash/:
neatpic sync --library /path/to/Photos --rating-threshold 1Show collection status:
neatpic status --library /path/to/PhotosPlayground
Repo-local sandbox for full command testing:
pnpm playground:reset
pnpm playground:smokeDocs and fixture map:
playground/README.md
Current behavior
- exact dedupe by SHA-256 on original media files
- RAW+JPG stay together as one shot group by stem
- XMP sidecars move with the shot group
- import previews the plan before writing files and confirmation defaults to
no - import shows live stage progress plus a custom copy progress bar
- collection matching uses collection timezone when present, otherwise library timezone
- unmatched imports fall back to
YEAR/Unsorted/ collection sortpreserves bucket names during moves:Capture,Selected,Trash,Videocollection sortscansUnsorted/by default;--allincludes named collections in the same year toocollection sortpreviews moves first, asks before apply, then asks before pruning empty dirs viatrash
