190 lines
7.2 KiB
Python
190 lines
7.2 KiB
Python
"""
|
||
Сервис для работы с Jitsi Meet.
|
||
Генерация JWT токенов, создание комнат, управление участниками.
|
||
"""
|
||
import jwt
|
||
import uuid
|
||
from datetime import datetime, timedelta
|
||
from django.conf import settings
|
||
from typing import Dict, Optional
|
||
|
||
|
||
class JitsiService:
|
||
"""Сервис для интеграции с Jitsi Meet."""
|
||
|
||
@staticmethod
|
||
def generate_room_name() -> str:
|
||
"""
|
||
Генерация уникального имени комнаты.
|
||
|
||
Returns:
|
||
str: Уникальное имя комнаты
|
||
"""
|
||
return str(uuid.uuid4())
|
||
|
||
@staticmethod
|
||
def generate_jwt_token(
|
||
room_name: str,
|
||
user_id: int,
|
||
user_name: str,
|
||
user_email: str,
|
||
is_moderator: bool = False,
|
||
avatar_url: Optional[str] = None,
|
||
expires_in_minutes: int = 120
|
||
) -> str:
|
||
"""
|
||
Генерация JWT токена для аутентификации в Jitsi Meet.
|
||
|
||
Args:
|
||
room_name: Имя комнаты
|
||
user_id: ID пользователя
|
||
user_name: Имя пользователя
|
||
user_email: Email пользователя
|
||
is_moderator: Является ли пользователь модератором
|
||
avatar_url: URL аватара пользователя
|
||
expires_in_minutes: Время жизни токена в минутах
|
||
|
||
Returns:
|
||
str: JWT токен
|
||
"""
|
||
# Получаем настройки из environment
|
||
app_id = getattr(settings, 'JITSI_APP_ID', 'platform')
|
||
app_secret = getattr(settings, 'JITSI_APP_SECRET', 'secret')
|
||
|
||
# Время создания и истечения токена
|
||
now = datetime.utcnow()
|
||
exp = now + timedelta(minutes=expires_in_minutes)
|
||
|
||
# Payload токена
|
||
payload = {
|
||
# Стандартные JWT claims
|
||
'iss': app_id,
|
||
'aud': app_id,
|
||
'sub': getattr(settings, 'JITSI_XMPP_DOMAIN', 'meet.jitsi'),
|
||
'room': room_name,
|
||
'exp': int(exp.timestamp()),
|
||
'iat': int(now.timestamp()),
|
||
'nbf': int(now.timestamp()),
|
||
|
||
# Контекст пользователя
|
||
'context': {
|
||
'user': {
|
||
'id': str(user_id),
|
||
'name': user_name,
|
||
'email': user_email,
|
||
'avatar': avatar_url or '',
|
||
'moderator': is_moderator,
|
||
},
|
||
'group': getattr(settings, 'JITSI_APP_NAME', 'Платформа'),
|
||
},
|
||
|
||
# Права доступа
|
||
'moderator': is_moderator,
|
||
}
|
||
|
||
# Генерация токена
|
||
token = jwt.encode(
|
||
payload,
|
||
app_secret,
|
||
algorithm='HS256'
|
||
)
|
||
|
||
return token
|
||
|
||
@staticmethod
|
||
def get_room_url(
|
||
room_name: str,
|
||
jwt_token: Optional[str] = None,
|
||
config_overrides: Optional[Dict] = None
|
||
) -> str:
|
||
"""
|
||
Получение URL комнаты Jitsi Meet.
|
||
|
||
Args:
|
||
room_name: Имя комнаты
|
||
jwt_token: JWT токен (опционально)
|
||
config_overrides: Переопределение конфигурации (опционально)
|
||
|
||
Returns:
|
||
str: URL комнаты
|
||
"""
|
||
jitsi_url = getattr(settings, 'JITSI_PUBLIC_URL', 'http://127.0.0.1:8443')
|
||
|
||
# Базовый URL
|
||
url = f"{jitsi_url}/{room_name}"
|
||
|
||
# Добавляем параметры
|
||
params = []
|
||
|
||
if jwt_token:
|
||
params.append(f"jwt={jwt_token}")
|
||
|
||
if config_overrides:
|
||
# Добавляем config overrides как URL параметры
|
||
for key, value in config_overrides.items():
|
||
if isinstance(value, bool):
|
||
value = 'true' if value else 'false'
|
||
params.append(f"config.{key}={value}")
|
||
|
||
if params:
|
||
url += '?' + '&'.join(params)
|
||
|
||
return url
|
||
|
||
@staticmethod
|
||
def get_default_config_overrides(user_role: str = 'client') -> Dict:
|
||
"""
|
||
Получение стандартных переопределений конфигурации.
|
||
|
||
Args:
|
||
user_role: Роль пользователя (mentor, client, parent)
|
||
|
||
Returns:
|
||
Dict: Переопределения конфигурации
|
||
"""
|
||
# Базовые настройки для всех
|
||
config = {
|
||
'startWithAudioMuted': False,
|
||
'startWithVideoMuted': False,
|
||
'prejoinPageEnabled': False, # Отключаем prejoin
|
||
'disableDeepLinking': True, # Отключаем deep linking
|
||
'enableWelcomePage': False, # Отключаем welcome page
|
||
'enableClosePage': False, # Отключаем close page
|
||
'defaultLanguage': 'ru',
|
||
'disableThirdPartyRequests': True,
|
||
'enableNoisyMicDetection': True,
|
||
|
||
# Настройки для использования GPU (аппаратное ускорение)
|
||
'p2p.enabled': False, # Отключаем P2P для работы через сервер с GPU
|
||
'resolution': 720, # Разрешение видео
|
||
'maxReceiveFrameRate': 30, # Максимальный FPS
|
||
'maxReceiveResolution': 720, # Максимальное разрешение приема
|
||
'videoQuality.adaptiveLastN': 4, # Адаптивное качество
|
||
'videoQuality.persist': True, # Сохранять настройки качества
|
||
|
||
# Приоритет H.264 кодека для лучшей поддержки GPU
|
||
'codecPreferences': 'H264', # H.264 лучше поддерживает GPU ускорение
|
||
|
||
# Дополнительные настройки производительности
|
||
'enableLayerSuspension': True, # Приостановка слоев для экономии ресурсов
|
||
'enableRemb': True, # REMB (Receiver Estimated Maximum Bitrate) для адаптивного битрейта
|
||
'enableTcc': True, # TCC (Transport-CC) для лучшего контроля качества
|
||
}
|
||
|
||
# Настройки для клиентов (студентов)
|
||
if user_role == 'client':
|
||
config.update({
|
||
'disableModeratorIndicator': True,
|
||
'enableForcedReload': False,
|
||
})
|
||
|
||
# Настройки для менторов (модераторы)
|
||
elif user_role == 'mentor':
|
||
config.update({
|
||
'disableModeratorIndicator': False,
|
||
'enableForcedReload': True,
|
||
})
|
||
|
||
return config
|
||
|