This commit is contained in:
2025-12-09 20:27:25 +09:00
parent cc1ff597cc
commit 5916055130
3 changed files with 125 additions and 12 deletions

View File

@@ -77,6 +77,7 @@ server_stats = None
stats_lock = None
cleanup_task = None
templates = None
admin_frame_queues = None
# Администраторы
ADMINS = [
@@ -571,6 +572,7 @@ async def lifespan(app: FastAPI):
admin_websockets = {}
video_queues = {}
command_queues = {}
admin_frame_queues = {}
room_stats = {}
server_stats = {
"total_rooms": 0,
@@ -1072,6 +1074,17 @@ async def client_websocket_endpoint(websocket: WebSocket, room_id: str, password
print(f"[WebSocket Client] ⚠️ {client_id}: video queue is FULL, dropping frame")
else:
print(f"[WebSocket Client] ❌ {client_id}: video_queue not found!")
# Также пробуем переслать тот же байтовый кадр администраторам, если кто-то смотрит
try:
if admin_frame_queues is not None and client_id in admin_frame_queues:
aq = admin_frame_queues[client_id]
try:
aq.put_nowait(message)
except asyncio.QueueFull:
# Если очередь админа переполнена, пропускаем кадр
pass
except Exception:
pass
elif isinstance(message, str):
try:
@@ -1113,6 +1126,12 @@ async def client_websocket_endpoint(websocket: WebSocket, room_id: str, password
if video_processor:
video_processor.stop()
# Очищаем очередь админа, чтобы фреймы перестали отправляться
try:
if admin_frame_queues is not None and client_id in admin_frame_queues:
del admin_frame_queues[client_id]
except Exception:
pass
print(f"[WebSocket Client] ✓ VideoProcessor stopped for {client_id}")
print(f"[WebSocket Client] ===== END OF CONNECTION =====\n")
@@ -1191,26 +1210,60 @@ async def admin_websocket_endpoint(websocket: WebSocket, session_id: str):
async def _stream_client_to_admin(client_id: str, admin_session_id: str):
"""Потоковая передача видео от клиента к администратору"""
global admin_frame_queues, admin_websockets, clients, stats_lock
if client_id not in clients or admin_session_id not in admin_websockets:
return
try:
await admin_websockets[admin_session_id].send_text(json.dumps({
"type": "stream_started",
"client_id": client_id
}))
ws = admin_websockets[admin_session_id]
# Уведомляем админа о старте стрима
await ws.send_text(json.dumps({"type": "stream_started", "client_id": client_id}))
with stats_lock:
if client_id in clients:
client = clients[client_id]
await admin_websockets[admin_session_id].send_text(json.dumps({
await ws.send_text(json.dumps({
"type": "stream_info",
"message": f"Streaming from client {client_id}",
"quality": client["video_settings"]["quality"],
"ip": client["ip_address"],
"connected_at": client["connected_at"]
"quality": client.get("video_settings", {}).get("quality"),
"ip": client.get("ip_address"),
"connected_at": client.get("connected_at")
}))
# Подготовим очередь для пересылки байтов администраторам
if admin_frame_queues is None:
return
if client_id not in admin_frame_queues:
# Создаём очередь для этого клиента, если её нет
admin_frame_queues[client_id] = asyncio.Queue(maxsize=128)
aq = admin_frame_queues[client_id]
print(f"[WebSocket] Started streaming frames to admin {admin_session_id} for client {client_id}")
# Цикл чтения кадров из очереди и отправки их админу
# Выходим только если очередь удалена (клиент отключился) или произойдёт исключение
try:
while client_id in admin_frame_queues:
try:
# Пытаемся получить кадр с таймаутом, чтобы периодически проверять наличие очереди
frame_bytes = await asyncio.wait_for(aq.get(), timeout=2.0)
try:
await ws.send_bytes(frame_bytes)
except Exception as e:
print(f"[WebSocket] Error sending bytes to admin {admin_session_id}: {e}")
break
except asyncio.TimeoutError:
# Таймаут OK — просто продолжаем ждать
continue
except asyncio.CancelledError:
break
except Exception as e:
print(f"[WebSocket] Error in admin frame loop: {e}")
finally:
print(f"[WebSocket] Stopped streaming frames to admin {admin_session_id} for client {client_id}")
except Exception as e:
print(f"[WebSocket] Error streaming to admin: {e}")