"""管理端:素材上传与管理。""" import os import uuid from fastapi import APIRouter, Depends, File, HTTPException, UploadFile from sqlalchemy import func, select from sqlalchemy.orm import Session from ..auth import get_current_user from ..config import get_server_config from ..database import get_db from ..models import Asset, PlaylistItem from ..schemas import AssetOut router = APIRouter(prefix="/api/assets", tags=["assets"], dependencies=[Depends(get_current_user)]) _EXT_MAP = { "image/jpeg": ".jpg", "image/png": ".png", "image/gif": ".gif", "image/webp": ".webp", "video/mp4": ".mp4", "video/webm": ".webm", "video/x-matroska": ".mkv", } @router.get("", response_model=list[AssetOut]) def list_assets(db: Session = Depends(get_db)): return db.scalars(select(Asset).order_by(Asset.created_at.desc())).all() @router.post("", response_model=AssetOut) async def upload_asset(file: UploadFile = File(...), db: Session = Depends(get_db)): """上传图片/视频素材。""" cfg = get_server_config() content_type = file.content_type or "" ext = _EXT_MAP.get(content_type) if not ext: raise HTTPException(status_code=400, detail=f"不支持的素材类型: {content_type or '未知'}") max_bytes = cfg.get("max_upload_mb", 500) * 1024 * 1024 # 流式读取并限制大小 data = b"" while chunk := await file.read(1024 * 1024): data += chunk if len(data) > max_bytes: raise HTTPException(status_code=413, detail=f"文件超过大小限制 {cfg['max_upload_mb']}MB") filename = f"{uuid.uuid4().hex}{ext}" upload_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), cfg.get("upload_dir", "./uploads"))) os.makedirs(upload_dir, exist_ok=True) dest = os.path.join(upload_dir, filename) with open(dest, "wb") as f: f.write(data) is_video = content_type.startswith("video/") asset = Asset( name=file.filename or filename, type="video" if is_video else "image", url=f"/media/{filename}", size=len(data), duration=0 if is_video else 10, # 图片默认展示 10 秒 ) db.add(asset) db.commit() db.refresh(asset) return asset @router.delete("/{asset_id}") def delete_asset(asset_id: int, db: Session = Depends(get_db)): asset = db.get(Asset, asset_id) if not asset: raise HTTPException(status_code=404, detail="素材不存在") used = db.scalar(select(func.count(PlaylistItem.id)).where(PlaylistItem.asset_id == asset_id)) if used: raise HTTPException( status_code=409, detail=f"该素材正被 {used} 个节目引用,请先从节目中移除再删除", ) cfg = get_server_config() upload_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), cfg.get("upload_dir", "./uploads"))) file_path = os.path.join(upload_dir, os.path.basename(asset.url)) if os.path.exists(file_path): os.remove(file_path) db.delete(asset) db.commit() return {"ok": True}