feature: create multitasking
This commit is contained in:
@@ -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)}
|
||||
Reference in New Issue
Block a user