init
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
"""WebSocket: широковещательные live-обновления статуса камер."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
|
||||
|
||||
from ..runtime import runtime
|
||||
from ..auth import COOKIE_NAME, auth_manager
|
||||
|
||||
log = logging.getLogger("nvr.ws")
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class ConnectionManager:
|
||||
def __init__(self) -> None:
|
||||
self._clients: set[WebSocket] = set()
|
||||
self._loop: asyncio.AbstractEventLoop | None = None
|
||||
|
||||
def bind_loop(self, loop: asyncio.AbstractEventLoop) -> None:
|
||||
self._loop = loop
|
||||
|
||||
async def connect(self, ws: WebSocket) -> None:
|
||||
await ws.accept()
|
||||
self._clients.add(ws)
|
||||
|
||||
def disconnect(self, ws: WebSocket) -> None:
|
||||
self._clients.discard(ws)
|
||||
|
||||
async def broadcast(self, message: dict) -> None:
|
||||
data = json.dumps(message, ensure_ascii=False)
|
||||
dead: list[WebSocket] = []
|
||||
for ws in list(self._clients):
|
||||
try:
|
||||
await ws.send_text(data)
|
||||
except Exception: # noqa: BLE001
|
||||
dead.append(ws)
|
||||
for ws in dead:
|
||||
self._clients.discard(ws)
|
||||
|
||||
def broadcast_threadsafe(self, message: dict) -> None:
|
||||
"""Вызов из синхронного callback рекордера."""
|
||||
if self._loop and self._loop.is_running():
|
||||
asyncio.run_coroutine_threadsafe(self.broadcast(message), self._loop)
|
||||
|
||||
|
||||
@router.websocket("/api/ws")
|
||||
async def ws_endpoint(ws: WebSocket) -> None:
|
||||
if not auth_manager.validate(ws.cookies.get(COOKIE_NAME)):
|
||||
await ws.close(code=1008) # policy violation
|
||||
return
|
||||
await runtime.ws.connect(ws)
|
||||
# начальный снапшот статусов
|
||||
await ws.send_text(json.dumps({
|
||||
"type": "status",
|
||||
"cameras": [s.to_dict() for s in runtime.supervisor.statuses()],
|
||||
}, ensure_ascii=False))
|
||||
try:
|
||||
while True:
|
||||
await ws.receive_text() # клиент ничего не шлёт; держим соединение
|
||||
except WebSocketDisconnect:
|
||||
runtime.ws.disconnect(ws)
|
||||
Reference in New Issue
Block a user