Files
enigFM/backend/app/routers/rooms.py

249 lines
7.4 KiB
Python
Raw Normal View History

2025-12-12 13:30:09 +03:00
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from sqlalchemy.orm import selectinload
from ..database import get_db
from ..models.user import User
from ..models.room import Room, RoomParticipant
from ..models.track import RoomQueue
from ..schemas.room import RoomCreate, RoomResponse, RoomDetailResponse, QueueAdd
from ..schemas.track import TrackResponse
from ..schemas.user import UserResponse
from ..services.auth import get_current_user
from ..services.sync import manager
from ..config import get_settings
settings = get_settings()
router = APIRouter(prefix="/api/rooms", tags=["rooms"])
@router.get("", response_model=list[RoomResponse])
async def get_rooms(db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(Room, func.count(RoomParticipant.user_id).label("participants_count"))
.outerjoin(RoomParticipant)
.group_by(Room.id)
.order_by(Room.created_at.desc())
)
rooms = []
for room, count in result.all():
room_dict = {
"id": room.id,
"name": room.name,
"owner_id": room.owner_id,
"current_track_id": room.current_track_id,
"playback_position": room.playback_position,
"is_playing": room.is_playing,
"created_at": room.created_at,
"participants_count": count,
}
rooms.append(RoomResponse(**room_dict))
return rooms
@router.post("", response_model=RoomResponse)
async def create_room(
room_data: RoomCreate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
room = Room(name=room_data.name, owner_id=current_user.id)
db.add(room)
await db.flush()
return RoomResponse(
id=room.id,
name=room.name,
owner_id=room.owner_id,
current_track_id=room.current_track_id,
playback_position=room.playback_position,
is_playing=room.is_playing,
created_at=room.created_at,
participants_count=0,
)
@router.get("/{room_id}", response_model=RoomDetailResponse)
async def get_room(room_id: UUID, db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(Room)
.options(
selectinload(Room.owner),
selectinload(Room.current_track),
selectinload(Room.participants).selectinload(RoomParticipant.user),
)
.where(Room.id == room_id)
)
room = result.scalar_one_or_none()
if not room:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Room not found")
return RoomDetailResponse(
id=room.id,
name=room.name,
owner=UserResponse.model_validate(room.owner),
current_track=TrackResponse.model_validate(room.current_track) if room.current_track else None,
playback_position=room.playback_position,
is_playing=room.is_playing,
created_at=room.created_at,
participants=[UserResponse.model_validate(p.user) for p in room.participants],
)
@router.delete("/{room_id}")
async def delete_room(
room_id: UUID,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
result = await db.execute(select(Room).where(Room.id == room_id))
room = result.scalar_one_or_none()
if not room:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Room not found")
if room.owner_id != current_user.id:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not room owner")
await db.delete(room)
return {"status": "deleted"}
@router.post("/{room_id}/join")
async def join_room(
room_id: UUID,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
result = await db.execute(select(Room).where(Room.id == room_id))
room = result.scalar_one_or_none()
if not room:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Room not found")
# Check participant limit
result = await db.execute(
select(func.count(RoomParticipant.user_id)).where(RoomParticipant.room_id == room_id)
)
count = result.scalar()
if count >= settings.max_room_participants:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Room is full")
# Check if already joined
result = await db.execute(
select(RoomParticipant).where(
RoomParticipant.room_id == room_id,
RoomParticipant.user_id == current_user.id,
)
)
if result.scalar_one_or_none():
return {"status": "already joined"}
participant = RoomParticipant(room_id=room_id, user_id=current_user.id)
db.add(participant)
# Notify others
await manager.broadcast_to_room(
room_id,
{"type": "user_joined", "user": {"id": str(current_user.id), "username": current_user.username}},
)
return {"status": "joined"}
@router.post("/{room_id}/leave")
async def leave_room(
room_id: UUID,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
result = await db.execute(
select(RoomParticipant).where(
RoomParticipant.room_id == room_id,
RoomParticipant.user_id == current_user.id,
)
)
participant = result.scalar_one_or_none()
if participant:
await db.delete(participant)
# Notify others
await manager.broadcast_to_room(
room_id,
{"type": "user_left", "user_id": str(current_user.id)},
)
return {"status": "left"}
@router.get("/{room_id}/queue", response_model=list[TrackResponse])
async def get_queue(room_id: UUID, db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(RoomQueue)
.options(selectinload(RoomQueue.track))
.where(RoomQueue.room_id == room_id)
.order_by(RoomQueue.position)
)
queue_items = result.scalars().all()
return [TrackResponse.model_validate(item.track) for item in queue_items]
@router.post("/{room_id}/queue")
async def add_to_queue(
room_id: UUID,
data: QueueAdd,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
# Get max position
result = await db.execute(
select(func.max(RoomQueue.position)).where(RoomQueue.room_id == room_id)
)
max_pos = result.scalar() or 0
queue_item = RoomQueue(
room_id=room_id,
track_id=data.track_id,
position=max_pos + 1,
added_by=current_user.id,
)
db.add(queue_item)
await db.flush()
# Notify others
await manager.broadcast_to_room(
room_id,
{"type": "queue_updated"},
)
return {"status": "added"}
@router.delete("/{room_id}/queue/{track_id}")
async def remove_from_queue(
room_id: UUID,
track_id: UUID,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
result = await db.execute(
select(RoomQueue).where(
RoomQueue.room_id == room_id,
RoomQueue.track_id == track_id,
)
)
queue_item = result.scalar_one_or_none()
if queue_item:
await db.delete(queue_item)
# Notify others
await manager.broadcast_to_room(
room_id,
{"type": "queue_updated"},
)
return {"status": "removed"}