Files
WebAVP/CLAUDE.md
T
peji 691f643edc Initial commit — Alta Video Player (WebAVP)
Web-based surveillance video player for Alta/Ava Security camera exports
with multi-camera sync, timeline, digital zoom, motion analytics, and
cryptographic integrity verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 12:50:24 +00:00

108 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**Alta Video Player (WebAVP)** — a web-based surveillance video player for Alta/Ava Security camera exports. Users drag-drop video files or ZIP archives (including AES-256 encrypted ones) and get a multi-camera synchronized playback experience with timeline, digital zoom, cryptographic integrity verification, and automatic motion analytics.
## Running the App
```bash
python3 app.py
# Serves on http://0.0.0.0:5152
```
No build step. No dependencies needed at runtime — `app.py` uses only Python stdlib (`http.server`). The `requirements.txt` (flask, requests, gunicorn) is vestigial; the server was rewritten to pure stdlib.
## Architecture
### Backend (`app.py`)
Minimal HTTP server with three routes:
- `GET /` — serves `templates/index.html`
- `GET /static/*` — serves static files with path traversal protection
- `GET /api/verify-cert?serial=...&certificateHash=...` — proxies certificate verification to `https://aware.avasecurity.com/api/v1/public/verifyServerCertificate`
### Frontend (`templates/index.html`)
Single self-contained HTML file (~5000 lines) with inline CSS and JavaScript in an IIFE. This is the entire application — there is no framework, no build system, no separate JS modules.
**State model:** Global `channels` Map keyed by channel name. Each channel holds segments (video blobs + time ranges), metadata, DOM references, color, and zoom state. Global timeline state (`globalStart`, `globalEnd`, `currentTime`) synchronizes all cameras. Motion analytics state lives in the `motionState` object.
**Key subsystems:**
- **File ingestion** — drag-drop files/folders, ZIP extraction (JSZip for plain, custom AES-256-CTR for encrypted), metadata pairing by base filename, concurrent import guard (`isImporting` flag)
- **Multi-camera grid** — responsive CSS grid (19 cameras), drag-to-reorder, click-to-expand
- **Playback engine** — `requestAnimationFrame` tick loop, per-channel segment visibility management, variable speed (0.25x8x), frame stepping
- **Timeline** — interactive scrub bar with zoom (mouse wheel), minimap, per-channel segment indicators with color-coded dots, motion heatmap row
- **Digital zoom** — per-camera scroll-to-zoom (up to 10x) with click-drag panning
- **Magnifier tool** — draw rectangle to zoom into region
- **Slideshow mode** — animated grid showing only currently active feeds with transitions
- **Motion analytics** — automatic background motion detection on load (see below)
- **Integrity verification** — offline X.509 certificate parsing, RSASSA-PKCS1-v1_5 and ECDSA signature verification via Web Crypto API, optional cloud verification through `/api/verify-cert`
- **Session persistence** — IndexedDB caching of video blobs and metadata for page refresh survival
**External dependency:** `/static/jszip.min.js` (vendored, for unencrypted ZIP parsing).
## Motion Analytics Subsystem
Zero-dependency canvas-based motion detection that runs automatically when videos are loaded.
### How it works
1. **Auto-scan on load**`scheduleAutoScan()` is called from `flushPending()` and `loadSessionData()` after videos finish loading. Debounced 800ms to batch all segments.
2. **Dedicated scan videos** — scan creates temporary offscreen `<video>` elements per segment (with `preload='auto'` for fast seeking) that don't interfere with the playback engine. Each is destroyed after its segment is processed.
3. **Pixel-diff at 160x120** — frames are drawn to a small offscreen canvas. Each pixel's RGB delta is compared against a threshold. Changed pixels are counted and mapped to a 10x8 hotspot grid.
4. **Sensitivity slider (1100)** — maps to two internal thresholds via `sensitivityToThresholds()`: `pixelThreshold` (how different a pixel must be) and `changeThreshold` (what % of pixels must change). Presets: Indoor (30), Default (40), Parking (45), Outdoor (55).
5. **Motion clusters** — consecutive motion detections within `clusterGap` (5s) are merged into clusters with start/end times, peak change %, and hotspot data.
6. **Results visualization** — motion heatmap row on timeline (purple/red intensity), clickable cluster markers, scrollable event cards in the analytics panel with hotspot mini-grids.
### Key state: `motionState`
- `sensitivity` (1100), `scanInterval` (2s default), `clusterGap` (5s)
- `detector.canvas` / `detector.ctx` — 160x120 offscreen canvas for pixel comparison
- `detector.prevFrames` — Map of channelName → previous ImageData
- `motionProfiles` — Map of channelName → array of `{ time, changePercent, hotspots, hasMotion }`
- `motionClusters` — sorted array of `{ startTime, endTime, channelName, peakChange, ... }`
- `isScanning` / `scanAbort` — scan lifecycle
- `isMonitoring` — live monitor mode flag
### Key functions
- `detectMotion(videoEl, channelName)` — core pixel-diff, returns `{ hasMotion, changePercent, hotspots }`
- `scanTimeline({ auto })` — full scan orchestrator, creates offscreen videos, yields to UI every 12 frames
- `scheduleAutoScan()` — debounced auto-trigger after video load
- `setScanUIState('scanning'|'idle')` — manages rail button grey-out and progress UI
- `seekToMotion(direction)` — skip to next/previous motion cluster
- `monitorTick(absTime)` — called from `tick()` during playback for live detection
- `buildMotionClusters()` — post-scan cluster identification
- `renderMotionResults()` — populates panel: sparkline, summary, event cards
### UI elements
- **Analytics panel** — right slide-out (400px), class `.analytics-panel`, toggled via `railAnalyticsBtn`
- **Rail button** — greyed out with pulsing purple border (`.scanning` class) during auto-scan
- **Skip-to-motion buttons** — `btnPrevMotion` / `btnNextMotion` in transport controls
- **Keyboard shortcuts** — Shift+N (next motion), Shift+P (previous motion)
### Two-tier distribution strategy
The browser version provides motion detection only (zero dependencies, fully offline). A future downloadable local app will add:
- Object classification via bundled ONNX/YOLO model (person/vehicle/animal detection)
- BYOA (Bring Your Own Agent) cloud AI integration for scene understanding, license plates, cross-camera tracking
- Optional local LLM support (Ollama) for fully offline AI reasoning
## Memory Management
Video elements must be properly destroyed to avoid browser memory exhaustion:
- **`destroyVideoEl(videoEl)`** — pauses video, removes `src`, calls `.load()` to force browser to release buffered data. Must be called before removing video elements from DOM.
- **`video.preload = 'metadata'`** — all playback videos use metadata-only preloading to avoid buffering entire files into RAM. Scan videos use `'auto'` temporarily and are destroyed after use.
- **`newSession()`** — comprehensive teardown: stops scan/monitor, destroys all video elements, revokes all blob URLs, nulls blob references, releases WebGL contexts, clears all state.
- **`isImporting` guard** — prevents concurrent file imports which could cause race conditions and duplicate segments.
- Slideshow video elements are destroyed on pane transitions and when slideshow is toggled off.
## Key Conventions
- All frontend code lives in `templates/index.html` — CSS at top, then the IIFE script block
- Video elements are created on-demand and hidden (not removed) for performance
- Segment visibility is recalculated every animation frame during playback
- The `batchingSegments` flag defers rendering during bulk file imports
- Keyboard shortcuts are defined inline (Space=play/pause, arrows=seek, S=slideshow, M=magnifier, F=fullscreen, [/]=speed, 0=reset zoom, Shift+N/P=skip motion)
- Motion scan runs automatically on load — the analytics rail button is disabled until scan completes
- Right-side panels (log, analytics) auto-close when the other opens