feature: create multitasking

This commit is contained in:
Daniil
2026-02-04 02:19:50 +03:00
parent 67e0f22b4f
commit a25bf623ea
24 changed files with 5227 additions and 21 deletions
+168
View File
@@ -0,0 +1,168 @@
"""
API endpoints for background task submission.
"""
from __future__ import annotations
import uuid
from typing import cast
from fastapi import APIRouter, Depends, HTTPException, Request, 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,
MediaConvertRequest,
MediaProbeRequest,
SilenceRemoveRequest,
TaskStatusEnum,
TaskStatusResponse,
TaskSubmitResponse,
TaskTypeEnum,
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(
"/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(
"/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="Job not found"
)
if not current_user.is_staff and job.user_id != current_user.id:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden")
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,
request: Request,
db: AsyncSession = Depends(get_db),
) -> dict[str, str]:
"""Internal webhook endpoint for task status updates."""
try:
await request.json()
except Exception:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid JSON payload"
)
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="Job not found"
)
return {"status": "received", "job_id": str(job_id)}