mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-06-20 23:03:53 +08:00
98 lines
3.7 KiB
Python
98 lines
3.7 KiB
Python
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Depends, Request, WebSocket, WebSocketDisconnect
|
|
|
|
from api.response import success
|
|
from domain.auth import User, get_current_active_user
|
|
from domain.video_room.service import VideoRoomService
|
|
from domain.video_room.types import PlaybackEvent, VideoRoomCreate, VideoRoomInfo
|
|
|
|
router = APIRouter(prefix="/api/video-rooms", tags=["Video Rooms"])
|
|
public_router = APIRouter(prefix="/api/watch", tags=["Video Rooms - Public"])
|
|
|
|
|
|
@router.post("", response_model=VideoRoomInfo)
|
|
async def create_video_room(
|
|
request: Request,
|
|
payload: VideoRoomCreate,
|
|
current_user: Annotated[User, Depends(get_current_active_user)],
|
|
):
|
|
room = await VideoRoomService.create_room(
|
|
user_id=current_user.id,
|
|
path=payload.path,
|
|
name=payload.name,
|
|
expires_in_days=payload.expires_in_days,
|
|
control_mode=payload.control_mode,
|
|
)
|
|
return VideoRoomInfo(
|
|
id=room.id,
|
|
name=room.name,
|
|
token=room.token,
|
|
path=room.path,
|
|
control_mode=room.control_mode,
|
|
created_at=room.created_at.isoformat(),
|
|
expires_at=room.expires_at.isoformat() if room.expires_at else None,
|
|
)
|
|
|
|
|
|
@public_router.get("/{token}")
|
|
async def get_watch_room(request: Request, token: str):
|
|
room = await VideoRoomService.get_room_by_token(token)
|
|
state = await VideoRoomService.get_state(room.id)
|
|
return success({"room": VideoRoomInfo(
|
|
id=room.id,
|
|
name=room.name,
|
|
token=room.token,
|
|
path=room.path,
|
|
control_mode=room.control_mode,
|
|
created_at=room.created_at.isoformat(),
|
|
expires_at=room.expires_at.isoformat() if room.expires_at else None,
|
|
).model_dump(), "playback": state})
|
|
|
|
|
|
@public_router.websocket("/{token}/ws")
|
|
async def watch_room_ws(websocket: WebSocket, token: str):
|
|
room = await VideoRoomService.get_room_by_token(token)
|
|
actor = websocket.query_params.get("actor") or "guest"
|
|
await VideoRoomService.ws_connect(room.id, websocket)
|
|
try:
|
|
state = await VideoRoomService.get_state(room.id)
|
|
await websocket.send_json({"type": "snapshot", "playback": state})
|
|
while True:
|
|
msg = await websocket.receive_json()
|
|
if msg.get("type") == "ping":
|
|
await websocket.send_json({"type": "pong"})
|
|
continue
|
|
event_type = msg.get("event")
|
|
if event_type not in {"play", "pause", "seek", "rate"}:
|
|
continue
|
|
position_ms = int(msg.get("position_ms") or 0)
|
|
playback_rate = float(msg.get("playback_rate") or 1.0)
|
|
state = await VideoRoomService.apply_event(
|
|
room=room,
|
|
actor=actor,
|
|
event_type=event_type,
|
|
position_ms=position_ms,
|
|
playback_rate=playback_rate,
|
|
)
|
|
await VideoRoomService.ws_broadcast(room.id, {"type": "playback", "event": event_type, "playback": state, "actor": actor})
|
|
except WebSocketDisconnect:
|
|
pass
|
|
finally:
|
|
await VideoRoomService.ws_disconnect(room.id, websocket)
|
|
|
|
|
|
@public_router.post("/{token}/events")
|
|
async def push_watch_event(request: Request, token: str, payload: PlaybackEvent):
|
|
room = await VideoRoomService.get_room_by_token(token)
|
|
actor = request.headers.get("X-Watch-Actor", "guest")
|
|
state = await VideoRoomService.apply_event(
|
|
room=room,
|
|
actor=actor,
|
|
event_type=payload.type,
|
|
position_ms=payload.position_ms,
|
|
playback_rate=payload.playback_rate,
|
|
)
|
|
await VideoRoomService.ws_broadcast(room.id, {"type": "playback", "event": payload.type, "playback": state, "actor": actor})
|
|
return success({"playback": state})
|