"""管理端:节目(播放清单)CRUD。""" from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import delete, select from sqlalchemy.orm import Session, selectinload from ..auth import get_current_user from ..database import get_db from ..models import Asset, Playlist, PlaylistItem, ScreenBinding from ..schemas import PlaylistCreate, PlaylistItemIn, PlaylistOut, PlaylistUpdate router = APIRouter(prefix="/api/playlists", tags=["playlists"], dependencies=[Depends(get_current_user)]) def _get_playlist_or_404(db: Session, playlist_id: int) -> Playlist: playlist = db.get(Playlist, playlist_id, options=[selectinload(Playlist.items).selectinload(PlaylistItem.asset)]) if not playlist: raise HTTPException(status_code=404, detail="节目不存在") return playlist def _replace_items(db: Session, playlist_id: int, items: list[PlaylistItemIn]): """全量替换节目项。""" asset_ids = {item.asset_id for item in items} if asset_ids: found = set(db.scalars(select(Asset.id).where(Asset.id.in_(asset_ids))).all()) missing = asset_ids - found if missing: raise HTTPException(status_code=400, detail=f"素材不存在: {sorted(missing)}") db.execute(delete(PlaylistItem).where(PlaylistItem.playlist_id == playlist_id)) for idx, item in enumerate(items): db.add( PlaylistItem( playlist_id=playlist_id, asset_id=item.asset_id, sort_order=idx, duration=item.duration, ) ) @router.get("", response_model=list[PlaylistOut]) def list_playlists(db: Session = Depends(get_db)): return db.scalars( select(Playlist) .options(selectinload(Playlist.items).selectinload(PlaylistItem.asset)) .order_by(Playlist.created_at.desc()) ).all() @router.get("/{playlist_id}", response_model=PlaylistOut) def get_playlist(playlist_id: int, db: Session = Depends(get_db)): return _get_playlist_or_404(db, playlist_id) @router.post("", response_model=PlaylistOut) def create_playlist(body: PlaylistCreate, db: Session = Depends(get_db)): playlist = Playlist(name=body.name, description=body.description) db.add(playlist) db.flush() _replace_items(db, playlist.id, body.items) db.commit() return _get_playlist_or_404(db, playlist.id) @router.put("/{playlist_id}", response_model=PlaylistOut) def update_playlist(playlist_id: int, body: PlaylistUpdate, db: Session = Depends(get_db)): playlist = _get_playlist_or_404(db, playlist_id) if body.name is not None: playlist.name = body.name if body.description is not None: playlist.description = body.description if body.items is not None: _replace_items(db, playlist.id, body.items) db.commit() return _get_playlist_or_404(db, playlist_id) @router.delete("/{playlist_id}") def delete_playlist(playlist_id: int, db: Session = Depends(get_db)): playlist = _get_playlist_or_404(db, playlist_id) has_active_binding = ( db.scalar(select(ScreenBinding.id).where(ScreenBinding.playlist_id == playlist_id, ScreenBinding.active == True)) # noqa: E712 is not None ) db.execute(delete(ScreenBinding).where(ScreenBinding.playlist_id == playlist_id)) db.delete(playlist) db.commit() return {"ok": True, "unbound_screens": has_active_binding}