uchill/backend/apps/video/janus_views.py

264 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Django views для работы с Janus Gateway.
Параллельная реализация с ion-sfu.
"""
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.conf import settings
from django.utils import timezone
from .models import VideoRoom, VideoParticipant, VideoCallLog
from .serializers import VideoRoomSerializer
from .janus_client import get_janus_client, JanusClient
from .permissions import IsVideoRoomParticipant
import logging
logger = logging.getLogger(__name__)
class JanusVideoRoomViewSet(viewsets.ViewSet):
"""
ViewSet для управления видеокомнатами через Janus Gateway.
Параллельная реализация с IonSFUVideoRoomViewSet.
"""
permission_classes = [IsAuthenticated]
lookup_field = 'room_id'
@action(detail=False, methods=['post'], url_path='create-room')
def create_room(self, request):
"""
Создать новую видеокомнату в Janus Gateway.
POST /api/video/janus/create-room/
Body:
{
"lesson_id": 123,
"is_recording": false,
"max_participants": 6
}
"""
lesson_id = request.data.get('lesson_id')
is_recording = request.data.get('is_recording', False)
max_participants = request.data.get('max_participants', 6)
if not lesson_id:
return Response(
{'error': 'lesson_id обязателен'},
status=status.HTTP_400_BAD_REQUEST
)
try:
from apps.schedule.models import Lesson
lesson = Lesson.objects.select_related('mentor', 'client', 'client__user').get(id=lesson_id)
except Lesson.DoesNotExist:
return Response(
{'error': 'Занятие не найдено'},
status=status.HTTP_404_NOT_FOUND
)
# Проверяем права
if request.user not in [lesson.mentor, lesson.client]:
return Response(
{'error': 'У вас нет доступа к этому занятию'},
status=status.HTTP_403_FORBIDDEN
)
# Проверяем, есть ли уже комната
if hasattr(lesson, 'video_room'):
return Response(
{'error': 'Видеокомната для этого занятия уже существует'},
status=status.HTTP_400_BAD_REQUEST
)
try:
# Создаем комнату в Django
video_room = VideoRoom.objects.create(
lesson=lesson,
mentor=lesson.mentor,
client=lesson.client,
is_recording=is_recording,
max_participants=max_participants,
sfu_type='janus' # Указываем тип SFU
)
# Создаем комнату в Janus Gateway
with get_janus_client() as janus:
# Используем числовой ID для Janus (преобразуем UUID в число)
janus_room_id = int(str(video_room.room_id).replace('-', '')[:12], 16) % (2**31 - 1)
# Генерируем уникальный secret для комнаты (или используем из настроек)
from django.conf import settings
room_secret = getattr(settings, 'JANUS_ROOM_SECRET', 'adminpwd')
janus.create_room(
room_id=janus_room_id,
description=f"Занятие: {lesson.title}",
publishers=max_participants,
secret=room_secret
)
# Сохраняем Janus room ID в router_id
video_room.router_id = str(janus_room_id)
video_room.save()
logger.info(f"Janus room created: Django={video_room.room_id}, Janus={janus_room_id}")
serializer = VideoRoomSerializer(video_room)
return Response(serializer.data, status=status.HTTP_201_CREATED)
except Exception as e:
logger.error(f"Error creating Janus room: {e}")
# Удаляем комнату из Django, если не удалось создать в Janus
if video_room:
video_room.delete()
return Response(
{'error': str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@action(detail=True, methods=['delete'], url_path='destroy-room', lookup_field='room_id')
def destroy_room(self, request, room_id=None):
"""
Удалить видеокомнату из Janus Gateway.
DELETE /api/video/janus/{room_id}/destroy-room/
"""
try:
video_room = VideoRoom.objects.select_related('lesson', 'mentor', 'client', 'client__user').get(room_id=room_id, sfu_type='janus')
except VideoRoom.DoesNotExist:
return Response(
{'error': 'Видеокомната не найдена'},
status=status.HTTP_404_NOT_FOUND
)
# Проверяем права
client_user = video_room.client.user if hasattr(video_room.client, 'user') else video_room.client
if request.user not in [video_room.mentor, client_user]:
return Response(
{'error': 'У вас нет доступа к этой комнате'},
status=status.HTTP_403_FORBIDDEN
)
try:
# Удаляем комнату из Janus
if video_room.router_id:
from django.conf import settings
room_secret = getattr(settings, 'JANUS_ROOM_SECRET', 'adminpwd')
with get_janus_client() as janus:
janus.destroy_room(int(video_room.router_id), secret=room_secret)
# Удаляем из Django
video_room.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
except Exception as e:
logger.error(f"Error destroying Janus room: {e}")
return Response(
{'error': str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@action(detail=True, methods=['get'], url_path='join', lookup_field='room_id')
def join(self, request, room_id=None):
"""
Получить информацию для присоединения к Janus комнате.
GET /api/video/janus/{room_id}/join/
Returns:
{
"room_id": "uuid",
"janus_room_id": 12345,
"http_url": "http://localhost:8088/janus",
"ws_url": "ws://localhost:8188",
"ice_servers": [...],
"participant_info": {...},
"room_info": {...}
}
"""
try:
video_room = VideoRoom.objects.select_related('lesson', 'mentor', 'client', 'client__user').get(room_id=room_id, sfu_type='janus')
except VideoRoom.DoesNotExist:
return Response(
{'error': 'Видеокомната не найдена'},
status=status.HTTP_404_NOT_FOUND
)
# Проверяем права
client_user = video_room.client.user if hasattr(video_room.client, 'user') else video_room.client
if request.user not in [video_room.mentor, client_user]:
return Response(
{'error': 'У вас нет доступа к этой комнате'},
status=status.HTTP_403_FORBIDDEN
)
# Получаем или создаем участника
participant, created = VideoParticipant.objects.get_or_create(
room=video_room,
user=request.user,
defaults={'is_connected': False}
)
# ICE серверы (STUN/TURN)
ice_servers = [
{'urls': 'stun:stun.l.google.com:19302'},
{'urls': 'stun:stun1.l.google.com:19302'}
]
# Janus room ID из router_id
janus_room_id = int(video_room.router_id) if video_room.router_id else None
return Response({
'room_id': str(video_room.room_id),
'janus_room_id': janus_room_id,
'http_url': settings.JANUS_HTTP_URL,
'ws_url': settings.JANUS_WS_URL,
'ice_servers': ice_servers,
'participant_info': {
'user_id': request.user.id,
'username': request.user.get_full_name(),
'role': 'publisher', # В Janus все участники могут быть publishers
},
'room_info': VideoRoomSerializer(video_room).data,
'sfu_type': 'janus'
})
@action(detail=False, methods=['get'], url_path='health')
def health(self, request):
"""
Проверить доступность Janus Gateway.
GET /api/video/janus/health/
"""
try:
import requests
response = requests.get(
f"{settings.JANUS_HTTP_URL.replace('/janus', '')}/janus/info",
timeout=2
)
response.raise_for_status()
data = response.json()
return Response({
'status': 'healthy',
'janus_info': data
})
except Exception as e:
logger.error(f"Janus health check failed: {e}")
return Response(
{
'status': 'unhealthy',
'error': str(e)
},
status=status.HTTP_503_SERVICE_UNAVAILABLE
)