from fastapi import APIRouter, Depends, HTTPException from starlette.websockets import WebSocket, WebSocketDisconnect from sqlalchemy.orm import Session from typing import Optional from pydantic import BaseModel from ..db.database import get_db from ..models.shareplay import SharePlayRoom, SharePlayCue as SharePlayCueModel from ..schemas.shareplay import SharePlayRoomResponse, SharePlayCommand, SharePlayCueResponse from ..services.shareplay import SharePlayManager class RoomIdRequest(BaseModel): room_id: str class ControlRequest(BaseModel): room_id: str type: str payload: Optional[dict] = None router = APIRouter(prefix="/api/shareplay", tags=["shareplay"]) manager = SharePlayManager() @router.post("/create") def create_room(db: Session = Depends(get_db)): return manager.create_room(db, creator="user") @router.post("/join") def join_room(data: RoomIdRequest, db: Session = Depends(get_db)): result = manager.join_room(db, data.room_id) if not result: raise HTTPException(status_code=404, detail="Room not found") return result @router.post("/leave") def leave_room(data: RoomIdRequest, db: Session = Depends(get_db)): success = manager.leave_room(db, data.room_id) if not success: raise HTTPException(status_code=404, detail="Room not found") return {"message": "Left room"} @router.get("/cue") def get_cue(room_id: str, db: Session = Depends(get_db)): items = manager.get_cue(db, room_id) return SharePlayCueResponse(items=items, next_song=None) @router.post("/cue") def add_to_cue(room_id: str, song_id: str, db: Session = Depends(get_db)): success = manager.add_to_cue(db, room_id, song_id) if not success: raise HTTPException(status_code=400, detail="Failed to add to cue") return {"message": "Added to cue"} @router.post("/control") def send_control(data: ControlRequest, db: Session = Depends(get_db)): state = manager.get_state(data.room_id) if not state: raise HTTPException(status_code=404, detail="Room not found") if data.type == "play": manager.update_state(data.room_id, is_playing=True) elif data.type == "pause": manager.update_state(data.room_id, is_playing=False) elif data.type == "seek" and data.payload: manager.update_state(data.room_id, position=data.payload.get("position", 0)) elif data.type == "shuffle": current = manager.get_state(data.room_id) manager.update_state(data.room_id, shuffle=not current.get("shuffle", False)) return {"message": f"Command '{data.type}' sent"} # WebSocket endpoint @router.websocket("/ws/{room_id}") async def websocket_endpoint(websocket: WebSocket, room_id: str): await websocket.accept() state = manager.get_state(room_id) if state: await websocket.send_json({"type": "state", "data": state}) try: while True: data = await websocket.receive_text() import json message = json.loads(data) if message.get("type") == "control": cmd = message.get("payload", {}) state = manager.get_state(room_id) if state and cmd.get("type") == "play": manager.update_state(room_id, is_playing=True) elif state and cmd.get("type") == "pause": manager.update_state(room_id, is_playing=False) await websocket.send_json({"type": "ack", "data": message}) except WebSocketDisconnect: pass