- таблица sections (дерево через parent_id), у видео section_id
- API: CRUD /sections, фильтры /videos (поиск, раздел с потомками, метод),
GET /videos/{id}/text, PATCH раздела, DELETE вместе с файлами
- pipeline: keep_video=false удаляет mp4 после расшифровки, section_id
- UI: панель разделов с вложенностью и счётчиками, поиск, фильтр по методу,
диалоги полного текста/выжимки, привязка к разделу, удаление,
переключатель «Сохранять видео на диске»
- compose: bind-mount ./web для правок без пересборки
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
45 lines
1.1 KiB
Python
45 lines
1.1 KiB
Python
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from api.db.section import create_section, delete_section, list_sections
|
|
|
|
|
|
router = APIRouter(prefix="/sections", tags=["sections"])
|
|
|
|
|
|
class SectionIn(BaseModel):
|
|
name: str
|
|
parent_id: int | None = None
|
|
|
|
|
|
class SectionOut(BaseModel):
|
|
id: int
|
|
name: str
|
|
parent_id: int | None
|
|
video_count: int
|
|
|
|
|
|
@router.get("/", response_model=list[SectionOut])
|
|
async def list_all():
|
|
return await list_sections()
|
|
|
|
|
|
@router.post("/", response_model=SectionOut)
|
|
async def create(body: SectionIn):
|
|
name = body.name.strip()
|
|
if not name:
|
|
raise HTTPException(status_code=400, detail="Section name is empty")
|
|
try:
|
|
s = await create_section(name, body.parent_id)
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
return SectionOut(id=s.id, name=s.name, parent_id=s.parent_id, video_count=0)
|
|
|
|
|
|
@router.delete("/{section_id}")
|
|
async def delete(section_id: int):
|
|
ok = await delete_section(section_id)
|
|
if not ok:
|
|
raise HTTPException(status_code=404, detail="Section not found")
|
|
return {"ok": True}
|