mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-09-06 16:17:06 +08:00
feat: add video room feature with API, database model, and UI integration
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Request, WebSocket, WebSocketDisconnect
|
||||
|
||||
from api.response import success
|
||||
from domain.audit import AuditAction, audit
|
||||
from domain.auth import User, get_current_active_user
|
||||
from domain.permission import require_path_permission
|
||||
from domain.permission.types import PathAction
|
||||
from models.database import UserAccount
|
||||
from .service import VideoRoomService
|
||||
from .types import VideoRoomCreate, VideoRoomInfo
|
||||
from .ws import video_room_ws_manager
|
||||
|
||||
router = APIRouter(prefix="/api/video-rooms", tags=["Video Rooms"])
|
||||
|
||||
|
||||
@router.post("", response_model=VideoRoomInfo)
|
||||
@audit(action=AuditAction.SHARE, description="创建视频房", body_fields=["name", "path"])
|
||||
@require_path_permission(PathAction.SHARE, "payload.path")
|
||||
async def create_video_room(
|
||||
request: Request,
|
||||
payload: VideoRoomCreate,
|
||||
current_user: Annotated[User, Depends(get_current_active_user)],
|
||||
):
|
||||
user_account = await UserAccount.get(id=current_user.id)
|
||||
room = await VideoRoomService.create_room(
|
||||
user=user_account,
|
||||
name=payload.name,
|
||||
path=payload.path,
|
||||
)
|
||||
return VideoRoomInfo.from_orm(room, VideoRoomService.get_effective_state(room))
|
||||
|
||||
|
||||
@router.get("/{token}", response_model=VideoRoomInfo)
|
||||
@audit(action=AuditAction.SHARE, description="获取视频房信息")
|
||||
async def get_video_room(request: Request, token: str):
|
||||
room = await VideoRoomService.get_room_by_token(token)
|
||||
return VideoRoomInfo.from_orm(room, VideoRoomService.get_effective_state(room))
|
||||
|
||||
|
||||
@router.get("/{token}/stream")
|
||||
@audit(action=AuditAction.DOWNLOAD, description="播放视频房文件")
|
||||
async def stream_video_room(token: str, request: Request):
|
||||
return await VideoRoomService.stream_room_file(token, request.headers.get("Range"))
|
||||
|
||||
|
||||
@router.websocket("/{token}/ws")
|
||||
async def video_room_ws(websocket: WebSocket, token: str):
|
||||
room = await VideoRoomService.get_room_by_token(token)
|
||||
await video_room_ws_manager.connect(token, websocket)
|
||||
try:
|
||||
state = VideoRoomService.get_effective_state(room)
|
||||
await websocket.send_json({"type": "state", "state": state.model_dump()})
|
||||
while True:
|
||||
data = await websocket.receive_json()
|
||||
if data.get("type") != "state":
|
||||
continue
|
||||
state = await VideoRoomService.update_state(
|
||||
room,
|
||||
current_time=float(data.get("current_time") or 0),
|
||||
paused=bool(data.get("paused")),
|
||||
)
|
||||
await video_room_ws_manager.broadcast(
|
||||
token,
|
||||
{"type": "state", "state": state.model_dump()},
|
||||
exclude=websocket,
|
||||
)
|
||||
except WebSocketDisconnect:
|
||||
pass
|
||||
finally:
|
||||
video_room_ws_manager.disconnect(token, websocket)
|
||||
Reference in New Issue
Block a user