Jitsi: сквозной пайплайн авто-записи
Полный путь записи звонка/конференции: решение политики → захват jibri → финализация → выгрузка в file
storage → Available → Kafka. Дом сущности записи — call-recording.md; здесь —
как её жизненный цикл реализуется на Jitsi (jibri) и file storage.
Этапы
- Решение. При старте сессии
RecordingPolicy.ShouldAutoRecord(chatKind, sessionKind)(типизированное, не булев флаг): звонки в контексте сделки → авто-запись (Trigger = AutoPolicy,InitiatedByUserId = null, INV-REC-3). СоздаётсяCallRecordingEntityвPending; участникам рассылается индикатор записи (NoticeState → Announced, INV-REC-4). - Старт захвата. Клиент-модератор при
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. - Обогащение метаданных.
mod_jibri_session.luaв двух проходах дописываетapp_data.file_recording_metadata:initiator(изcontext.user) иconference_details.session_id(=room._data.meetingId). Пробрасываемый клиентомpin: { conferenceId, tenantId }сохраняется — так finalize-скрипт знает нашу сессию. - Захват. jibri (headless Chrome,
SYS_ADMIN) пишет вJIBRI_RECORDING_DIR = /mnt/recordings/<dir>и кладёт рядомmetadata.json(initiator +conference_details.session_id+pin.*). WebhookRecording→CallRecording.Status: Pending → Recording,StartedAt. - Финализация. По завершении jibri вызывает
JIBRI_FINALIZE_RECORDING_SCRIPT_PATH = /usr/bin/pin_finalize.sh(замена штатногоjitsi_uploader.sh) с директорией записи. Скрипт стримит файл multipart вmessenger. - Ingest → file storage.
messenger(POST /internal/call-recordings/{sessionId}/ingest) стримит байты в file storage; получаетFileId; переводит записьUploading → Available(FileId+SizeBytes+DurationSeconds+Format, INV-REC-5) атомарно с публикацией Kafka. - Событие.
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.json — conference_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(клиентский SDKIFilesService.UploadFile/ multipartPOST /api/files/upload-file/form, streamed) — синхронно, без Kafka со стороны file storage. - Ключ объекта —
{CategoryId}/{FileId}; скачивание — CDN presignedGET(~1ч), выдаётся отдельным методом ../api/recordings/get-recording-download.md (auditReadSensitive). - Memory-bounded streaming. Записи крупные — использовать streamed multipart (
upload-file/form), не буферизовать файл в память ни вpin_finalize.sh, ни в ingest-эндпоинте, ни при выгрузке.
Блокирующие предпосылки file storage
Целевой сервис — F:\Workspace\Talent\talent.microservices.filestorage. До записи нужно (на стороне file
storage):
- Новая категория
CALL_RECORDINGS(сидер/миграция). - Разрешённые видео content-types:
video/mp4,video/webm,video/x-matroska(mkv); аудиоaudio/mpeg(mp3) для будущего audio-only. - Схема уникального отображаемого имени в категории —
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+ hubRecordingState.
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (1): Entity / Aggregate: CallRecordingEntity — запись звонка (хранение в file storage)
- API (1): POST /internal/call-recordings/{sessionId}/ingest — Финализация записи (jibri → file storage)
- Use Cases (1): UC-MSG-007. Авто-запись звонка по сделке и её выдача участнику
- События (1): Event Contract: pin.messenger.call-recording-available (CallRecordingAvailableEvent)
- calls (11): Медиа-звонки и запись на Jitsi — интеграционный гид (messenger), Медиа-звонки на Jitsi: план поставки (playbook аналитик → разработчик), Предусловия в file storage для хранения записей звонков, Backend-реализация медиа-звонков: спека → код (messenger), Jitsi-звонки: план верификации и приёмки, Jitsi: именование комнат, admission и presence, Jitsi: инвентарь изменений в thirdparty/jitsi, Jitsi: нативный клиент lib-jitsi-meet на Next.js 16, Jitsi: роадмап транскрипции и конспектов (будущая фаза), Jitsi: k8s-топология и взаимодействие с бэкендом, Jitsi: k8s/Helm-манифесты (SKETCH)
Связанные документы
- Сущность записи: call-recording.md; событие: call-recording-available.md; ingest/скачивание: ../api/internal/, ../api/recordings/get-recording-download.md.
- Клиент (старт записи): jitsi-client.md; развёртывание jibri: jitsi-deployment.md; изменения jibri: jitsi-modifications.md; роадмап STT: transcription-roadmap.md.
- Источник:
thirdparty/jitsi/jitsi-meet/resources/prosody-plugins/mod_jibri_session.lua,thirdparty/jitsi/infra-provisioning/nomad/jibri.hcl(JIBRI_FINALIZE_RECORDING_SCRIPT_PATH,JIBRI_RECORDING_DIR),thirdparty/jitsi/jicofo/.../reference.conf(conference.enable-moderator-checks,jibri.brewery-jid). - Решения: ADR-0062, ADR-0032 retention.