""" 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