Skip to content

Records API

Records API предназначен для backend-to-backend доступа к архиву записей VideoGrace. Партнерский backend получает список завершенных recording jobs, метаданные записи и ссылки на артефакты в VideoGrace Storage.

Основной профиль recorder consolidated_native создает готовый единый Matroska-файл конференции с H.264-видео и Opus-звуком. Диагностические профили также могут сохранять отдельные дорожки участников.

Что доступно сейчас

Текущий контракт позволяет:

  • получить до 500 последних записей;
  • связать запись с конференцией по conference_tag;
  • определить время, длительность, профиль и статус записи;
  • получить консолидированный recording.mkv, report.json, report.jsonl и events.jsonl;
  • скачать готовую запись или диагностические аудио- и видеодорожки;
  • проверить размер и SHA-256 каждого файла;
  • получить подписанный webhook recording.ready после upload и индексации;
  • повторно читать архив независимо от истории CAN jobs.

Пока не входят в публичный контракт:

  • консолидированный MP4 и MP3 для всей конференции;
  • получение одной записи по ID;
  • серверная фильтрация и pagination;
  • поиск записи по external_id партнерской комнаты;
  • удаление записи и управление retention через API;
  • создание и остановка recording job через партнерский Records API.

Дополнительные поля могут появляться без изменения версии API. Клиент должен игнорировать неизвестные поля.

Общая схема

sequenceDiagram
    participant Partner as Partner backend
    participant API as VideoGrace API
    participant Recorder as Recorder worker
    participant Storage as VideoGrace Storage
    participant Index as records.db
    participant Webhook as Partner webhook

    Recorder->>Storage: upload recording.mkv and reports
    Storage-->>Recorder: blob_id and URL
    Recorder->>API: CAN artifact_ready
    API->>Index: upsert recording by id
    API->>Webhook: recording.ready
    Partner->>API: GET /api/v1.0/recordings
    API-->>Partner: recordings and artifact URLs
    Partner->>Storage: GET /api/storage/blobs/{blob_id}
    Storage-->>Partner: file or byte range

records.db является постоянным индексом архива. Очистка старых CAN jobs не удаляет записи из Records API.

Базовый адрес

https://<server>

Архив:

GET /api/v1.0/recordings

Файлы:

GET /api/storage/blobs/{blob_id}

Используйте только HTTPS.

Авторизация

Для партнерской интеграции создается отдельный service account. Capability recordings.access нужна ему для просмотра полного архива через Records API. Для приема recording.ready и скачивания артефактов собственных партнерских комнат это глобальное право не требуется.

Access token можно получить через Integration API:

POST /api/v1/integration/auth/token
Content-Type: application/json

{
  "service_account": "partner-recordings",
  "client_secret": "<secret>"
}

Ответ:

{
  "result": 200,
  "access_token": "<jwt>",
  "token_type": "Bearer",
  "expires_in": 604800,
  "refresh_token": null
}

refresh_token в текущей версии не выдается. После истечения token backend повторяет POST /api/v1/integration/auth/token.

Во всех запросах используйте:

Authorization: Bearer <access_token>

JWT должен быть подписан серверным VG_JWT_SECRET. Без настроенного JWT service-account авторизация не работает.

Доступ к файлам

После индексации сервер помечает загруженные артефакты scope recording:<conference_tag>. Такой приватный blob может скачать его владелец, владелец соответствующей Integration API room или технический пользователь с capability recordings.access. Поэтому партнерский service account не нужно привязывать к пользователю recorder. Для скачивания URL из recording.ready владельцу партнерской комнаты глобальный grant recordings.access не нужен.

Не передавайте партнеру CAN token, service secret recorder или пароль его технического пользователя. Партнер получает собственный client_secret. Capability recordings.access выдавайте связанному пользователю только тогда, когда backend действительно должен читать полный Records API.

Получение архива

