# ROADMAP Goal: Build a self-hosted security camera footage viewer — Go backend, modern dark UI, Docker Compose deployment, responsive for desktop and mobile. --- ## Constraints - Target hardware: Intel Atom (low CPU budget). Minimise transcoding at all costs. - Video files are raw H.265 bitstreams (`.265`). Browsers cannot play them natively. - Remuxing (copy stream into MP4 container) is acceptable — no re-encode. - No Node.js build step. Tailwind CSS loaded from CDN. --- ## Footage directory layout (read-only mount) ``` / YYYYMMDD/ images/ A.jpg record/ A__.265 ``` - `FOOTAGE_ROOT` is configured as an env var / volume in `docker-compose.yml`. - The app must not write into the footage tree (thumbnails cached in memory). --- ## Priority 1 — Project scaffold Objective: Establish a working Go module, Dockerfile, and docker-compose skeleton so every subsequent task has a solid base to build on. - Go module `github.com/domagojzecevic/cammonitor` (or local path). - `cmd/server/main.go` entry point, `chi` router wired up, static health endpoint. - Multi-stage `Dockerfile` (builder → `gcr.io/distroless/static` or `debian:slim` for ffmpeg). - `docker-compose.yml` with: - `app` service (Go binary) - `FOOTAGE_ROOT` volume bind-mount configurable via `.env` - `DB_PATH` for SQLite file - Port mapping (default `8080`) - `README.md` with quick-start instructions. --- ## Priority 2 — Auth (multi-user, SQLite) Objective: Secure the app behind a login wall with session cookies and an admin user-management page. - SQLite schema: `users` table (id, username, bcrypt_password_hash, is_admin, created_at). - SQLite schema: `sessions` table (token, user_id, expires_at). - First-run bootstrap: if no users exist, create an admin user from env vars (`ADMIN_USER`, `ADMIN_PASS`). - Login page (`/login`): username + password form, sets `session` cookie on success. - Logout endpoint (`/logout`). - Auth middleware protecting all non-login routes. - Admin user-management page (`/admin/users`): list, add, delete users (admin only). - Session expiry configurable via env var (`SESSION_TTL`, default 24 h). --- ## Priority 3 — Footage scanner Objective: Efficiently list available days, images, and videos from the footage tree. - On startup and periodically (configurable interval, default 5 min), scan `FOOTAGE_ROOT`. - Build an in-memory index: `map[date]DayEntry` where `DayEntry` holds sorted slices of image paths and video paths. - Expose a simple internal API used by HTTP handlers to query the index. - Parse filenames to extract timestamps for display and sorting. - Handle missing `images/` or `record/` sub-directories gracefully. --- ## Priority 4 — Image browser Objective: Browse JPEG images for a selected day with thumbnail strip and arrow navigation. - Route `/day/{date}/images` — lists all images for the day. - Thumbnail endpoint `/thumb/image/{relpath}`: resize JPEG to 160×90 px in Go (`golang.org/x/image` or stdlib `image` + `nfnt/resize` or manual scaling), cache result in memory (bounded LRU, max 500 entries). - Full-image endpoint `/raw/image/{relpath}`: serve JPEG directly from disk (no processing). - UI: - Fixed top strip showing all image thumbnails for the day (horizontally scrollable). - Main area shows the currently selected image at full width. - Left / right arrow buttons (keyboard and on-screen) to step through images. - Active thumbnail highlighted in the strip. - Deep-linkable URL per image (`?idx=N`). --- ## Priority 5 — Video browser Objective: Browse and play H.265 videos for a selected day with thumbnail strip, arrow navigation, and on-demand remux streaming. - Route `/day/{date}/videos` — lists all videos for the day. - Thumbnail endpoint `/thumb/video/{relpath}`: extract first frame via `ffmpeg -i -vframes 1 -f image2 pipe:1`, scale to 160×90, cache in memory (bounded LRU, max 200 entries). - Stream endpoint `/stream/video/{relpath}`: `ffmpeg -i {input} -c:v copy -movflags frag_keyframe+empty_moov -f mp4 pipe:1` piped directly to the HTTP response (`Content-Type: video/mp4`). No disk writes. One ffmpeg process per active stream (kill on client disconnect). - UI: - Fixed top strip showing all video thumbnails for the day (horizontally scrollable, shows clip duration parsed from filename). - Main area: HTML5 `