"""Admin management routes.""" import os from fastapi import APIRouter, HTTPException, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func, or_ from app.database import get_db from app.models import Video, DownloadLog from app.schemas import VideoInfo, VideoListResponse, StorageStats, DownloadLogInfo, DownloadLogListResponse from app.auth import get_current_user router = APIRouter(prefix="/api/admin", tags=["admin"]) def human_size(size_bytes: int) -> str: for unit in ["B", "KB", "MB", "GB", "TB"]: if size_bytes < 1024: return f"{size_bytes:.1f} {unit}" size_bytes /= 1024 return f"{size_bytes:.1f} PB" @router.get("/videos", response_model=VideoListResponse) async def list_videos( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), search: str = Query("", max_length=200), status: str = Query("", max_length=20), user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): query = select(Video).order_by(Video.created_at.desc()) count_query = select(func.count(Video.id)) if search: flt = or_(Video.title.contains(search), Video.url.contains(search)) query = query.where(flt) count_query = count_query.where(flt) if status: query = query.where(Video.status == status) count_query = count_query.where(Video.status == status) total = (await db.execute(count_query)).scalar() or 0 videos = (await db.execute(query.offset((page - 1) * page_size).limit(page_size))).scalars().all() return VideoListResponse( videos=[VideoInfo.model_validate(v) for v in videos], total=total, page=page, page_size=page_size, ) @router.delete("/videos/{video_id}") async def delete_video(video_id: int, user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db)): video = (await db.execute(select(Video).where(Video.id == video_id))).scalar_one_or_none() if not video: raise HTTPException(status_code=404, detail="Video not found") # Delete file from disk if video.file_path and os.path.exists(video.file_path): os.remove(video.file_path) await db.delete(video) await db.commit() return {"ok": True} @router.get("/stats", response_model=StorageStats) async def storage_stats(user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db)): total = (await db.execute(select(func.count(Video.id)).where(Video.status == "done"))).scalar() or 0 total_size = (await db.execute(select(func.sum(Video.file_size)).where(Video.status == "done"))).scalar() or 0 return StorageStats(total_videos=total, total_size=total_size, total_size_human=human_size(total_size)) @router.get("/download-logs", response_model=DownloadLogListResponse) async def download_logs( page: int = Query(1, ge=1), page_size: int = Query(50, ge=1, le=200), video_id: int = Query(None), user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): from sqlalchemy.orm import joinedload query = ( select(DownloadLog) .options(joinedload(DownloadLog.video)) .order_by(DownloadLog.downloaded_at.desc()) ) count_query = select(func.count(DownloadLog.id)) if video_id is not None: query = query.where(DownloadLog.video_id == video_id) count_query = count_query.where(DownloadLog.video_id == video_id) total = (await db.execute(count_query)).scalar() or 0 logs = (await db.execute(query.offset((page - 1) * page_size).limit(page_size))).scalars().all() items = [] for l in logs: d = DownloadLogInfo.model_validate(l) if l.video: d.video_title = l.video.title or "" d.video_platform = l.video.platform or "" items.append(d) return DownloadLogListResponse(logs=items, total=total, page=page, page_size=page_size)