Перейти к основному содержимому

Jitsi: сквозной пайплайн авто-записи

Полный путь записи звонка/конференции: решение политики → захват jibri → финализация → выгрузка в file storage → Available → Kafka. Дом сущности записи — call-recording.md; здесь — как её жизненный цикл реализуется на Jitsi (jibri) и file storage.

Этапы

  1. Решение. При старте сессии RecordingPolicy.ShouldAutoRecord(chatKind, sessionKind) (типизированное, не булев флаг): звонки в контексте сделки → авто-запись (Trigger = AutoPolicy, InitiatedByUserId = null, INV-REC-3). Создаётся CallRecordingEntity в Pending; участникам рассылается индикатор записи (NoticeState → Announced, INV-REC-4).
  2. Старт захвата. Клиент-модератор при CONFERENCE_JOINED и features.recording вызывает startRecording({ mode:'file', appData }) (jitsi-client.md). jicofo с conference.enable-moderator-checks = true разрешает запись только модератору, берёт jibri из brewery (jibri.brewery-jid), pending-timeout = 90s.
  3. Обогащение метаданных. mod_jibri_session.lua в двух проходах дописывает app_data.file_recording_metadata: initiator (из context.user) и conference_details.session_id (= room._data.meetingId). Пробрасываемый клиентом pin: { conferenceId, tenantId } сохраняется — так finalize-скрипт знает нашу сессию.
  4. Захват. jibri (headless Chrome, SYS_ADMIN) пишет в JIBRI_RECORDING_DIR = /mnt/recordings/<dir> и кладёт рядом metadata.json (initiator + conference_details.session_id + pin.*). Webhook RecordingCallRecording.Status: Pending → Recording, StartedAt.
  5. Финализация. По завершении jibri вызывает JIBRI_FINALIZE_RECORDING_SCRIPT_PATH = /usr/bin/pin_finalize.sh (замена штатного jitsi_uploader.sh) с директорией записи. Скрипт стримит файл multipart в messenger.
  6. Ingest → file storage. messenger (POST /internal/call-recordings/{sessionId}/ingest) стримит байты в file storage; получает FileId; переводит запись Uploading → Available (FileId + SizeBytes + DurationSeconds + Format, INV-REC-5) атомарно с публикацией Kafka.
  7. Событие. pin.messenger.call-recording-available.v1 (ключ SessionId) — консюмеры crm/ notifications (call-recording-available.md).

Контракт finalize-скрипта pin_finalize.sh

Заменяет jitsi_uploader.sh; задаётся JIBRI_FINALIZE_RECORDING_SCRIPT_PATH. jibri вызывает его с директорией завершённой записи.

АспектЗначение
Вход$1 = путь к директории записи (/mnt/recordings/<dir>), содержащей медиа-файл + metadata.json
Чтениеиз $1/metadata.jsonconference_details.session_id, pin.conferenceId/pin.tenantId (наш id сессии + тенант)
Выходmultipart-stream файла в messenger: POST /api/messenger/internal/call-recordings/{sessionId}/ingest (HMAC/mTLS, ../api/internal/)
Успехответ 2xx → удалить локальную директорию $1 (место освобождено)
Отказне-2xxне удалять; выйти с ненулевым кодом (jibri/оператор ретраит); запись остаётся вне Available

sessionId в маршруте берётся из metadata.json (наш conferenceId/callSessionId), а не из имени файла. Скрипт не грузит в file storage напрямую — байты проксирует messenger (единая точка авторизации, tenant и именования).

Выгрузка в file storage

messenger — единственный, кто пишет в file storage (по образцу вложений сообщений):

  • Нет presigned-PUT: байты проксируются через messenger (клиентский SDK IFilesService.UploadFile / multipart POST /api/files/upload-file/form, streamed) — синхронно, без Kafka со стороны file storage.
  • Ключ объекта — {CategoryId}/{FileId}; скачивание — CDN presigned GET (~1ч), выдаётся отдельным методом ../api/recordings/get-recording-download.md (audit ReadSensitive).
  • Memory-bounded streaming. Записи крупные — использовать streamed multipart (upload-file/form), не буферизовать файл в память ни в pin_finalize.sh, ни в ingest-эндпоинте, ни при выгрузке.

Блокирующие предпосылки file storage

Целевой сервис — F:\Workspace\Talent\talent.microservices.filestorage. До записи нужно (на стороне file storage):

  1. Новая категория CALL_RECORDINGS (сидер/миграция).
  2. Разрешённые видео content-types: video/mp4, video/webm, video/x-matroska (mkv); аудио audio/mpeg (mp3) для будущего audio-only.
  3. Схема уникального отображаемого имени в категории — FileName = call-{sessionId}-{ts} (call-recording.md).

Без этих предпосылок ingest завершается MSG_STORAGE_UNAVAILABLE/MSG_RECORDING_FILE_MISSING — запись уходит в Failed (UploadFailed).

Форматы

jibri пишет видеомикс (Mp4/Webm) — основной путь. Audio-only .mka (multitrack-recorder) — не в MVP, относится к роадмапу транскрипции (transcription-roadmap.md). Format проставляется в Available из результата захвата (enum RecordingFormat).

Retention и согласие

  • Retention. RetentionUntil (ADR-0032) → по истечении запись переходит в Deleted: файл удаляется из file storage, FileId очищается, строка-факт сохраняется (метаданные без бинаря).
  • Согласие/индикатор. Даже при авто-записи участники уведомляются индикатором записи (NoticeState → Announced до Recording, INV-REC-4); пер-участниковое согласие — роадмап. Живой индикатор в UI — RECORDER_STATE_CHANGED + hub RecordingState.

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы