Refactor Google Meet Transcripts Extension for Local Use

- 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.
This commit is contained in:
vahidaskari
2026-06-12 00:31:32 +03:30
parent 602dcb7430
commit 7bc34c79ed
35 changed files with 1069 additions and 840 deletions
+79
View File
@@ -0,0 +1,79 @@
"""
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👋 خاموش شد.")