Initial commit: XDL Twitter/X video downloader

This commit is contained in:
mini
2026-02-18 17:15:12 +08:00
commit 7fdd181728
32 changed files with 1230 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
"""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
from app.schemas import VideoInfo, VideoListResponse, StorageStats
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))