Photo Date Editor
Version 0.2.0
A self-hosted browser UI for manually dating scanned photographs. The browser receives temporary read/write access to a local computer or Chromebook folder, sends one JPEG at a time to the Docker backend for ExifTool processing, and overwrites the same local file after processing.
Current scope
- JPG and JPEG
- Local directory picker in supported Chromium browsers
- Three-column photo workflow
- Exact date, month/year, year-only, approximate year
- Optional time; unknown time defaults to noon
- Description and keywords
- In-place overwrite: no browser download and no
_originalfile - Saved/skipped/pending/failed state stored in browser local storage
- Existing metadata is read from the JPEG when a photo is selected
- Files previously edited by this app are recognized from their XMP marker, even in a different browser
- Keyboard navigation
Requirements
- Docker with Docker Compose
- A current Chromium browser with the File System Access API enabled, such as Chrome, Edge, or Brave with the relevant flag enabled
- HTTPS when accessed from another device
- A backup of irreplaceable scans before testing any metadata editor
The browser File System Access API is required. Firefox and Safari are not supported by this first version.
Install
cp .env.example .env
nano .env
docker compose up -d --build
Example .env:
APP_URL=https://photos.example.internal
APP_PORT=8080
APP_NAME=Photo Date Editor
MAX_UPLOAD_MB=150
LOG_LEVEL=INFO
Open the configured HTTPS URL through Caddy, click Open photo folder, and grant read/write access.
Upgrade from 0.1
Replace the project files with this version and rebuild:
docker compose up -d --build
The existing .env can be kept. Browser progress from 0.1 remains compatible. A normal refresh should load the new service-worker cache; use a hard refresh if an old UI remains visible.
Caddy example
Caddy may run in another container or host. Proxy to the Docker host and exposed port:
photos.example.internal {
@lan remote_ip private_ranges
handle @lan {
reverse_proxy 192.168.1.20:8080
}
respond 403
}
The browser directory picker requires a secure context. Use a certificate trusted by the client computer. A real domain with internal DNS is usually easier than deploying a private CA certificate to every family device.
What happens when Save is clicked
- The browser reads the selected local JPEG.
- It sends the file and entered fields to
/api/process. - ExifTool modifies a temporary file inside the container using
-overwrite_original. - The backend returns the modified JPEG.
- The browser writes the returned bytes over the selected original file with
createWritable(). - The backend temporary directory is deleted automatically.
No second photo is intentionally left on the client computer or Docker host.
Date behavior
EXIF date fields require a complete timestamp:
| Selected precision | EXIF value | XMP DateCreated |
|---|---|---|
| Exact date | Chosen date | YYYY-MM-DD |
| Month and year | First day of month | YYYY-MM |
| Year only | January 1 at noon | YYYY |
| Approximate year | January 1 at noon | YYYY |
The chosen precision is also written into XMP Photoshop Instructions.
Keyboard shortcuts
EnterorCtrl+Enter: Save & NextCtrl+S: Save the current photo and remain on itLeft/Right: Previous / next photo when not typingS: Mark the photo as skipped and open the next photo when not typingC: Copy previous values when not typing
Important limitations
- The app cannot silently access a local folder. The user must choose it and approve read/write access.
- Browser permission may need to be granted again after closing/reopening the browser.
- Saved and skipped progress is stored per browser and folder name. The app also reads its own XMP marker from edited files so saved metadata can be recognized on another browser.
- Overwriting a file through the browser generally changes its filesystem modified time to the time of the save. EXIF/XMP photo dates are independent of that filesystem timestamp.
- Preview rotation is visual only in this MVP; it does not rotate image pixels or write orientation metadata.
- Test with copies first, then use your own normal backup routine for the originals.
Planned format expansion
The backend already uses ExifTool, which is a good foundation for later format support. The next likely steps are:
- HEIC/HEIF preview and metadata round-trip testing on ChromeOS.
- Canon RAW (
CR2,CR3) and Nikon RAW (NEF,NRW). - Sidecar XMP mode for RAW files, because modifying proprietary RAW containers directly is a different risk profile from JPEG.
- Optional server-side folder mode for photos stored on NAS-mounted storage.