225 lines
7.6 KiB
Python
225 lines
7.6 KiB
Python
"""
|
||
Python клиент для взаимодействия с sfu-server (ion-sfu).
|
||
"""
|
||
import logging
|
||
import requests
|
||
from typing import Optional, Dict, List, Any
|
||
from django.conf import settings
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class SFUClientError(Exception):
|
||
"""Ошибка при работе с SFU сервером."""
|
||
pass
|
||
|
||
|
||
class SFUClient:
|
||
"""
|
||
Клиент для взаимодействия с sfu-server через HTTP API.
|
||
|
||
Используется для управления видеокомнатами на стороне Django.
|
||
"""
|
||
|
||
def __init__(self, base_url: Optional[str] = None):
|
||
"""
|
||
Инициализация клиента.
|
||
|
||
Args:
|
||
base_url: Базовый URL sfu-server (по умолчанию из настроек)
|
||
"""
|
||
self.base_url = base_url or getattr(
|
||
settings,
|
||
'SFU_SERVER_URL',
|
||
'http://sfu-server:7001'
|
||
)
|
||
self.timeout = getattr(settings, 'SFU_CLIENT_TIMEOUT', 10)
|
||
|
||
def _request(
|
||
self,
|
||
method: str,
|
||
endpoint: str,
|
||
data: Optional[Dict] = None,
|
||
params: Optional[Dict] = None
|
||
) -> Dict[str, Any]:
|
||
"""
|
||
Выполнение HTTP запроса к sfu-server.
|
||
|
||
Args:
|
||
method: HTTP метод (GET, POST, DELETE)
|
||
endpoint: Endpoint API
|
||
data: Данные для отправки
|
||
params: Query параметры
|
||
|
||
Returns:
|
||
Ответ от сервера в виде словаря
|
||
|
||
Raises:
|
||
SFUClientError: При ошибке запроса
|
||
"""
|
||
url = f"{self.base_url}{endpoint}"
|
||
|
||
try:
|
||
response = requests.request(
|
||
method=method,
|
||
url=url,
|
||
json=data,
|
||
params=params,
|
||
timeout=self.timeout,
|
||
headers={'Content-Type': 'application/json'}
|
||
)
|
||
|
||
# Проверка статуса ответа
|
||
if not response.ok:
|
||
error_msg = f"SFU server error: {response.status_code}"
|
||
try:
|
||
error_data = response.json()
|
||
error_msg = error_data.get('error', error_msg)
|
||
except:
|
||
error_msg = f"{error_msg} - {response.text}"
|
||
|
||
logger.error(f"SFU request failed: {method} {url} - {error_msg}")
|
||
raise SFUClientError(error_msg)
|
||
|
||
# Парсинг ответа
|
||
try:
|
||
return response.json()
|
||
except ValueError:
|
||
return {'status': 'ok'}
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
error_msg = f"Failed to connect to SFU server: {str(e)}"
|
||
logger.error(f"SFU connection error: {error_msg}")
|
||
raise SFUClientError(error_msg)
|
||
|
||
def create_room(self, room_id: str) -> Dict[str, Any]:
|
||
"""
|
||
Создать видеокомнату в sfu-server.
|
||
|
||
Args:
|
||
room_id: Уникальный ID комнаты (обычно UUID из VideoRoom)
|
||
|
||
Returns:
|
||
Информация о созданной комнате
|
||
|
||
Example:
|
||
>>> client = SFUClient()
|
||
>>> result = client.create_room('123e4567-e89b-12d3-a456-426614174000')
|
||
>>> print(result)
|
||
{'room_id': '123e4567-e89b-12d3-a456-426614174000', 'status': 'created'}
|
||
"""
|
||
try:
|
||
response = self._request('POST', f'/api/rooms/{room_id}/create')
|
||
logger.info(f"Room {room_id} created in SFU server")
|
||
return response
|
||
except SFUClientError:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"Unexpected error creating room {room_id}: {e}")
|
||
raise SFUClientError(f"Failed to create room: {str(e)}")
|
||
|
||
def get_room(self, room_id: str) -> Dict[str, Any]:
|
||
"""
|
||
Получить информацию о видеокомнате.
|
||
|
||
Args:
|
||
room_id: ID комнаты
|
||
|
||
Returns:
|
||
Информация о комнате (room_id, peers_count, status)
|
||
|
||
Example:
|
||
>>> client = SFUClient()
|
||
>>> room = client.get_room('123e4567-e89b-12d3-a456-426614174000')
|
||
>>> print(room)
|
||
{'room_id': '...', 'peers_count': 2, 'status': 'active'}
|
||
"""
|
||
try:
|
||
response = self._request('GET', f'/api/rooms/{room_id}')
|
||
return response
|
||
except SFUClientError:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"Unexpected error getting room {room_id}: {e}")
|
||
raise SFUClientError(f"Failed to get room: {str(e)}")
|
||
|
||
def delete_room(self, room_id: str) -> Dict[str, Any]:
|
||
"""
|
||
Удалить видеокомнату из sfu-server.
|
||
|
||
Args:
|
||
room_id: ID комнаты
|
||
|
||
Returns:
|
||
Подтверждение удаления
|
||
|
||
Example:
|
||
>>> client = SFUClient()
|
||
>>> result = client.delete_room('123e4567-e89b-12d3-a456-426614174000')
|
||
>>> print(result)
|
||
{'room_id': '...', 'status': 'deleted'}
|
||
"""
|
||
try:
|
||
response = self._request('DELETE', f'/api/rooms/{room_id}')
|
||
logger.info(f"Room {room_id} deleted from SFU server")
|
||
return response
|
||
except SFUClientError:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"Unexpected error deleting room {room_id}: {e}")
|
||
raise SFUClientError(f"Failed to delete room: {str(e)}")
|
||
|
||
def list_rooms(self) -> List[Dict[str, Any]]:
|
||
"""
|
||
Получить список всех активных комнат.
|
||
|
||
Returns:
|
||
Список комнат с информацией о каждой
|
||
|
||
Example:
|
||
>>> client = SFUClient()
|
||
>>> rooms = client.list_rooms()
|
||
>>> print(rooms)
|
||
[{'room_id': '...', 'peers_count': 2, 'status': 'active'}, ...]
|
||
"""
|
||
try:
|
||
response = self._request('GET', '/api/rooms')
|
||
return response.get('rooms', [])
|
||
except SFUClientError:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"Unexpected error listing rooms: {e}")
|
||
raise SFUClientError(f"Failed to list rooms: {str(e)}")
|
||
|
||
def health_check(self) -> bool:
|
||
"""
|
||
Проверить доступность sfu-server.
|
||
|
||
Returns:
|
||
True если сервер доступен, False иначе
|
||
"""
|
||
try:
|
||
response = self._request('GET', '/health')
|
||
return response.get('status') == 'ok'
|
||
except Exception as e:
|
||
logger.warning(f"SFU health check failed: {e}")
|
||
return False
|
||
|
||
|
||
# Singleton экземпляр клиента
|
||
_sfu_client: Optional[SFUClient] = None
|
||
|
||
|
||
def get_sfu_client() -> SFUClient:
|
||
"""
|
||
Получить singleton экземпляр SFU клиента.
|
||
|
||
Returns:
|
||
Экземпляр SFUClient
|
||
"""
|
||
global _sfu_client
|
||
if _sfu_client is None:
|
||
_sfu_client = SFUClient()
|
||
return _sfu_client
|
||
|