Spaces:
Running
Running
from fastapi import FastAPI, WebSocket | |
from fastapi.staticfiles import StaticFiles | |
from fastapi.responses import HTMLResponse | |
from app.asr_worker import create_recognizer, stream_audio, finalize_stream | |
import json | |
app = FastAPI() | |
app.mount("/static", StaticFiles(directory="app/static"), name="static") | |
recognizer = create_recognizer() | |
async def root(): | |
with open("app/static/index.html") as f: | |
return HTMLResponse(f.read()) | |
async def websocket_endpoint(websocket: WebSocket): | |
print("[DEBUG main] ▶ Attempting to accept WebSocket…") | |
await websocket.accept() | |
print("[DEBUG main] ▶ WebSocket.accept() returned → client is connected!") | |
# Immediately create a new stream per client | |
stream = recognizer.create_stream() | |
orig_sr = 48000 # default fallback | |
print("[INFO main] WebSocket connection accepted; created a streaming context.") | |
try: | |
while True: | |
data = await websocket.receive() | |
kind = data.get("type") | |
# Debug: log any event we don't handle explicitly | |
if kind not in ("websocket.receive", "websocket.receive_bytes"): | |
print(f"[DEBUG main] Received control/frame: {data}") | |
# If client cleanly disconnected, finalize and break | |
if kind == "websocket.disconnect": | |
print(f"[INFO main] Client disconnected (code={data.get('code')}). Flushing final transcript...") | |
final = finalize_stream(stream, recognizer) | |
await websocket.send_json({"final": final}) | |
break | |
continue | |
# Handle text (config) frame | |
if kind == "websocket.receive" and "text" in data: | |
raw = data["text"] | |
try: | |
config_msg = json.loads(raw) | |
except Exception as e: | |
print(f"[ERROR main] JSON parse failed: {e}") | |
continue | |
if config_msg.get("type") == "config": | |
orig_sr = int(config_msg["sampleRate"]) | |
print(f"[INFO main] Set original sample rate to {orig_sr}") | |
continue | |
# If it’s a text payload but with bytes (some FastAPI versions put audio under 'text'!) | |
if kind == "websocket.receive" and "bytes" in data: | |
raw_audio = data["bytes"] | |
print(f"[INFO main] (text+bytes) Received audio chunk: {len(raw_audio)} bytes") | |
result, rms = stream_audio(raw_audio, stream, recognizer, orig_sr) | |
vol_to_send = min(rms * 20.0, 1.0) | |
print(f"[INFO main] Sending → partial='{result[:30]}…', volume={vol_to_send:.4f}") | |
await websocket.send_json({"partial": result, "volume": vol_to_send}) | |
continue | |
elif isinstance(data, dict) and data.get("type") == "websocket.receive_bytes": | |
raw_audio = data["bytes"] | |
print(f"[INFO main] Received audio chunk: {len(raw_audio)} bytes") | |
# This will also print its own debug info (see asr_worker.py) | |
result, rms = stream_audio(raw_audio, stream, recognizer, orig_sr) | |
vol_to_send = min(rms * 20.0, 1.0) | |
print(f"[INFO main] Sending → partial='{result[:30]}…', volume={vol_to_send:.4f}") | |
await websocket.send_json({ | |
"partial": result, | |
"volume": vol_to_send | |
}) | |
except Exception as e: | |
print(f"[ERROR main] Unexpected exception: {e}") | |
final = finalize_stream(stream, recognizer) | |
await websocket.send_json({"final": final}) | |
await websocket.close() | |
print("[INFO main] WebSocket closed, cleanup complete.") |