Skip to content

Records API

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

API архива уже можно использовать в пилотной интеграции. Сам recorder пока развивается: записи могут состоять из отдельных дорожек участников, а готовый консолидированный файл конференции пока не является обязательным результатом.

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

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

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

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

  • консолидированный MP4, MKV или MP3 для всей конференции;
  • получение одной записи по ID;
  • серверная фильтрация и pagination;
  • поиск записи по external_id партнерской комнаты;
  • удаление записи и управление retention через API;
  • webhook recording.ready;
  • создание и остановка 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

    Recorder->>Storage: upload raw tracks and reports
    Storage-->>Recorder: blob_id and URL
    Recorder->>API: CAN artifact_ready
    API->>Index: upsert recording by id
    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.

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 авторизация не работает.

Временное правило доступа к файлам

Архив проверяет capability recordings.access, а Storage дополнительно проверяет владельца blob-файла. В текущей версии service account партнера для чтения файлов должен быть привязан к тому же техническому пользователю, от имени которого recorder загружает артефакты.

Рекомендуемая конфигурация:

service account: recorder
    -> client_id: <recorder technical user>

service account: partner-recordings
    -> client_id: <the same recorder technical user>
    -> separate client_secret

Не передавайте партнеру CAN token и пароль пользователя recorder. Партнер получает только отдельный client_secret.

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

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",
      "profile": "raw_tracks_v1",
      "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": "tracks/audio/device_1002.ogg",
          "blob_id": "8bc08b8c-58e1-4f90-8af0-8a29fbe04f52",
          "url": "/api/storage/blobs/8bc08b8c-58e1-4f90-8af0-8a29fbe04f52",
          "size": 364980,
          "content_type": "audio/ogg",
          "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.
profile string Профиль recorder, например raw_tracks_v1.
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 для диапазона.

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

Форматы recorder

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.

Консолидация

В текущем API запись не следует интерпретировать как один готовый фильм. Партнер должен:

  1. проверить profile;
  2. прочитать manifest.json и report.json;
  3. показать пользователю доступные дорожки или обработать их своим pipeline;
  4. не ожидать обязательное поле consolidated_url.

После появления консолидации она будет добавлена новым артефактом и новыми полями без удаления raw tracks.

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

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

{
  "external_id": "notabridge_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.

Polling

До появления 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.

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