90 lines
4.4 KiB
Markdown
90 lines
4.4 KiB
Markdown
# Self-Hosted Photo Manager — TODO
|
||
|
||
## Architecture
|
||
|
||
A self-built alternative to Immich: React/Vite frontend + Express/SQLite backend,
|
||
files stay on disk, DB indexes them, thumbnails are the only derived asset.
|
||
|
||
```
|
||
┌───────────────────┐ watches ┌──────────────────────┐
|
||
│ Photo folder(s) │ ─────────────────────▶ │ Ingestion Worker │
|
||
│ (disk / NAS) │ │ (chokidar file watch) │
|
||
└───────────────────┘ └───────────┬────────────┘
|
||
│ extract EXIF,
|
||
│ generate thumbnail,
|
||
│ compute pHash
|
||
▼
|
||
┌──────────────────────┐
|
||
│ SQLite metadata DB │
|
||
│ (photos, tags, │
|
||
│ albums, hashes) │
|
||
└───────────┬────────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────┐
|
||
│ Express API server │
|
||
└───────────┬────────────┘
|
||
│ REST
|
||
▼
|
||
┌──────────────────────┐
|
||
│ React/Vite frontend │
|
||
│ (timeline, albums, │
|
||
│ search, map) │
|
||
└──────────────────────┘
|
||
```
|
||
|
||
## Build Order
|
||
|
||
- [ ] **1. Ingestion worker + SQLite schema** — watch folder, extract EXIF, generate thumbnails, populate DB
|
||
- [ ] **2. Basic API + timeline UI** — paginated grid, chronological browsing (usable on its own)
|
||
- [ ] **3. Detail view + manual tagging/albums**
|
||
- [ ] **4. Dedupe detection + review UI** — pHash comparison, side-by-side review, keep/delete
|
||
- [ ] **5. Search + filters** — date range, camera model, tag
|
||
- [ ] **6. Map view** — Leaflet/MapLibre pins from GPS EXIF
|
||
- [ ] **7. Face grouping** — only if wanted after v1–v5 (biggest scope jump, treat as v2+)
|
||
|
||
## Stack
|
||
|
||
| Layer | Tech |
|
||
|---|---|
|
||
| File watching | `chokidar` (persistent) or cron scan |
|
||
| EXIF extraction | `exifr` or `exiftool` wrapper |
|
||
| Thumbnails | `sharp` (1–2 sizes: grid thumb + preview) |
|
||
| Dedupe hashing | pHash via `sharp` + hashing lib, or `blockhash-core` |
|
||
| Database | SQLite via `better-sqlite3` |
|
||
| API | Express |
|
||
| Frontend | React + Vite |
|
||
| Map | Leaflet or MapLibre |
|
||
| Deployment | Docker container, Nginx Proxy Manager, behind Authelia |
|
||
|
||
## Database Schema
|
||
|
||
```sql
|
||
photos (
|
||
id, filepath, thumbnail_path, date_taken, date_imported,
|
||
camera_model, gps_lat, gps_lon, phash, width, height, filesize
|
||
)
|
||
albums (id, name, created_at)
|
||
photo_albums (photo_id, album_id)
|
||
tags (id, name)
|
||
photo_tags (photo_id, tag_id)
|
||
```
|
||
|
||
## API Endpoints (planned)
|
||
|
||
```
|
||
GET /photos paginated timeline (filter by date, tag, album)
|
||
GET /photos/:id full detail + EXIF
|
||
GET /photos/duplicates grouped by pHash similarity
|
||
GET /photos/map GPS-tagged photos for map view
|
||
POST /albums create album
|
||
POST /photos/:id/tags tag a photo
|
||
```
|
||
|
||
## Deployment Notes
|
||
|
||
- Mount photo folder(s) **read-only** (worker doesn't need write access to originals)
|
||
- Writable volume for thumbnails + DB
|
||
- Route: `photos.necloud.us` via Nginx Proxy Manager
|
||
- Same homelab pattern as other services
|