211 lines
6.5 KiB
Python
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="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,
|
|
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)}
|