Jitsi: нативный клиент lib-jitsi-meet на Next.js 16
Как фронт PIN входит в медиа-комнату нативной библиотекой lib-jitsi-meet (без веб-SPA jitsi-meet,
без iframe) и мостит медиа-события в собственный SignalR-мессенджер. Медиа идёт клиент ↔ SFU; SignalR — только
UI-нотификации и приглашения (авторитет ростера — presence-вебхук prosody, jitsi-rooms.md).
Поток входа
- Получить дескриптор входа. Из REST (
start/answer/join/media-token) или из hub-инвайта (CallInvite/ConferenceInvite) приходитMediaJoinModel:{ RoomName, AccessToken (JWT), SignalingUrl, MediaDomain, IceServers, ExpiresAt }. JitsiConnection. Собрать options из дескриптора (serviceUrl = SignalingUrl,hosts.domain = MediaDomain, JWT =AccessToken) →new JitsiMeetJS.JitsiConnection(...)→connect().initJitsiConference. По событиюCONNECTION_ESTABLISHED—connection.initJitsiConference(RoomName, conferenceOptions).- Локальные треки.
JitsiMeetJS.createLocalTracks({ devices: ['audio','video'] }); демонстрация экрана —createLocalTracks({ devices: ['desktop'] })только приcontext.features['screen-sharing'](jitsi-jwt.md). conference.join()→ добавить локальные треки.- Подписка на события (см. ниже) и мост в SignalR/Zustand.
- Обновление токена. До
ExpiresAt— запросить новыйMediaJoinModel(media-token), пересоздатьJitsiConnectionпри необходимости (в Jitsi нет refresh-flow, jitsi-jwt.md).
События lib-jitsi-meet → мост в SignalR/стор
Клиентские события — источник живого UI; авторитетный состав приходит отдельно через hub
(MediaParticipant, из presence-вебхука). Мост односторонний: события Jitsi обновляют локальный UI-стор, но
ростер сверяется с hub-проекцией.
Событие JitsiConferenceEvents | Значение на проводе | UI-действие |
|---|---|---|
CONFERENCE_JOINED | conference.joined | вошли; если features.recording && isModerator — стартовать запись (ниже) |
CONFERENCE_LEFT / CONFERENCE_FAILED | conference.left / conference.failed | выход/ошибка → закрыть комнату в UI |
USER_JOINED | conference.userJoined | добавить тайл (сверить с hub-ростером) |
USER_LEFT | conference.userLeft | убрать тайл |
TRACK_ADDED | conference.trackAdded | приклеить медиа-трек к тайлу |
RECORDER_STATE_CHANGED | conference.recorderStateChanged | индикатор записи (on/off/pending) |
DOMINANT_SPEAKER_CHANGED | conference.dominantSpeaker | подсветить активного спикера |
Авто-запись (модератор)
При CONFERENCE_JOINED, если context.features.recording === true и occupant — модератор
(jitsi-jwt.md), клиент стартует запись через RecordingManager:
conference.startRecording({
mode: 'file',
appData: JSON.stringify({
file_recording_metadata: { pin: { conferenceId, tenantId } }
})
})
mode: 'file' (из recordingConstants.mode.FILE) — запись в файл (не стрим). appData.file_recording_metadata
пробрасывается до jibri: mod_jibri_session.lua обогащает его initiator и conference_details.session_id,
а pin.* сохраняется до finalize-скрипта (recording-pipeline.md). Живой статус
записи — RECORDER_STATE_CHANGED (значения on/off/pending); авторитетная готовность файла —
отдельно через hub RecordingState(Available) и REST-скачивание.
FSD-раскладка (talent.frontends.pin / front-end)
| Слой | Содержимое |
|---|---|
shared/lib/jitsi | обёртка над lib-jitsi-meet: init connection/conference, createLocalTracks, типы событий |
entities/call | модели CallSession/Conference/Participant (Zod-схемы ответов), селекторы |
features/call-start, features/call-answer, features/call-screen-share, features/call-record | пользовательские действия (features не импортируют features) |
widgets/call-room | UI комнаты: сетка тайлов, контролы, индикатор записи |
shared/store (Zustand call-store) | живое UI-состояние комнаты (участники, треки, статус записи) |
- Компоненты комнаты —
'use client'; загрузка библиотеки —next/dynamicсssr: false(WebRTC/windowнедоступны в RSC/SSR). lib-jitsi-meetне публикуется в npm — вендорится изthirdparty/jitsi/lib-jitsi-meet(сборкаnpm run build→ UMD/ESM, ../README.md); версия фиксируется в монорепо.- Внешний ввод (ответы REST/hub) валидируется Zod в
entities/callперед попаданием в стор.
Конфиг-кнобы клиента
| Кноб | Значение | Зачем |
|---|---|---|
disableThirdPartyRequests | true | никаких внешних вызовов (аналитика/gravatar) — приватность + self-host |
p2p.enabled | true для 1:1 | медиа напрямую между двумя (JVB не нужен); при 3+ — escalation на SFU |
serviceUrl / hosts.domain | из SignalingUrl/MediaDomain | endpoint prosody |
Обратные ссылки
Автоссылки: где используется этот документ.
- calls (7): Медиа-звонки и запись на Jitsi — интеграционный гид (messenger), Медиа-звонки на Jitsi: план поставки (playbook аналитик → разработчик), Jitsi: именование комнат, admission и presence, Jitsi: инвентарь изменений в thirdparty/jitsi, Jitsi: минтинг JWT входа в .NET, Jitsi: сквозной пайплайн авто-записи, Jitsi: k8s-топология и взаимодействие с бэкендом
Связанные документы
- Дескриптор входа: MediaJoinModel; JWT/features: jitsi-jwt.md; комнаты/присутствие: jitsi-rooms.md; запись: recording-pipeline.md.
- Сущности: call-session.md, conference-session.md, call-participant.md; гид: README.md.
- Источник:
thirdparty/jitsi/lib-jitsi-meet/{JitsiMeetJS.ts,JitsiConferenceEvents.ts,modules/recording/RecordingManager.ts,modules/recording/recordingConstants.ts}. - Решение: ADR-0062; фронтенды: ../../../07-frontends/README.md.