GET /api/v1.0/recordings
Authorization: Bearer <access_token>
Accept: application/json

Пример:

curl -sS \
  -H "Authorization: Bearer $VG_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  https://join.example.com/api/v1.0/recordings

Успешный ответ:

{
  "ok": true,
  "recordings": [
    {
      "id": "19f7010bc231",
      "job_id": "19f7010bc231",
      "conference_tag": "lesson-a8f3k2",
      "conference_session_id": 1842,
      "profile": "consolidated_native",
      "status": "completed",
      "started_at_ms": 1783952338332,
      "ended_at_ms": 1783952395816,
      "duration_ms": 57484,
      "output_dir": "recordings/lesson-a8f3k2/19f7010bc231",
      "report_blob_id": "e8a82165-331d-41bb-887c-9abb240c8c70",
      "report_url": "/api/storage/blobs/e8a82165-331d-41bb-887c-9abb240c8c70",
      "report_jsonl_blob_id": "f805d628-2998-49f2-b089-c9a405683826",
      "report_jsonl_url": "/api/storage/blobs/f805d628-2998-49f2-b089-c9a405683826",
      "manifest_blob_id": "1f0287b8-d88a-43d6-b29c-ab89522893db",
      "manifest_url": "/api/storage/blobs/1f0287b8-d88a-43d6-b29c-ab89522893db",
      "events_blob_id": "fc9848a4-3ff5-48d9-b9e0-d2b5e6195b41",
      "events_url": "/api/storage/blobs/fc9848a4-3ff5-48d9-b9e0-d2b5e6195b41",
      "files": [
        {
          "path": "recording.mkv",
          "blob_id": "8bc08b8c-58e1-4f90-8af0-8a29fbe04f52",
          "url": "/api/storage/blobs/8bc08b8c-58e1-4f90-8af0-8a29fbe04f52",
          "size": 364980,
          "content_type": "video/x-matroska",
          "sha256": "157579d36ac791d840fa26862b1f85c271f9f869c6e1b4b09d5dfbd96ff12219",
          "scope": "manual"
        }
      ],
      "created_at": 1783952396,
      "updated_at": 1783952396,
      "artifact": {}
    }
  ]
}

Записи отсортированы от новых к старым. Сервер возвращает не более 500 элементов. Query-параметры фильтрации в текущей версии не поддерживаются.

Поле db_path, если присутствует в ответе, является диагностическим и не должно использоваться партнерским клиентом.

Поля записи

Поле Тип Описание
id string Стабильный ID записи. Обычно совпадает с job_id.
job_id string ID recording job в CAN.
conference_tag string Тег конференции VideoGrace.
conference_session_id integer Точный ID сеанса конференции из stats.db. Используйте его для связи записи со статистикой и расшифровкой; тег обозначает комнату и может повторяться между сеансами. Для старых записей без достоверной связи равен 0.
profile string Профиль recorder, обычно consolidated_native.
status string Итоговый статус recorder. Значение следует считать расширяемым.
started_at_ms integer Начало записи, Unix time в миллисекундах.
ended_at_ms integer Завершение записи, Unix time в миллисекундах.
duration_ms integer Длительность записи в миллисекундах.
output_dir string Внутренний путь worker. Не использовать для скачивания.
report_blob_id string ID итогового report.json.
report_url string URL итогового report.json.
report_jsonl_blob_id string ID append-only отчета report.jsonl.
report_jsonl_url string URL report.jsonl.
manifest_blob_id string ID track manifest.
manifest_url string URL track manifest.
events_blob_id string ID журнала событий.
events_url string URL журнала событий.
files array Загруженные файлы записи.
artifact object Исходный расширяемый payload artifact_ready.
created_at integer Создание записи в индексе, Unix time в секундах.
updated_at integer Последнее обновление индекса, Unix time в секундах.

Пустая строка означает, что соответствующий артефакт recorder не создал или не загрузил.

Статусы

