7bc34c79ed
- Removed all cloud-related functionalities, including login prompts and token handling. - Disabled Laxis cloud features, ensuring no data is sent to external servers. - Updated manifest to reflect the new local-only functionality. - Added a new Python server to handle transcripts locally, including WebSocket support. - Implemented storage management for transcripts, including deduplication and file writing. - Created a smoke test for the WebSocket server to simulate transcript updates. - Updated README with setup instructions and usage details for the new local server.
80 lines
2.9 KiB
Python
80 lines
2.9 KiB
Python
"""
|
|
ws_server.py — سرور WebSocket که caption ها را از افزونهی Chrome میگیرد.
|
|
|
|
افزونه (service worker → bridge.js) هر caption را با این پیام میفرستد:
|
|
{type:"TRANSCRIPT_UPDATE", sessionId, speaker, text, startedAt, endedAt}
|
|
و heartbeat هم:
|
|
{type:"PING", ts}
|
|
|
|
این فایل را میتوان مستقل اجرا کرد (فقط WebSocket، برای تست با _smoke_test.py):
|
|
python ws_server.py
|
|
ولی در حالت عادی mcp_server.py همین را بهعنوان background task بالا میآورد.
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
from datetime import datetime
|
|
|
|
import websockets
|
|
|
|
from storage import upsert_segment, log
|
|
|
|
HOST = "127.0.0.1"
|
|
PORT = 8765
|
|
|
|
|
|
async def handle_client(websocket):
|
|
peer = getattr(websocket, "remote_address", "?")
|
|
log(f"🔌 افزونه وصل شد: {peer}")
|
|
try:
|
|
async for raw in websocket:
|
|
try:
|
|
data = json.loads(raw)
|
|
except (json.JSONDecodeError, TypeError):
|
|
log(f"⚠️ پیام نامعتبر: {raw!r}")
|
|
continue
|
|
|
|
msg_type = data.get("type")
|
|
|
|
if msg_type == "PING":
|
|
await websocket.send(json.dumps({"type": "PONG", "ts": data.get("ts")}))
|
|
continue
|
|
|
|
if msg_type == "TRANSCRIPT_UPDATE":
|
|
text = (data.get("text") or "").strip()
|
|
if not text:
|
|
continue
|
|
session_id = data.get("sessionId", "default")
|
|
speaker = data.get("speaker", "Unknown")
|
|
upsert_segment(session_id, speaker, text,
|
|
data.get("startedAt"), data.get("endedAt"))
|
|
stamp = datetime.now().strftime("%H:%M:%S")
|
|
log(f"[{stamp}] ({session_id[:8]}) {speaker}: {text[:60]}")
|
|
await websocket.send(json.dumps({"type": "ACK", "ok": True}))
|
|
continue
|
|
|
|
log(f"❓ نوع ناشناخته: {msg_type}")
|
|
except websockets.ConnectionClosed:
|
|
pass
|
|
finally:
|
|
log(f"❌ افزونه قطع شد: {peer}")
|
|
|
|
|
|
async def run_forever():
|
|
"""WebSocket را بالا میآورد. اگر پورت اشغال بود (نمونهی دیگری بالاست) graceful رد میشود."""
|
|
try:
|
|
async with websockets.serve(handle_client, HOST, PORT):
|
|
log(f"🚀 WebSocket روی ws://{HOST}:{PORT} گوش میدهد …")
|
|
await asyncio.Future()
|
|
except OSError as e:
|
|
log(f"⚠️ نتوانست ws://{HOST}:{PORT} را بگیرد ({e}).")
|
|
log(" این نمونه فقط MCP را سرو میکند و transcript ها را از روی دیسک میخواند.")
|
|
await asyncio.Future()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
asyncio.run(run_forever())
|
|
except KeyboardInterrupt:
|
|
log("\n👋 خاموش شد.")
|