Highlight reel video, kiosk ambient mode for magic picture frame, and on-demand time-lapse generator with scheduling. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12 KiB
Daily Delight — Design Spec
Overview
Three features that make Vigilar enjoyable to look at every day: an auto-generated daily highlight reel video, a kiosk ambient mode for a magic picture frame, and an on-demand time-lapse generator with scheduling.
Q1: Daily Highlight Reel
What It Does
Each morning, auto-generates a 60-90 second MP4 montage of yesterday's most interesting events — pet zoomies, wildlife visits, deliveries, notable detections. Generated once, viewable on the dashboard, kiosk, and phone via push notification.
Event Scoring
Events are ranked by a scoring heuristic to select the top N clips (default 10, configurable):
| Event Type | Score |
|---|---|
| PET_ESCAPE | 10 |
| WILDLIFE_PREDATOR | 9 |
| PACKAGE_DELIVERED | 8 |
| UNKNOWN_ANIMAL | 7 |
| PET_DETECTED (high motion) | 6 |
| WILDLIFE_NUISANCE | 5 |
| PERSON_DETECTED | 4 |
| UNKNOWN_VEHICLE_DETECTED | 4 |
| WILDLIFE_PASSIVE | 3 |
| VEHICLE_DETECTED | 2 |
| PET_DETECTED (normal) | 1 |
"High motion" for pet detections means the motion confidence was above the zoomie_threshold (0.8) from pet activity config.
FFmpeg Pipeline
- For each selected event, extract a 5-second clip centered on the event timestamp from the associated recording segment (decrypt if needed).
- Overlay a date/time watermark and event label (e.g., "Angel — Kitchen — 2:34 PM") on each clip using FFmpeg's
drawtextfilter. - Concatenate clips with 0.5-second crossfade transitions using the
xfadefilter. - Output as H.264 MP4, CRF 23, resolution matching the source (or downscaled to 1280x720 if sources vary).
New File: vigilar/highlights/reel.py
generate_daily_reel(engine, recordings_dir, date, config) -> str:
- Queries events for the given date, scores and ranks them
- Extracts clips from recordings via FFmpeg subprocess
- Concatenates with transitions and watermarks
- Saves to
recordings_dir/highlights/YYYY-MM-DD.mp4 - Inserts a recording row with
trigger=HIGHLIGHT - Returns the file path
ReelConfig (from config):
[highlights]
enabled = true
generate_time = "06:00" # when to generate yesterday's reel
max_clips = 10 # max events to include
clip_duration_s = 5 # seconds per clip
cameras = [] # empty = all cameras
event_types = [] # empty = all types
Config Model
class HighlightsConfig(BaseModel):
enabled: bool = True
generate_time: str = "06:00"
max_clips: int = 10
clip_duration_s: int = 5
cameras: list[str] = Field(default_factory=list)
event_types: list[str] = Field(default_factory=list)
Add to VigilarConfig as highlights: HighlightsConfig.
Scheduling
The daily reel generation runs as a scheduled job in the health monitor process (which already handles the daily digest timing). At generate_time, it calls generate_daily_reel() for yesterday's date.
Delivery
- Dashboard: "Yesterday's Highlights" card on the main dashboard. Shows thumbnail + duration + "Play" button. Links to
/recordings/<id>/download. - Push notification: After generation, sends a Web Push notification: "Your daily highlight reel is ready (8 clips, 52s)". Link opens the recording.
- Kiosk: Auto-plays at a configurable time (see Q4 kiosk spec). Then returns to ambient mode.
- Dashboard config: Settings page section for highlights — toggle cameras, event types, generation time, max clips.
Recording Integration
Highlight reels are stored as regular recordings with trigger=HIGHLIGHT. They appear in the recordings list with a distinct badge. The RecordingTrigger enum already has values for different trigger types — add HIGHLIGHT and TIMELAPSE.
Q4: Kiosk Ambient Mode
What It Does
Extends the existing kiosk blueprint with an ambient "all-quiet" state optimized for a magic picture frame (RPi + thin LCD). When nothing is happening, it shows a beautiful home awareness dashboard. When alerts fire, the relevant camera takes over the screen.
Ambient Screen Layout
Fullscreen, no nav, no scrollbar. Landscape orientation. Dark theme with muted colors to avoid light pollution.
Top bar (10% height):
- Current time (large,
hh:mm, updates every minute) - Current date (weekday + month + day)
- Weather icon + temperature (from Open-Meteo, cached hourly, "—" if offline)
Center area (70% height):
- Rotating camera thumbnail: cycles through cameras every 10 seconds
- Shows the camera name and a subtle "live" indicator
- Thumbnail source: latest HLS segment frame or snapshot endpoint
- Smooth CSS crossfade transition between cameras
Bottom bar (20% height):
- Household presence: who's home (icons + names from presence monitor)
- Pet locations: pet name + last-seen camera + time ago (e.g., "Angel — Kitchen — 4m ago")
- If highlight reel is available: small "Play highlights" button
Alert Takeover
When an alert event arrives via SSE (/events/stream):
- Fade ambient layout to 0 opacity (300ms)
- Show the relevant camera's live HLS feed fullscreen
- Overlay event info: type badge, camera name, timestamp
- After timeout (configurable, default 30s for normal, 60s for predator alerts), if no new events, fade back to ambient
- If a new event fires during takeover, reset the timer and switch cameras if different
Highlight Reel Playback
If a daily highlight reel exists for yesterday, auto-play it once at a configurable time. After playback, return to ambient. Configurable in the kiosk settings.
Power / Performance (RPi Considerations)
- No heavy canvas rendering — all layout via CSS Grid/Flexbox
- Camera thumbnails are static
<img>elements swapped via JS, not video streams - CSS
will-change: opacityon transition elements - Minimal DOM manipulation — update text content only when values change
- Screen dimming: configurable dark hours (e.g., 23:00-06:00) via a CSS overlay with
opacity: 0.1that dims the screen. Optionally pair with HDMI CECtvservice -ofor full screen-off (documented as optional). - Auto-refresh: full page reload every 6 hours to prevent memory leaks in long-running browser tabs
Config
[kiosk]
ambient_enabled = true
camera_rotation_s = 10 # seconds between camera switches
alert_timeout_s = 30 # seconds before returning to ambient
predator_alert_timeout_s = 60 # predator alerts stay longer
dim_start = "23:00" # screen dim start
dim_end = "06:00" # screen dim end
highlight_play_time = "07:00" # when to auto-play highlight reel
Files
New file: vigilar/web/templates/kiosk/ambient.html — self-contained fullscreen template
Modified: vigilar/web/blueprints/kiosk.py — add GET /kiosk/ambient route
Modified: vigilar/config.py — add KioskConfig model
Weather Dependency
Uses vigilar/detection/weather.py (WeatherFetcher) from Group C's wildlife spec. If Group C isn't built yet when this is implemented, the weather section simply shows "—" (graceful degradation, no hard dependency).
Q6: Time-lapse Generator
What It Does
Generate time-lapse videos on demand for any camera and date range, with optional scheduled presets for automatic daily generation.
On-Demand Generation
Route: POST /cameras/<id>/timelapse
Request body:
{
"date": "2026-04-02",
"start_hour": 6,
"end_hour": 20,
"fps": 30
}
Process:
- Find all recording segments for the camera on the given date within the hour range
- Extract one frame per minute from each segment (decrypt if needed) via FFmpeg
- Stitch frames into a time-lapse MP4 at the requested FPS (default 30 — one minute of real time per frame at 30fps gives a ~14-hour day in ~28 seconds)
- Save as a recording with
trigger=TIMELAPSE - Return
{id, status: "generating"}immediately — generation runs in a background thread
Status polling: GET /cameras/<id>/timelapse/status returns progress of any in-flight generation:
{"status": "generating", "progress": 0.65, "eta_seconds": 12}
Or {"status": "idle"} when nothing is running.
Scheduled Presets
Route: POST /cameras/<id>/timelapse/schedule
Request body:
{
"name": "Daily backyard",
"start_hour": 6,
"end_hour": 20,
"time": "20:00"
}
Creates a daily schedule: every day at the specified time, auto-generate a timelapse for that camera covering the hour range.
Management routes:
GET /cameras/<id>/timelapse/schedules— list schedules for a cameraDELETE /cameras/<id>/timelapse/schedule/<schedule_id>— remove a schedule
Database
New table: timelapse_schedules
| Column | Type | Notes |
|---|---|---|
| id | Integer PK | Autoincrement |
| camera_id | String NOT NULL | Which camera |
| name | String NOT NULL | User-given label |
| start_hour | Integer NOT NULL | Start hour (0-23) |
| end_hour | Integer NOT NULL | End hour (0-23) |
| generate_time | String NOT NULL | "HH:MM" daily generation time |
| enabled | Integer NOT NULL | 1 = active |
| created_at | Float NOT NULL | Timestamp |
New File: vigilar/highlights/timelapse.py
generate_timelapse(camera_id, date, start_hour, end_hour, fps, recordings_dir, engine) -> str:
- Queries recordings for the camera + date + hour range
- Extracts frames via FFmpeg (1 per minute)
- Stitches into MP4
- Inserts recording row with
trigger=TIMELAPSE - Returns file path
check_schedules(engine, recordings_dir):
- Called periodically by the health monitor (every 60 seconds)
- Finds schedules where
enabled=1and current time matchesgenerate_time(within 60-second window) - Triggers
generate_timelapse()for today's date - Tracks "last generated" to avoid double-generation
UI
Camera page: "Time-lapse" tab alongside the existing stream and heatmap views.
- Date picker + start/end hour sliders + "Generate" button
- Progress bar when generating
- List of generated time-lapses (from recordings with
trigger=TIMELAPSE) - "Schedule daily" toggle that creates/removes a preset
New Event Types
Add to RecordingTrigger enum:
HIGHLIGHT— daily highlight reelTIMELAPSE— time-lapse video
(Note: HIGHLIGHT and TIMELAPSE are recording triggers, not event types — they go in RecordingTrigger, not EventType.)
Dependencies
New Packages
None — FFmpeg is already used by the recorder. All generation is via subprocess calls.
New Files
| File | Purpose |
|---|---|
vigilar/highlights/reel.py |
Daily highlight reel generator |
vigilar/highlights/timelapse.py |
Time-lapse generator + schedule checker |
vigilar/web/templates/kiosk/ambient.html |
Ambient mode fullscreen template |
Modified Files
| File | Changes |
|---|---|
vigilar/config.py |
Add HighlightsConfig, KioskConfig models |
vigilar/constants.py |
Add HIGHLIGHT, TIMELAPSE to RecordingTrigger |
vigilar/storage/schema.py |
Add timelapse_schedules table |
vigilar/storage/queries.py |
Add timelapse schedule CRUD functions |
vigilar/web/blueprints/kiosk.py |
Add /kiosk/ambient route |
vigilar/web/blueprints/cameras.py |
Add timelapse generation + schedule routes |
vigilar/health/monitor.py |
Add highlight reel + timelapse schedule triggers |
vigilar/web/app.py |
No new blueprints (uses existing kiosk + cameras) |
Out of Scope
- Highlight reel with background music (YAGNI)
- Time-lapse with motion interpolation / smooth zoom (just frame concat)
- Kiosk touch controls (RPi magic frame is view-only)
- Multiple highlight reel presets (one per day is sufficient)
- Streaming time-lapse generation (generate full file, then serve)