Appearance
Event Hooks
Event hooks allow you to execute custom scripts or commands when specific events occur during recording. This enables integration with external systems, automation, and custom processing workflows.
Overview
ForgeVis supports three types of event hooks:
runOnSegmentCreate- Triggered when a new recording segment startsrunOnSegmentComplete- Triggered when a recording segment finishesrunPeriodically- Fires at a fixed interval per camera (independent of segments). Designed for live-preview thumbnails. A global concurrency limit caps simultaneous executions.
Configuration
Add hooks to your config.yaml in the record section:
yaml
record:
segmentDuration: 900
runOnSegmentCreate: "/path/to/on-create.sh"
runOnSegmentComplete: "/path/to/on-complete.sh"
runPeriodically:
intervalSec: 60 # how often to fire (per camera)
command: "/path/to/periodic.sh"
maxConcurrent: 50 # global cap across all camerasEnvironment Variables
Your hook scripts receive the following environment variables:
Common Variables (both hooks)
SF_PATH- Camera identifier (e.g.,camera_001)SF_SEGMENT_PATH- Full path to the segment file
Additional Variables (complete hook only)
SF_SEGMENT_DURATION- Segment duration in seconds
Periodic Hook Variables
SF_PATH- Camera identifierSF_CAMERA_DIR- Per-camera recordings root. The script is expected to locate the latest segment itself (filenames are zero-padded timestamps, so a lexical sort matches chronological order).
Example Scripts
Basic Notification Hook
bash
#!/bin/bash
# on-complete.sh - Send notification when segment completes
echo "Segment complete: $SF_SEGMENT_PATH"
echo "Camera: $SF_PATH"
echo "Duration: $SF_SEGMENT_DURATION seconds"
# Send notification (example)
# curl -X POST "https://hooks.slack.com/..." \
# -d "{\"text\": \"Recording complete: $SF_SEGMENT_PATH\"}"Generate Thumbnail
bash
#!/bin/bash
# generate-thumbnail.sh - Create thumbnail from segment
if [ -n "$SF_SEGMENT_DURATION" ]; then
THUMB_PATH="${SF_SEGMENT_PATH%.mp4}.jpg"
ffmpeg -i "$SF_SEGMENT_PATH" \
-ss 00:00:01 \
-vframes 1 \
-q:v 2 \
"$THUMB_PATH" \
-loglevel quiet
echo "Thumbnail created: $THUMB_PATH"
fiLive Preview via Periodic Hook
Generate a near-live JPEG preview for each camera every 60 seconds. ffmpeg reads the last few seconds of the most recent segment, so the snapshot is at most segmentDuration + intervalSec seconds old.
bash
#!/bin/bash
# preview.sh — extract a preview snapshot from the latest segment
set -eu
[ -z "${SF_CAMERA_DIR:-}" ] && exit 1
[ ! -d "$SF_CAMERA_DIR" ] && exit 0
# Filenames carry zero-padded timestamps; lexical sort = chronological.
LATEST=$(find "$SF_CAMERA_DIR" -name '*.mp4' -type f 2>/dev/null | sort | tail -1)
[ -z "$LATEST" ] && exit 0
ffmpeg -hide_banner -loglevel error -y \
-sseof -3 -i "$LATEST" \
-update 1 -frames:v 1 -q:v 5 \
"$SF_CAMERA_DIR/preview.jpg"yaml
record:
runPeriodically:
intervalSec: 60
command: "/etc/forgevis/hooks/preview.sh"
maxConcurrent: 50The preview is then accessible through the existing archive file route: GET /api/archive/file/<camera_id>/preview.jpg.
The global maxConcurrent cap means that even with 1000 cameras only maxConcurrent ffmpeg processes run at any one time; the rest queue in FIFO order on the camera-side. Effective preview lag scales as (total_cameras × avg_ffmpeg_time) / maxConcurrent.
Webhook Notification
bash
#!/bin/bash
# webhook-notify.sh - Send notifications via HTTP webhook
WEBHOOK_URL="https://example.com/api/webhook"
if [ -n "$SF_SEGMENT_DURATION" ]; then
EVENT_TYPE="segment_complete"
else
EVENT_TYPE="segment_create"
fi
curl -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{
\"event\": \"$EVENT_TYPE\",
\"camera\": \"$SF_PATH\",
\"path\": \"$SF_SEGMENT_PATH\",
\"duration\": \"$SF_SEGMENT_DURATION\"
}" \
-s > /dev/nullCleanup Old Recordings
bash
#!/bin/bash
# cleanup-old.sh - Delete recordings older than 7 days
if [ -n "$SF_SEGMENT_DURATION" ]; then
RECORDINGS_DIR="$(dirname "$SF_SEGMENT_PATH")"
# Delete files older than 7 days
find "$RECORDINGS_DIR" -name "*.mp4" -mtime +7 -delete
echo "Cleaned up old recordings in $RECORDINGS_DIR"
fiIntegrations
Video Processing Pipeline
Push completed segments into an external processing queue or call a worker service directly:
bash
#!/bin/bash
# process-video.sh
# Append the segment path to a worker queue file
echo "$SF_SEGMENT_PATH" >> /var/queue/video-processing.txt
# Or call a processing service's API directly
curl -X POST http://video-processor:8080/process \
-H "Content-Type: application/json" \
-d '{
"camera": "'"$SF_PATH"'",
"file": "'"$SF_SEGMENT_PATH"'",
"duration": '"$SF_SEGMENT_DURATION"'
}'Best Practices
1. Make Scripts Executable
bash
chmod +x /path/to/your-hook.sh2. Handle Errors Gracefully
bash
#!/bin/bash
set -e # Exit on error
# Your hook logic here
exit 0 # Always exit successfully3. Run Long Tasks in Background
bash
#!/bin/bash
# Don't block recording process
{
# Long-running task
process_video "$SF_SEGMENT_PATH"
} &
exit 04. Log Hook Output
bash
#!/bin/bash
LOG_FILE="/var/log/forgevis-hooks.log"
{
echo "Hook triggered: $(date)"
echo "Camera: $SF_PATH"
# Your logic here
} >> "$LOG_FILE" 2>&15. Test Your Hooks
Create a test script to verify hook functionality:
bash
# Set test environment variables
export SF_PATH="camera_001"
export SF_SEGMENT_PATH="./test.mp4"
export SF_SEGMENT_DURATION="60"
# Run your hook
./your-hook.shUse Cases
Common Use Cases
- Monitoring & Alerting: Send notifications when recording starts/stops
- Post-Processing: Generate thumbnails, extract metadata, transcode
- Integration: Upload to cloud storage, update external systems via API
- Cleanup: Delete old recordings automatically, archive to cold storage
Troubleshooting
Hook Not Executing
- Check script permissions:
ls -l your-hook.sh - Verify script path in config.yaml
- Check ForgeVis logs for errors
- Test script manually with environment variables
Hook Failing Silently
Add logging to your script:
bash
#!/bin/bash
exec 2>> /tmp/hook-errors.log
set -x # Print all commands
# Your hook logicPerformance Issues
If hooks slow down recording:
- Run long tasks in background (
&) - Use separate worker processes
- Queue tasks for batch processing
- Monitor hook execution time
Security Considerations
- Never log sensitive credentials
- Validate all input paths
- Use absolute paths in scripts
- Limit file system access
- Run hooks with minimal privileges
- Sanitize environment variables before using in commands
Advanced Examples
Conditional Execution
bash
#!/bin/bash
# Only process specific cameras
if [ "$SF_PATH" == "camera_front" ]; then
# Special processing for front camera
process_important_camera "$SF_SEGMENT_PATH"
fiRate Limiting
bash
#!/bin/bash
# Prevent hook spam
LOCK_FILE="/tmp/hook-${SF_PATH}.lock"
if [ -f "$LOCK_FILE" ]; then
exit 0 # Another hook is running
fi
touch "$LOCK_FILE"
trap "rm -f $LOCK_FILE" EXIT
# Your hook logic hereWebhook with Retry
bash
#!/bin/bash
# Retry webhook calls on failure
MAX_RETRIES=3
RETRY_DELAY=5
for i in $(seq 1 $MAX_RETRIES); do
if curl -X POST "https://api.example.com/webhook" \
-d "{\"path\": \"$SF_SEGMENT_PATH\"}" \
--max-time 10; then
exit 0
fi
sleep $RETRY_DELAY
done
echo "Webhook failed after $MAX_RETRIES attempts" >&2
exit 1