Skip to content

Repository files navigation

RAW Manager

A local-first RAW photo management system for home networks. Drop RAW files into an import directory and RAW Manager automatically ingests them — deduplicating, extracting EXIF metadata, generating thumbnails, and making them available through a web gallery for browsing, rating, and culling.

Personal project. This app is built around my own RAW archiving and culling workflow. It is opinionated by design — the development pipeline maps directly to how I personally work with RAW files from shoot to final library.


My Workflow

RAW Manager sits between the camera SD card and a finished photo library. The steps I follow:

SD Card (automounted to /import)
    │
    ▼
RAW Manager — ingest & deduplicate
    │
    ▼
Cull "pending" RAWs — pick ✓ or reject ✗
    │
    ▼
Move picks to "In Darkroom"  ──►  Darktable opens DARKROOM_DIR,
                                   edits RAWs, exports JPEGs
    │
    ▼
Upload JPEGs to Immich (main photo library)
    │
    ▼
Mark RAWs as "Developed" in RAW Manager

In practice:

  • My NAS automounts the SD card to the IMPORT_DIR path (/import), so importing is just inserting the card
  • I open RAW Manager in a browser, filter by pending, and quickly reject or keep shots using keyboard shortcuts
  • Keepers I flag as in darkroom — RAW Manager hard-links those files into the DARKROOM_DIR folder
  • Darktable watches that folder; I develop the RAWs there and export JPEGs
  • JPEGs go into Immich, my main photo archive
  • Once processed I mark the RAWs as developed to keep the library clean

Planned: Immich integration

In the near future I plan to close the loop automatically:

  • Watch the Darktable export folder and auto-upload JPEGs to Immich via the Immich API
  • When an image is confirmed stored in Immich, automatically set the corresponding RAW to developed
  • This would make the entire pipeline — from card insert to library — fully hands-off after the culling step

Screenshots

Gallery

Gallery view showing infinite-scroll photo grid with filters

Lightbox

Lightbox view with full photo and EXIF metadata panel

Compare Mode

Side-by-side compare mode with synchronized zoom


Features

Ingestion

  • Automatic watchdog — drop RAW files into IMPORT_DIR; they're processed without manual steps
  • Deduplication — SHA256 hashing silently discards duplicate files
  • EXIF metadata — camera, lens, ISO, aperture, shutter speed, focal length, date
  • Tone-mapped thumbnails — JPEG previews rendered from RAW data using camera white balance

Gallery & Filtering

  • Infinite-scroll grid, dark-themed, responsive
  • Filter by rating, archive status, development status, camera model, and date range
  • Filename search
  • Month-based timeline scrubber for navigating large libraries

Culling Workflow

  • 5-star ratings — rate individual or batches of photos
  • Archive status — pending / keep / discard
  • Development pipeline — consideration → in darkroom → developed
  • Darkroom folder — files marked in darkroom are automatically hard-linked to DARKROOM_DIR for external editing

Lightbox

  • Full-screen photo viewer with EXIF panel
  • Zoom & pan — mouse wheel zoom, click-drag pan, double-click to toggle 100%/fit, pinch-to-zoom
  • Navigate with keyboard or arrow buttons
  • Rate, set status, set dev status, download original, or delete directly from the lightbox

Compare Mode

  • Select exactly 2 photos → Compare button appears in the selection bar
  • Side-by-side view with synchronized zoom & pan (lock/unlock sync)
  • Overlay / swipe mode — drag a divider to reveal each photo

Uploads

  • Drag & drop RAW files anywhere on the page — a full-screen drop target appears
  • Or click Upload in the header to browse for files
  • Files are validated against supported extensions before upload
  • Queued files are picked up automatically by the ingestion service

Batch Operations

  • Select any number of photos; apply rating, status, or dev status to all in one API call
  • Batch download — selected originals as a streaming ZIP archive

Notifications

  • Toast notifications for every action: ratings, status changes, errors, rebuild complete

Supported RAW Formats

Extension Camera Brand
.cr2 Canon (DSLR)
.cr3 Canon EOS R series
.arw Sony
.nef Nikon
.rw2 Panasonic
.raf Fujifilm
.orf Olympus
.dng Adobe / Universal
.heif / .hif Various (experimental)

Quick Start

Requirements: Docker and Docker Compose.

git clone <repo-url> RAW-Manager
cd RAW-Manager
docker compose up --build -d

Open http://localhost:8000 in your browser.

To import photos, copy RAW files into ./data/import/. The ingestion service processes them automatically — or use the Upload button in the web UI.


How It Works

Ingestion Pipeline

When a RAW file lands in the import directory:

  1. Settle check — waits 2 s, confirms file size is stable (guards against partial writes)
  2. Deduplication — computes SHA256; skips if hash already exists in the database
  3. Metadata extraction — runs exiftool to pull all EXIF fields
  4. Safe copy — moves the file to ORIGINALS_DIR/YYYY/MM/filename with hash verification
  5. Thumbnail generation — renders a tone-mapped JPEG via rawpy + Pillow, resized to THUMB_SIZE
  6. Database insert — writes the record to SQLite
  7. Cleanup — removes the original from the import directory

