Files
Daniil 259d3da89f rev 4
2026-04-07 13:42:45 +03:00

211 lines
6.5 KiB
Python

"""
API endpoints for background task submission.
"""
from __future__ import annotations
import uuid
from typing import cast
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from cpv3.db.session import get_db
from cpv3.infrastructure.auth import get_current_user
from cpv3.modules.jobs.service import JobService
from cpv3.modules.tasks.schemas import (
CaptionsGenerateRequest,
FrameExtractRequest,
MediaConvertRequest,
MediaProbeRequest,
SilenceApplyRequest,
SilenceDetectRequest,
SilenceRemoveRequest,
TaskStatusEnum,
TaskStatusResponse,
TaskSubmitResponse,
TaskTypeEnum,
TaskWebhookEvent,
TranscriptionGenerateRequest,
)
from cpv3.modules.tasks.service import TaskService
from cpv3.modules.users.models import User
router = APIRouter(prefix="/api/tasks", tags=["tasks"])
@router.post(
"/media-probe/",
response_model=TaskSubmitResponse,
status_code=status.HTTP_202_ACCEPTED,
)
async def submit_media_probe(
body: MediaProbeRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> TaskSubmitResponse:
"""Submit a background task to probe media file metadata."""
service = TaskService(db)
return await service.submit_media_probe(requester=current_user, request=body)
@router.post(
"/silence-remove/",
response_model=TaskSubmitResponse,
status_code=status.HTTP_202_ACCEPTED,
)
async def submit_silence_remove(
body: SilenceRemoveRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> TaskSubmitResponse:
"""Submit a background task to remove silence from media file."""
service = TaskService(db)
return await service.submit_silence_remove(requester=current_user, request=body)
@router.post(
"/silence-detect/",
response_model=TaskSubmitResponse,
status_code=status.HTTP_202_ACCEPTED,
)
async def submit_silence_detect(
body: SilenceDetectRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> TaskSubmitResponse:
"""Submit a background task to detect silent segments in media file."""
service = TaskService(db)
return await service.submit_silence_detect(requester=current_user, request=body)
@router.post(
"/silence-apply/",
response_model=TaskSubmitResponse,
status_code=status.HTTP_202_ACCEPTED,
)
async def submit_silence_apply(
body: SilenceApplyRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> TaskSubmitResponse:
"""Submit a background task to apply silence cuts to media file."""
service = TaskService(db)
return await service.submit_silence_apply(requester=current_user, request=body)
@router.post(
"/media-convert/",
response_model=TaskSubmitResponse,
status_code=status.HTTP_202_ACCEPTED,
)
async def submit_media_convert(
body: MediaConvertRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> TaskSubmitResponse:
"""Submit a background task to convert media file format."""
service = TaskService(db)
return await service.submit_media_convert(requester=current_user, request=body)
@router.post(
"/transcription-generate/",
response_model=TaskSubmitResponse,
status_code=status.HTTP_202_ACCEPTED,
)
async def submit_transcription_generate(
body: TranscriptionGenerateRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> TaskSubmitResponse:
"""Submit a background task to generate transcription from audio/video."""
service = TaskService(db)
return await service.submit_transcription_generate(
requester=current_user, request=body
)
@router.post(
"/frame-extract/",
response_model=TaskSubmitResponse,
status_code=status.HTTP_202_ACCEPTED,
)
async def submit_frame_extract(
body: FrameExtractRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> TaskSubmitResponse:
"""Submit a background task to extract video frames for timeline thumbnails."""
service = TaskService(db)
return await service.submit_frame_extract(requester=current_user, request=body)
@router.post(
"/captions-generate/",
response_model=TaskSubmitResponse,
status_code=status.HTTP_202_ACCEPTED,
)
async def submit_captions_generate(
body: CaptionsGenerateRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> TaskSubmitResponse:
"""Submit a background task to generate captions on video."""
service = TaskService(db)
try:
return await service.submit_captions_generate(
requester=current_user, request=body
)
except ValueError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
@router.get("/status/{job_id}/", response_model=TaskStatusResponse)
async def get_task_status(
job_id: uuid.UUID,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> TaskStatusResponse:
"""Get the status of a background task."""
job_service = JobService(db)
job = await job_service.get_job(job_id)
if job is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Задача не найдена"
)
if not current_user.is_staff and job.user_id != current_user.id:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Доступ запрещён")
return TaskStatusResponse(
job_id=job.id,
status=cast(TaskStatusEnum, job.status),
job_type=cast(TaskTypeEnum, job.job_type),
progress_pct=job.project_pct,
current_message=job.current_message,
error_message=job.error_message,
output_data=job.output_data,
started_at=job.started_at,
finished_at=job.finished_at,
)
@router.post("/webhook/{job_id}/", include_in_schema=False)
async def task_webhook_callback(
job_id: uuid.UUID,
body: TaskWebhookEvent,
db: AsyncSession = Depends(get_db),
) -> dict[str, str]:
"""Internal webhook endpoint for task status updates."""
service = TaskService(db)
try:
await service.record_webhook_event(job_id=job_id, event=body)
except ValueError as exc:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)
) from exc
return {"status": "received", "job_id": str(job_id)}