SIRINVIDEO Документация администратора и интегратора На сайт 2026.08

Введение и аутентификация

Базовый путь /api/v1, вход по паролю, статические токены чтения и записи, форма ответов, коды ошибок и пагинация.

Всё, что делает панель управления, делается через REST API — панель не имеет ни одной привилегии, недоступной вам из curl. Здесь описано то, что относится ко всем запросам: как получить токен, как его передавать, как устроены ответы и как читать ошибки. Отдельные группы операций разобраны на страницах Потоки и Конфигурация и система, готовые сценарии — на странице Готовые сценарии.

Базовый путь и порты#

API живёт на том же HTTP-слушателе, что и панель, плеер и медиа — отдельный порт поднимать не нужно.

Поверхность Адрес Директива
REST API http://media.example.com:8080/api/v1 http <порт>;
То же поверх TLS https://media.example.com/api/v1 https <порт>;
Метрики Prometheus http://media.example.com:8080/metrics тот же слушатель

Дальше в примерах используется переменная окружения BASE — задайте её один раз:

BASE=https://media.example.com
Метрики живут вне /api/v1

GET /metrics — это корень хоста, а не /api/v1/metrics. Оба адреса существуют, но это разные поверхности: в корне — текстовая экспозиция Prometheus, под /api/v1 — JSON-дамп. Учётные данные у них общие. Подробности — Мониторинг и метрики.

Три вида учётных данных#

Все учётные данные задаются в блоке auth { } файла конфигурации и предъявляются одним и тем же заголовком Authorization: Bearer <ТОКЕН>.