Directory Layout

data/
├── import/        # Drop RAW files here (or upload via the web UI)
├── originals/     # Organized storage: YYYY/MM/filename
├── thumbnails/    # Content-addressed JPEGs (named by SHA256 hash)
├── darkroom/      # Hard-links to files marked "in darkroom"
└── raw_manager.db # SQLite database (WAL mode)

Keyboard Shortcuts

Gallery (no lightbox open):

Key Action
1 – 5 Batch rate selected photos
K Batch keep selected
X Batch reject selected

Lightbox:

Key Action
1 – 5 Set star rating
P Mark pending
K Mark keep
X Mark discard
← / → Previous / next photo
Escape Close lightbox
Scroll Zoom in/out
Double-click Toggle 100% / fit

Compare mode:

Key Action
V Toggle side-by-side / overlay
S Toggle sync lock
Escape Close compare

Environment Variables

Variable Default Description
IMPORT_DIR ./data/import Directory watched for incoming RAW files
ORIGINALS_DIR ./data/originals Organized RAW storage
THUMB_DIR ./data/thumbnails Generated JPEG thumbnails
DB_PATH ./data/raw_manager.db SQLite database path
DARKROOM_DIR ./data/darkroom Darkroom file copies
API_HOST 0.0.0.0 API bind address
API_PORT 8000 API bind port
THUMB_SIZE 800 Maximum thumbnail dimension (px)

API Reference

Interactive docs (Swagger UI): http://localhost:8000/docs

Method Path Description
GET /api/photos List photos with filters and cursor pagination
GET /api/photos/count Count matching photos
GET /api/photos/timeline Photo counts grouped by year-month
GET /api/photos/{id} Single photo by ID
PATCH /api/photos/{id} Update rating, status, dev_status
PATCH /api/photos/batch Batch-update multiple photos in one transaction
DELETE /api/photos/{id} Delete photo record and original file
GET /api/files/thumb/{id} Serve thumbnail JPEG (immutable cache)
GET /api/files/original/{id} Serve original RAW file
POST /api/upload Upload RAW files to IMPORT_DIR (multipart, max 20)
POST /api/download Stream selected originals as ZIP
POST /api/rebuild Trigger background database rebuild
GET /api/rebuild/status Rebuild progress
GET /api/stats Aggregate library statistics
GET /api/cameras Distinct camera models
GET /api/health Health check

Project Structure

RAW-Manager/
├── Dockerfile               # Multi-stage: Node builds frontend, Python serves everything
├── docker-compose.yml       # api + ingestion services
├── requirements.txt
├── backend/
│   ├── main.py              # FastAPI app — all endpoints
│   ├── database.py          # SQLite CRUD layer with schema versioning
│   ├── ingestion.py         # Watchdog-based ingestion engine
│   └── utils/
│       ├── exif_utils.py    # EXIF extraction via exiftool
│       ├── file_utils.py    # SHA256, safe copy, path organisation
│       └── raw_processing.py # RAW → JPEG via rawpy + Pillow
├── docs/
│   ├── screenshot-gallery.jpg
│   ├── screenshot-lightbox.png
│   └── screenshot-compare.png
└── frontend/
    ├── vite.config.js       # Dev server on :5173, proxies /api → :8000
    ├── tailwind.config.js   # Dark theme, custom surface palette
    └── src/
        ├── App.jsx
        ├── components/
        │   ├── CompareView.jsx      # Side-by-side / overlay compare
        │   ├── DropZone.jsx         # Drag-and-drop upload
        │   ├── Gallery.jsx          # Infinite-scroll photo grid
        │   ├── Lightbox.jsx         # Full-screen viewer with zoom/pan
        │   ├── PhotoCard.jsx
        │   ├── Settings.jsx         # Rebuild dialog
        │   ├── StarRating.jsx
        │   ├── TimelineScrubber.jsx
        │   ├── Toolbar.jsx
        │   └── ToastContainer.jsx   # Toast notification renderer
        └── hooks/
            ├── useKeyboardShortcuts.js
            ├── usePhotos.js
            ├── useToast.jsx
            └── useZoomPan.js        # Zoom/pan state and event handlers

Development Setup

Prerequisites

# Debian / Ubuntu
sudo apt install libimage-exiftool-perl libraw-dev

Backend

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload   # API
python -m backend.ingestion                                       # Ingestion (separate terminal)

Frontend

cd frontend
npm install
npm run dev   # http://localhost:5173 — proxies /api to :8000

Stack

Layer Technology
Backend Python 3.12, FastAPI, SQLite 3 (WAL), rawpy, Pillow, watchdog
Frontend React 18, Vite, Tailwind CSS 3
Deployment Docker, Docker Compose

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages