from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException from fastapi.responses import HTMLResponse, FileResponse from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel import uvicorn import json import secrets from typing import Dict, Set from datetime import datetime app = FastAPI(title="ParthMeet") # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # In-memory storage meetings: Dict[str, dict] = {} connections: Dict[str, Dict[str, WebSocket]] = {} class MeetingCreate(BaseModel): host_name: str class MeetingJoin(BaseModel): meeting_id: str participant_name: str def generate_meeting_id() -> str: """Generate a unique 10-character meeting ID""" return secrets.token_urlsafe(8)[:10].replace('-', 'x').replace('_', 'y') @app.get("/", response_class=HTMLResponse) async def read_root(): """Serve the main HTML page""" return HTMLResponse(content=open("index.html").read()) @app.post("/api/meeting/create") async def create_meeting(data: MeetingCreate): """Create a new meeting""" meeting_id = generate_meeting_id() meetings[meeting_id] = { "id": meeting_id, "host": data.host_name, "participants": [], "created_at": datetime.now().isoformat(), "active": True } connections[meeting_id] = {} return { "success": True, "meeting_id": meeting_id, "meeting": meetings[meeting_id] } @app.get("/api/meeting/{meeting_id}") async def get_meeting(meeting_id: str): """Get meeting details""" if meeting_id not in meetings: raise HTTPException(status_code=404, detail="Meeting not found") return { "success": True, "meeting": meetings[meeting_id] } @app.post("/api/meeting/end/{meeting_id}") async def end_meeting(meeting_id: str): """End a meeting""" if meeting_id not in meetings: raise HTTPException(status_code=404, detail="Meeting not found") meetings[meeting_id]["active"] = False # Notify all participants if meeting_id in connections: for ws in connections[meeting_id].values(): try: await ws.send_json({ "type": "meeting_ended", "data": {"message": "The meeting has been ended by the host"} }) except: pass connections[meeting_id].clear() return {"success": True, "message": "Meeting ended"} @app.websocket("/ws/{meeting_id}/{participant_id}") async def websocket_endpoint(websocket: WebSocket, meeting_id: str, participant_id: str): """WebSocket endpoint for real-time communication""" await websocket.accept() if meeting_id not in meetings: await websocket.send_json({ "type": "error", "data": {"message": "Meeting not found"} }) await websocket.close() return if not meetings[meeting_id]["active"]: await websocket.send_json({ "type": "error", "data": {"message": "Meeting has ended"} }) await websocket.close() return # Add connection if meeting_id not in connections: connections[meeting_id] = {} connections[meeting_id][participant_id] = websocket # Notify others about new participant for pid, ws in connections[meeting_id].items(): if pid != participant_id: try: await ws.send_json({ "type": "participant_joined", "data": {"participant_id": participant_id} }) except: pass try: while True: data = await websocket.receive_text() message = json.loads(data) # Broadcast to specific participant or all if message.get("target"): target_id = message["target"] if target_id in connections[meeting_id]: try: await connections[meeting_id][target_id].send_json(message) except: pass else: # Broadcast to all except sender for pid, ws in connections[meeting_id].items(): if pid != participant_id: try: await ws.send_json(message) except: pass except WebSocketDisconnect: # Remove connection if meeting_id in connections and participant_id in connections[meeting_id]: del connections[meeting_id][participant_id] # Notify others about participant leaving if meeting_id in connections: for ws in connections[meeting_id].values(): try: await ws.send_json({ "type": "participant_left", "data": {"participant_id": participant_id} }) except: pass if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=7860)