Учётные данные Директива Что открывает
Сессия администратора admin_passwordPOST /api/v1/auth/login всё: любой маршрут, любой метод
Статический токен записи api_write_token все методы /api/v1/* и GET /metrics
Статический токен чтения api_read_token только GET, HEAD, OPTIONS на /api/v1/* и GET /metrics
Параметр Тип и допустимые значения По умолчанию Применение
admin_password строка, не пустая не задан горячее
api_read_token строка, не короче 16 символов не задан горячее
api_write_token строка, не короче 16 символов не задан горячее
session_secret строка, не пустая генерируется при старте горячее
admin_ip_bind флаг: admin_ip_bind; / admin_ip_bind off; off горячее

Токен записи — строгое надмножество токена чтения. Интеграции, которая и читает, и изменяет, достаточно одного токена записи; службе наблюдения выдавайте токен чтения.

Пустое значение любого токена отвергается при проверке конфигурации, как и совпадение api_read_token с api_write_token.

Гейт включается любыми учётными данными

Если не задано ничего — ни пароль, ни один из токенов, — /api/v1/* открыт полностью: сервер печатает предупреждение при старте, а POST /api/v1/auth/login отвечает 404 {"error":"auth disabled"}. Достаточно одной директивы из таблицы, чтобы гейт заработал.

Что не открывает ни один API-токен#

Статический токен никогда не становится ключом к воспроизведению: POST /api/v1/auth/media-grants отвечает ему 403. Доступ к медиа — отдельный уровень, см. Модель доступа.

Вход и получение сессионного токена#

curl -sS -X POST "$BASE/api/v1/auth/login" \
  -H 'Content-Type: application/json' \
  -d '{"password":"ваш-пароль-администратора"}'
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIs…","exp":1787186134}

Поле exp — абсолютный дедлайн в секундах Unix. Время жизни токена — 12 часов. Ключ подписи берётся из session_secret; если он не задан, секрет генерируется случайно при каждом старте, и токены не переживают перезапуск сервиса.

Удобная заготовка для всех остальных примеров:

TOKEN=$(curl -sS -X POST "$BASE/api/v1/auth/login" \
  -H 'Content-Type: application/json' \
  -d '{"password":"ваш-пароль-администратора"}' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["token"])')
Токен получен и работает
curl -sS "$BASE/api/v1/totals" -H "Authorization: Bearer $TOKEN"
{"streams":10,"sessions":1,"bytes_in":6508772297,"bytes_out":105870921,
 "dvr_sessions":0,"dvr_bytes_out":24412877,"live_sessions":1,"live_bytes_out":81458044}

Ответы входа при неудаче:

Код Тело Причина
400 {"error":"need password"} в теле нет ключа password либо это не JSON
401 {"error":"invalid password"} пароль неверный
404 {"error":"auth disabled"} admin_password не задан — входить некуда
413 {"error":"body too large"} тело больше 4 КиБ
429 {"error":"too many failed logins","retry_after_ms":4000} окно торможения для этого адреса
Перебор пароля тормозится на уровне узла

Неверный пароль отвечает 401 после безусловной задержки 600 мс, а адрес попадает в общеузловую таблицу торможения: пока окно открыто, запросы с него отсекаются кодом 429 ещё до чтения тела. Таблица общая для узла, поэтому открыть больше соединений не значит перебирать быстрее. Верный пароль сбрасывает состояние адреса.

Как передавать токен#

Только заголовком. Единственное исключение — поток событий:

# Правильно — всегда и везде:
curl -sS "$BASE/api/v1/streams" -H "Authorization: Bearer $TOKEN"

# Допустимо ТОЛЬКО для /api/v1/events и ТОЛЬКО с сессионным токеном:
curl -sN "$BASE/api/v1/events?topics=totals&token=$TOKEN"

Строку запроса ?token= принимает единственный маршрут — GET /api/v1/events, потому что браузерный EventSource не умеет задавать заголовки. На любом другом маршруте параметр игнорируется и запрос получает 401.

Статические токены нельзя передавать строкой запроса — никогда

api_read_token и api_write_token не принимаются в ?token= даже на /api/v1/events. Они долгоживущие: URL с таким токеном оседает в журналах каждого прокси по пути и в истории браузера. Для них — только заголовок Authorization.

Привязка сессии к адресу#

При auth { admin_ip_bind on; } сессионный токен привязан к IP-адресу, с которого выполнен вход, и отвергается при предъявлении с другого адреса. Реальный адрес берётся из заголовков X-Forwarded-For, Forwarded, X-Real-IP только если непосредственный клиент перечислен в trusted_proxies; иначе используется адрес сокета. За обратным прокси без trusted_proxies все входы будут выглядеть как приходящие с адреса прокси.

Обозначения прав в этой документации#

У каждой операции на страницах API стоит строка с требуемым уровнем:

Чтение — подходят любые учётные данные: сессия администратора, токен записи или токен чтения.

Запись — нужна сессия администратора либо api_write_token; токен чтения получает 403.

Только сессия — принимается исключительно сессионный токен из POST /auth/login; ни один статический токен не подходит.

Идентификатор потока в URL#

Идентификатор потока — непрозрачный путь, который может содержать / (cam1, live/cam1, site-north/entrance/main). Отсюда одно правило, о которое спотыкаются все интеграции:

  • В путях /api/v1/… идентификатор передаётся одним percent-encoded сегментом: live%2Fcam1.
  • В медиа-URL идентификатор идёт с обычными слэшами: /live/cam1/index.m3u8.
# Поток называется live/cam1
curl -sS "$BASE/api/v1/streams/live%2Fcam1" -H "Authorization: Bearer $TOKEN"   # снимок
curl -sS "$BASE/live/cam1/index.m3u8"                                            # плейлист

Маршруты GET|PATCH|DELETE /streams/{id}, /dvr-archive/{stream} и все маршруты с {name} жёстко отвергают литеральный / в сегменте. Маршруты действий (/start, /stop, /restart, /clone, /config, /timeseries, /sessions) принимают обе формы, но кодировать безопаснее всегда.

Кодируйте идентификатор программно

В bashpython3 -c 'import sys,urllib.parse as u; print(u.quote(sys.argv[1], safe=""))' "live/cam1". В Python — urllib.parse.quote(stream_id, safe="").

Форма ответа: конверта нет там, где вы его ждёте#

Ключ itemsне универсальный. Формы ровно три.

Форма Кто так отвечает
Конверт с пагинацией {"items":[…],"limit","total","raw_total","total_pages","next_cursor","page"} GET /streams, GET /sessions, GET /streams/{id}/sessions
Собственный конверт {"items":[…],"next_cursor","oldest","tip","limit"} GET /audit
Голый массив […] GET /storages, GET /config/streams, GET /config/history, GET /dvr-sync
Именованный объект {"templates":[…]}, {"libraries":[…]}, {"certificates":[…]}, {"fields":[…]}

Одиночные ресурсы (GET /streams/{id}, GET /system, GET /totals, GET /license) возвращают объект без обёртки.

Пагинация, сортировка и проекция#

Параметры ниже понимают все три пагинированных списка.

Параметр Тип и допустимые значения По умолчанию Смысл
limit целое, зажимается в 1 … 50000 100 размер страницы
page целое ≥ 1 номер страницы; приоритетнее cursor
cursor строка из next_cursor предыдущего ответа keyset-курсор следующей страницы
fields имена полей верхнего уровня через запятую все поля проекция элементов items
sort до 4 ключей колонка или колонка:asc|desc через запятую по id сортировка
dir asc | desc asc направление для ключей без явного суффикса
curl -sS "$BASE/api/v1/streams?fields=id,health,viewers,bitrate&sort=viewers:desc&limit=3" \
  -H "Authorization: Bearer $TOKEN"
{"items":[{"bitrate":2920000.0,"health":"ok","id":"cam1","viewers":1},
          {"bitrate":14649134.86,"health":"ok","id":"cam2","viewers":1},
          {"bitrate":3455232.76,"health":"ok","id":"cam3","viewers":0}],
 "limit":3,"next_cursor":"18446744073709551615\u001fcam3","page":null,
 "raw_total":10,"total":10,"total_pages":4}

Поле total — сколько строк осталось после фильтров, raw_total — сколько было до них. Значение next_cursor подставляйте в ?cursor= следующего запроса; если оно null — курсорная пагинация для этой сортировки недоступна, листайте через ?page=.

Курсор — непрозрачная строка, её надо кодировать

При сортировке по числовой колонке курсор склеен из двух частей служебным символом (\u001f в JSON-ответе, %1F в URL). Подставляйте значение только после percent-кодирования: с сырым символом curl отказывается собрать адрес (URL rejected: Malformed input to a URL function), а если отбросить «хвост» курсора после служебного символа, следующая страница придёт с перекрытием — последняя строка предыдущей повторится.

# Первая страница
cur=$(curl -sS "$BASE/api/v1/streams?fields=id&limit=3" -H "Authorization: Bearer $TOKEN" \
  | python3 -c 'import sys,json,urllib.parse; print(urllib.parse.quote(json.load(sys.stdin)["next_cursor"] or "", safe=""))')

# Следующая
curl -sS "$BASE/api/v1/streams?fields=id&limit=3&cursor=$cur" -H "Authorization: Bearer $TOKEN"
Плохая сортировка не даёт ошибки

Читающие маршруты никогда не отвечают 4xx на неизвестное имя колонки или направление: неизвестная колонка молча сводится к сортировке по умолчанию, неизвестное направление читается как asc. Если порядок вышел не тот, что вы ждали, — проверьте написание имени колонки, ошибки в ответе не будет.

Условные запросы#

Списки потоков и счётчики снабжены ETag. Присылайте If-None-Match — при совпадении сервер ответит 304 и не будет строить тело.

Ошибки#

Форм ошибок три, и они не смешиваются.

1. application/problem+json — все 404 на /api/* и все ошибки изменяющих операций:

{"type":"about:blank","title":"Not Found","status":404,"instance":"/api/v1/streams/nosuchstream"}

Поле type всегда about:blank, title — короткая машинно-стабильная формулировка, detail — пояснение для человека (у чтений отсутствует).

2. application/json {"error":"<тег>"} — маршруты /auth/* и любой отказ гейта:

{"error":"unauthorized"}

3. Короткий text/plain — только медиа-маршруты; плееры не читают тело.

Коды, которые вернёт любая операция#

Код Форма Когда
401 {"error":"unauthorized"} токена нет, он повреждён, истёк или предъявлен с чужого адреса при admin_ip_bind on
403 {"error":"forbidden","reason":"…"} учётные данные подлинные, но уровня недостаточно
404 problem+json ресурса нет либо маршрут не существует в этой сборке
405 text/plain метод не поддерживается маршрутом
409 problem+json имя занято либо конфигурация изменилась под вами
413 problem+json / {"error":…} тело больше лимита маршрута
422 problem+json правка не проходит проверку конфигурации
423 problem+json конфигурация заморожена файлом <config>.locked
501 problem+json сервер запущен без файла конфигурации
Недостаток прав — это 403, а не 401

Ответ 401 предложил бы корректному клиенту повторить запрос с тем же токеном — и так бесконечно. Поэтому при подлинном, но недостаточном токене приходит 403 с полем reason:

reason Что произошло
read_token_cannot_write токеном чтения выполнен небезопасный метод
read_token_cannot_read_config токеном чтения запрошен GET /api/v1/config
api_token_is_not_a_media_credential статическим токеном запрошен медиа-грант

Само значение токена в reason никогда не попадает. Сессия администратора этих ограничений не знает.

Ограничения транспорта#

Лимит Значение
Тело POST /auth/login и /auth/media-grants 4 КиБ → 413
Тело изменений конфигурации, потоков и хранилищ 1 МиБ → 413
Тело GET /api/v1/metrics (JSON-дамп) 2000 строк потоков и сессий → 413
Запросов на одно keep-alive-соединение 1000
Таймаут первого заголовка и простоя keep-alive 15 с

Машиночитаемая спецификация#

Полная спецификация OpenAPI 3.1 со всеми 71 операцией, схемами тел, примерами и кодами ответов:

openapi.yaml — скачать

Страницы этого раздела описывают операции над потоками, конфигурацией и системой. Всё остальное — сессии целиком, шаблоны, библиотеки VoD, обслуживание архива, кластер и действия узла — есть в спецификации; её можно открыть в любом просмотрщике OpenAPI или скормить генератору клиента.

Куда дальше#