Appearance
Event Hooks
ForgeVis поддерживает выполнение произвольных команд при определённых событиях записи сегментов.
Доступные хуки
runOnSegmentCreate
Выполняется когда начинается запись нового сегмента (создаётся новый файл).
Переменные окружения:
SF_PATH- идентификатор камерыSF_SEGMENT_PATH- полный путь к файлу сегмента
Пример использования:
yaml
record:
runOnSegmentCreate: "./hooks/on-create.sh"runOnSegmentComplete
Выполняется когда сегмент полностью записан и закрыт.
Переменные окружения:
SF_PATH- идентификатор камерыSF_SEGMENT_PATH- полный путь к файлу сегментаSF_SEGMENT_DURATION- длительность сегмента в секундах
Пример использования:
yaml
record:
runOnSegmentComplete: "./hooks/on-complete.sh"runPeriodically
Запускается с фиксированным интервалом для каждой камеры — независимо от границ сегментов. Основной use case — генерация live-превью.
Параметры конфигурации:
intervalSec— частота запуска для каждой камеры (по умолчанию 60)command— путь к скриптуmaxConcurrent— глобальный лимит одновременно работающих хуков по всем камерам (по умолчанию 50). Превышающие запуски ожидают в FIFO-очереди.
Переменные окружения:
SF_PATH— идентификатор камерыSF_CAMERA_DIR— корневой каталог записей этой камеры. Скрипт сам находит последний сегмент (имена файлов содержат zero-padded timestamp, поэтому лексическая сортировка совпадает с хронологической).
Пример использования:
yaml
record:
runPeriodically:
intervalSec: 60
command: "./hooks/preview.sh"
maxConcurrent: 50При первом запуске каждая камера получает рандомный stagger в [0, intervalSec) чтобы 1000 камер не били одновременно. Эффективный лаг превью растёт как (всего_камер × среднее_время_ffmpeg) / maxConcurrent.
Примеры скриптов
Уведомление при создании сегмента
bash
#!/bin/bash
# on-create.sh
echo "New segment started: $SF_SEGMENT_PATH for camera $SF_PATH"
# Отправить уведомление через webhook
curl -X POST "https://example.com/webhook" \
-H "Content-Type: application/json" \
-d '{"event": "segment_created", "camera": "'"$SF_PATH"'", "path": "'"$SF_SEGMENT_PATH"'"}'Обработка завершённого сегмента
bash
#!/bin/bash
# on-complete.sh
echo "Segment completed: $SF_SEGMENT_PATH (${SF_SEGMENT_DURATION}s)"
# Создать резервную копию
rsync -av "$SF_SEGMENT_PATH" /mnt/backup/recordings/
# Отправить уведомление
curl -X POST "https://example.com/webhook" \
-H "Content-Type: application/json" \
-d '{"event": "segment_completed", "camera": "'"$SF_PATH"'", "path": "'"$SF_SEGMENT_PATH"'", "duration": '"$SF_SEGMENT_DURATION"'}'Проверка размера файла
bash
#!/bin/bash
# check-size.sh
FILE_SIZE=$(stat -f%z "$SF_SEGMENT_PATH" 2>/dev/null || stat -c%s "$SF_SEGMENT_PATH" 2>/dev/null)
FILE_SIZE_MB=$((FILE_SIZE / 1024 / 1024))
echo "Segment size: ${FILE_SIZE_MB}MB"
if [ $FILE_SIZE_MB -lt 1 ]; then
echo "WARNING: Segment is too small!"
# Отправить алерт
fiГенерация thumbnail
bash
#!/bin/bash
# generate-thumbnail.sh
THUMBNAIL="${SF_SEGMENT_PATH%.mp4}.jpg"
# Создать превью из середины видео
ffmpeg -i "$SF_SEGMENT_PATH" -ss 00:00:30 -vframes 1 -q:v 2 "$THUMBNAIL" 2>/dev/null
echo "Thumbnail created: $THUMBNAIL"Условное выполнение
bash
#!/bin/bash
# conditional-upload.sh
# Загружать только длинные сегменты
if [ "$SF_SEGMENT_DURATION" -gt 600 ]; then
echo "Segment is long enough, uploading..."
# Выполнить загрузку
fiRate limiting
bash
#!/bin/bash
# rate-limited-notify.sh
LOCK_FILE="/tmp/forgevis-notify.lock"
LOCK_TIMEOUT=60
# Отправлять уведомления не чаще раза в минуту
if [ ! -f "$LOCK_FILE" ] || [ $(($(date +%s) - $(stat -f%m "$LOCK_FILE" 2>/dev/null || stat -c%Y "$LOCK_FILE"))) -gt $LOCK_TIMEOUT ]; then
touch "$LOCK_FILE"
# Отправить уведомление
curl -X POST "https://example.com/webhook" -d '{"event": "recording"}'
fiLive-превью через periodic-hook
Генерирует JPEG-превью для каждой камеры раз в 60 секунд. ffmpeg читает последние секунды самого свежего сегмента, поэтому превью устаревает максимум на segmentDuration + intervalSec.
bash
#!/bin/bash
# preview.sh — извлечение превью-кадра из последнего сегмента
set -eu
[ -z "${SF_CAMERA_DIR:-}" ] && exit 1
[ ! -d "$SF_CAMERA_DIR" ] && exit 0
# Имена файлов содержат zero-padded timestamp, лексическая сортировка = хронологическая.
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: 50Превью доступно через существующий маршрут archive: GET /api/archive/file/<camera_id>/preview.jpg.
Webhook с повторными попытками
Перезапрашивает webhook при сетевой ошибке, ограничивая число попыток:
bash
#!/bin/bash
# webhook-with-retry.sh
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Интеграции
Запуск обработки видео
bash
#!/bin/bash
# process-video.sh
# Добавить задачу в очередь обработки
echo "$SF_SEGMENT_PATH" >> /var/queue/video-processing.txt
# Или вызвать API сервиса обработки
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"'
}'Применение
- Мониторинг и алерты - уведомления о событиях записи, аномальная длительность сегментов, сбои записи
- Пост-обработка - транскодирование, генерация превью, извлечение метаданных
- Интеграция с внешними системами - webhooks, очереди задач, системы хранения
- Очистка и архивирование - удаление старых файлов, загрузка в облако, сжатие
Лучшие практики
1. Сделать скрипт исполняемым
bash
chmod +x /path/to/your-hook.sh2. Обрабатывать ошибки
bash
#!/bin/bash
set -e # Выйти при первой ошибке
# Логика хука
exit 0 # Возвращать успех, чтобы ForgeVis не считал хук упавшим3. Запускать долгие задачи в фоне
Не блокируйте процесс записи — хук должен завершаться быстро:
bash
#!/bin/bash
{
process_video "$SF_SEGMENT_PATH"
} &
exit 04. Логировать вывод хука
bash
#!/bin/bash
LOG_FILE="/var/log/forgevis-hooks.log"
{
echo "Hook triggered: $(date)"
echo "Camera: $SF_PATH"
# Логика хука
} >> "$LOG_FILE" 2>&15. Тестировать хуки до подключения
Запустите хук вручную с подставленными переменными окружения:
bash
export SF_PATH="camera_001"
export SF_SEGMENT_PATH="./test.mp4"
export SF_SEGMENT_DURATION="60"
./your-hook.shПомимо ручного запуска, удобен универсальный тестовый скрипт:
bash
#!/bin/bash
# test-hook.sh — вывести всё, что ForgeVis передал хуку
echo "=========================="
echo "ForgeVis Hook Test"
echo "=========================="
echo "Time: $(date)"
echo "Camera: $SF_PATH"
echo "Segment: $SF_SEGMENT_PATH"
[ -n "$SF_SEGMENT_DURATION" ] && echo "Duration: $SF_SEGMENT_DURATION seconds"
echo "=========================="Устранение проблем
Хук не запускается
- Проверьте права на исполнение:
ls -l your-hook.sh - Убедитесь, что путь в
config.yamlправильный (предпочтительно абсолютный) - Поищите ошибки в логах ForgeVis
- Запустите скрипт вручную с тестовыми переменными окружения
Логи выполнения хуков можно найти в выводе ForgeVis:
INFO Hook executed successfully: ./test-hook.sh
ERROR Hook failed with status exit status: 1: ./failed-hook.shХук молча падает
Добавьте подробное логирование в сам скрипт:
bash
#!/bin/bash
exec 2>> /tmp/hook-errors.log
set -x # Печатать все команды
# Логика хукаХук замедляет запись
Если запись начинает «отставать»:
- Запускайте долгие задачи в фоне (
&илиnohup ... &) - Выносите обработку в отдельные процессы/воркеры
- Накапливайте задачи в очередь и обрабатывайте пачкой
- Замеряйте время выполнения хука
Безопасность
Хуки выполняются с правами процесса ForgeVis — относитесь к ним как к доверенному коду. Рекомендации:
- Никогда не логируйте чувствительные данные (пароли, токены, ключи).
- Валидируйте все входные пути перед использованием в файловых операциях.
- Используйте абсолютные пути в скриптах — никаких
$PATH-зависимостей. - Минимизируйте права процесса ForgeVis: запись только в нужные каталоги.
- Санитизируйте переменные окружения перед подстановкой в шелл-команды. Переменные
SF_PATHиSF_SEGMENT_PATHприходят из конфигурации и от файловой системы — в шелл-командах используйте двойные кавычки и избегайтеeval/конкатенации.
Пример небезопасного и безопасного использования:
bash
# ❌ Небезопасно: $SF_PATH может содержать пробелы или специальные символы
cp /var/recordings/$SF_PATH/* /backup/
# ✅ Безопасно: кавычки + проверка
if [[ "$SF_PATH" =~ ^[a-zA-Z0-9_-]+$ ]]; then
cp -r "/var/recordings/$SF_PATH" "/backup/"
fiЗапуск в фоне
Тяжёлые операции (транскодирование, загрузка в облако) стоит запускать в фоне, чтобы хук возвращал управление быстро и не накапливал отставание:
bash
#!/bin/bash
# Запустить транскодирование в фоне и отвязать от основного процесса
nohup ffmpeg -i "$SF_SEGMENT_PATH" \
-c:v libx264 -preset fast \
"/transcoded/${SF_PATH}/$(basename "$SF_SEGMENT_PATH")" \
> /dev/null 2>&1 &
disownСм. также
- Конфигурация — все параметры
record.runOnSegment* - Мониторинг — метрики и логи