76 lines
2.3 KiB
Python
76 lines
2.3 KiB
Python
"""Bonvoice media — raw frame relay only (no PCM processing on the API)."""
|
|
|
|
import asyncio
|
|
import json
|
|
import logging
|
|
from collections.abc import Awaitable, Callable
|
|
from typing import Any
|
|
from uuid import UUID
|
|
|
|
from fastapi import WebSocket, WebSocketDisconnect
|
|
|
|
from .presence import console_presence
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
OnEndCallback = Callable[[], Awaitable[None]]
|
|
|
|
|
|
def parse_event(raw: str) -> dict[str, Any] | None:
|
|
try:
|
|
payload = json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
return None
|
|
return payload if isinstance(payload, dict) else None
|
|
|
|
|
|
def extract_start_metadata(message: dict[str, Any]) -> dict[str, Any]:
|
|
data = message.get("data") or message.get("start") or {}
|
|
stream_id = data.get("stream_id") if data.get("stream_id") is not None else message.get("stream_id")
|
|
return {
|
|
"stream_id": stream_id,
|
|
"stream_id_str": str(stream_id) if stream_id is not None else "",
|
|
"call_id": str(data.get("call_id") or message.get("call_id") or ""),
|
|
"from": str(data.get("from") or message.get("from") or ""),
|
|
"to": str(data.get("to") or message.get("to") or ""),
|
|
}
|
|
|
|
|
|
async def run_console_raw_relay(
|
|
provider: WebSocket,
|
|
user_id: UUID,
|
|
console_ws: WebSocket,
|
|
*,
|
|
initial_frames: list[str] | None = None,
|
|
on_end: OnEndCallback | None = None,
|
|
) -> None:
|
|
"""Console test bridge — forward raw JSON frames both ways (no server audio processing)."""
|
|
stop = asyncio.Event()
|
|
relay_queue = await console_presence.start_relay(user_id)
|
|
|
|
for raw in initial_frames or []:
|
|
await console_ws.send_text(raw)
|
|
|
|
async def console_to_provider() -> None:
|
|
try:
|
|
while not stop.is_set():
|
|
raw = await relay_queue.get()
|
|
await provider.send_text(raw)
|
|
except (WebSocketDisconnect, RuntimeError):
|
|
stop.set()
|
|
|
|
async def provider_to_console() -> None:
|
|
try:
|
|
while not stop.is_set():
|
|
raw = await provider.receive_text()
|
|
await console_ws.send_text(raw)
|
|
except (WebSocketDisconnect, RuntimeError):
|
|
stop.set()
|
|
|
|
try:
|
|
await asyncio.gather(console_to_provider(), provider_to_console())
|
|
finally:
|
|
stop.set()
|
|
await console_presence.stop_relay(user_id)
|
|
if on_end:
|
|
await on_end()
|