Сейчас обычно встречаются:

Статус Значение
completed Recorder завершил job штатно.
stopped Запись остановлена пользователем или отменой job, отчет сохранен.

Не используйте закрытый enum на стороне партнера. Новые статусы должны отображаться как неизвестное строковое значение, а не приводить к ошибке десериализации.

Файлы записи

Каждый элемент files содержит:

Поле Тип Описание
path string Логический путь файла внутри записи.
blob_id string ID объекта в VideoGrace Storage.
url string Относительный или абсолютный URL скачивания.
size integer Размер в байтах.
content_type string MIME type.
sha256 string SHA-256 содержимого в hex.
scope string Storage scope. Диагностическое поле.

Если URL относительный, его нужно разрешить относительно базового адреса VideoGrace:

/api/storage/blobs/abc
    -> https://join.example.com/api/storage/blobs/abc

Не формируйте URL из output_dir.

Скачивание

curl -fL \
  -H "Authorization: Bearer $VG_ACCESS_TOKEN" \
  -o device_1002.ogg \
  "https://join.example.com/api/storage/blobs/8bc08b8c-58e1-4f90-8af0-8a29fbe04f52"

После скачивания рекомендуется проверить размер и SHA-256:

test "$(wc -c < device_1002.ogg | tr -d ' ')" = "364980"
echo "157579d36ac791d840fa26862b1f85c271f9f869c6e1b4b09d5dfbd96ff12219  device_1002.ogg" \
  | sha256sum -c -

Storage поддерживает HTTP Range:

GET /api/storage/blobs/{blob_id}
Authorization: Bearer <access_token>
Range: bytes=0-1048575

Успешный ответ будет иметь HTTP 200 для полного файла или 206 Partial Content для диапазона. Поддерживаются закрытый (bytes=0-1048575), открытый (bytes=1048576-) и suffix-диапазон (bytes=-65536) для чтения индекса медиаконтейнера с конца файла. Некорректный или составной диапазон возвращает 416 Range Not Satisfiable.

Не передавайте token в query string: URL может попасть в access log, историю браузера или систему аналитики.

Форматы recorder

consolidated_native

Основной production-профиль. Python CAN worker запускает нативный Recorder, который использует общий с Consolidator pipeline раскладки, VAD, модерации, микширования и кодирования. Результат:

  • recording.mkv: единый H.264 + Opus файл конференции;
  • events.jsonl: события lifecycle;
  • report.json и report.jsonl: отчет и журнал финализации.

Нативный процесс не выполняет upload самостоятельно. Python worker после финализации файла загружает его в Storage и отправляет artifact_ready. Локальный каталог job удаляется только после успешной загрузки всех артефактов и подтвержденного CAN complete. Ошибка upload или CAN сохраняет файлы на узле для повторной доставки и диагностики.

raw_tracks_v1

Профиль хранит отдельные дорожки по device_id:

  • аудио: Ogg/Opus, обычно tracks/audio/device_<id>.ogg;
  • камера: H.264 Annex-B или IVF/VP8;
  • демонстрация экрана: отдельная видеодорожка;
  • manifest.json: описание дорожек, codec и lifecycle;
  • events.jsonl: события конференции, устройств и VAD;
  • report.json: итоговый снимок recording job;
  • report.jsonl: append-only журнал финализации.

Наличие конкретного типа дорожки зависит от устройств, активных во время конференции.

decoded_audio

Профиль может содержать декодированные WAV или MP3-сегменты. Состав файлов определяется параметрами recorder job.

Выбор результата

Партнер должен проверить profile. Для consolidated_native основным media artifact является recording.mkv в массиве files. Для raw_tracks_v1 следует читать manifest.json и обрабатывать отдельные дорожки. Не выбирайте файл только по его позиции в массиве: используйте path и content_type.

Связь с партнерской комнатой

Integration API возвращает tag при создании комнаты. Сохраните его рядом с external_id:

