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

7.8 KiB
Raw Blame History

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

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 enginerequestAnimationFrame 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 loadscheduleAutoScan() 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 buttonsbtnPrevMotion / 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