{
  "external_id": "partner_lesson_987",
  "tag": "lesson-a8f3k2"
}

Records API возвращает этот же tag в conference_tag. Текущая схема связи:

partner lesson external_id
    -> saved VideoGrace room tag
    -> recording.conference_tag

Не пытайтесь извлекать external_id из conference_tag.

Webhook recording.ready

Для партнерских комнат рекомендуется настроить webhook через Integration API:

PUT /api/v1/integration/webhook
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "url": "https://partner.example.com/webhooks/videograce",
  "secret": "replace-with-a-random-secret",
  "events": ["recording.ready"],
  "enabled": true
}

Событие ставится в персистентную очередь после завершения загрузки файлов и успешного commit записи в records.db. Payload содержит external_id, conference_tag, метаданные записи, список файлов и URL артефактов. Формат подписи, retry policy и полный пример payload описаны в разделе Integration API: Webhook готовности записи.

Получатель должен дедуплицировать повторы по полю id или заголовку X-VideoGrace-Delivery.

Polling как fallback

Если webhook временно недоступен или интеграция еще не переведена на события, можно использовать polling:

  1. Выполнять GET /api/v1.0/recordings раз в 10-30 секунд, пока ожидается запись.
  2. Индексировать элементы по id.
  3. Обновлять существующий элемент, если изменился updated_at.
  4. Считать запись доступной, когда нужные URL и files уже присутствуют.
  5. Использовать exponential backoff при HTTP 5xx.

artifact_ready индексируется после загрузки файлов. Между остановкой записи и ее появлением в API возможна задержка на финализацию и upload.

Ошибки

Архив

HTTP Причина
200 Запрос выполнен. Проверить ok.
401 Token отсутствует, истек или невалиден.
403 У пользователя нет recordings.access.

Если индекс недоступен, сервер может вернуть HTTP 200:

{
  "ok": false,
  "error": "database error",
  "recordings": []
}

Клиент должен проверять не только HTTP status, но и ok.

Storage

HTTP Причина
200 Полный файл.
206 Диапазон файла.
401 Token не передан.
403 Token не имеет доступа к blob.
404 Blob не найден.
416 Некорректный Range.

При 401 получите новый access token. При 403 проверьте привязку service account к техническому пользователю recorder.

Рекомендуемый flow партнера

flowchart TD
    Token[Получить access token] --> Rooms[Получить список записей]
    Rooms --> Match[Найти conference_tag]
    Match --> Ready{Есть files и report_url?}
    Ready -->|Нет| Wait[Повторить polling позже]
    Wait --> Rooms
    Ready -->|Да| Report[Скачать report.json]
    Report --> Manifest[Скачать manifest.json при наличии]
    Manifest --> Files[Скачать нужные files]
    Files --> Verify[Проверить size и sha256]
    Verify --> Publish[Показать запись или передать в обработку]

Минимальный smoke-тест

BASE_URL=https://join.example.com

ACCESS_TOKEN="$(
  curl -fsS "$BASE_URL/api/v1/integration/auth/token" \
    -H 'Content-Type: application/json' \
    -d '{
      "service_account": "partner-recordings",
      "client_secret": "'"$VG_RECORDINGS_CLIENT_SECRET"'"
    }' |
  python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])'
)"

curl -fsS "$BASE_URL/api/v1.0/recordings" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Accept: application/json' |
python3 -m json.tool

Для полного smoke-теста возьмите первый непустой report_url, разрешите его относительно BASE_URL и скачайте с тем же bearer token.

Версионирование

Текущий endpoint использует ветку API v1.0. В рамках совместимых изменений сервер может:

  • добавлять поля;
  • добавлять новые значения status и profile;
  • добавлять новые типы файлов;
  • возвращать новые объекты внутри artifact.

Несовместимые изменения должны публиковаться в новом endpoint или с новым явным version contract.

Связанные разделы