video-accessibility/backend/app/schemas/job.py
Vadym Samoilenko fb99a5e8c7
Some checks failed
Deploy Backend / Deploy API to Cloud Run (push) Has been cancelled
Deploy Frontend / Build and Deploy Frontend (push) Has been cancelled
CI / Backend Lint & Test (push) Has been cancelled
CI / Frontend Lint & Test (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / Dependency Check (push) Has been cancelled
Deploy Backend / Deploy Worker to Cloud Run (push) Has been cancelled
Deploy Backend / Run Smoke Tests (push) Has been cancelled
Deploy Backend / Notify Deployment Status (push) Has been cancelled
Deploy Frontend / Notify Deployment Status (push) Has been cancelled
CI / Integration Tests (push) Has been cancelled
CI / Build Backend Docker Image (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
feat(vtt): add note field to VttUpdateRequest and wire through create_version calls
2026-05-14 11:44:07 +01:00

200 lines
5.4 KiB
Python

from typing import Any
from pydantic import BaseModel, field_validator
from ..models.job import (
AccessibleVideoProgressItem,
JobFailure,
JobStatus,
LangOutput,
RequestedOutputs,
Review,
TTSPreferences,
)
from ..schemas.accessible_video import AccessibleVideoMethod
class JobResponse(BaseModel):
id: str
client_id: str | None = None # ID of the user who created the job
title: str
status: JobStatus
source: dict[str, Any]
requested_outputs: RequestedOutputs
review: Review
outputs: dict[str, LangOutput] | None = None
accessible_video_progress: dict[str, AccessibleVideoProgressItem] | None = None
failure: JobFailure | None = None
error: dict[str, Any] | None = None
created_at: str | None = None
updated_at: str | None = None
created_by_name: str | None = None # User's full_name who created the job
cost_tracker_project_id: str | None = None
class JobListResponse(BaseModel):
jobs: list[JobResponse]
total: int
page: int
size: int
class JobCreateRequest(BaseModel):
title: str
requested_outputs: RequestedOutputs
class JobUpdateRequest(BaseModel):
title: str | None = None
review_notes: str | None = None
cost_tracker_project_id: str | None = None
class ApproveEnglishRequest(BaseModel):
notes: str | None = None
class ApproveSourceRequest(BaseModel):
"""Request to approve source language content (works for any language)"""
notes: str | None = None
tts_preferences: TTSPreferences | None = None # Override TTS voice settings
accessible_video_method: AccessibleVideoMethod | None = None # User-selected method for accessible video
class UpdateTTSPreferencesRequest(BaseModel):
"""Request to update TTS preferences and regenerate all TTS segments"""
tts_preferences: TTSPreferences
class RejectJobRequest(BaseModel):
notes: str
class CompleteJobRequest(BaseModel):
notes: str | None = None
class VttUpdateRequest(BaseModel):
captions_vtt: str | None = None
audio_description_vtt: str | None = None
language: str | None = None # If None, defaults to source language
if_match: str | None = None # Optimistic locking — SHA1 of expected current content
retranslate_languages: bool = False # Re-translate all target languages from updated source VTT
note: str | None = None # Optional save message shown in version history
@field_validator('captions_vtt', 'audio_description_vtt', mode='before')
@classmethod
def empty_str_to_none(cls, v: Any) -> str | None:
return None if v == '' else v
class VttTimingAdjustRequest(BaseModel):
offset_seconds: float
language: str = "en"
adjust_captions: bool = True
adjust_audio_description: bool = True
class JobDownloadsResponse(BaseModel):
downloads: dict[str, dict[str, str] | str] # language -> {file_type: signed_url} OR source_video -> signed_url
class VttContentResponse(BaseModel):
captions_vtt: str | None = None
audio_description_vtt: str | None = None
retimed_captions_vtt: str | None = None # Re-timed captions for accessible videos
etag: str | None = None # SHA1 hash for optimistic locking (If-Match on PATCH)
class AssetValidationResponse(BaseModel):
is_valid: bool
errors: list[str]
warnings: list[str] = []
class JobDeleteResponse(BaseModel):
message: str
class BulkDeleteRequest(BaseModel):
job_ids: list[str]
class BulkDeleteResponse(BaseModel):
deleted_count: int
total_requested: int
errors: list[str]
class BulkApproveRequest(BaseModel):
"""Request to bulk approve multiple jobs with optional settings"""
job_ids: list[str]
notes: str | None = None
accessible_video_method: AccessibleVideoMethod | None = None # Method for accessible video
tts_preferences: TTSPreferences | None = None
class BulkApproveResponse(BaseModel):
"""Response for bulk approval operation"""
approved_count: int
total_requested: int
errors: list[str]
class ReturnToQCRequest(BaseModel):
notes: str
class BulkReturnToQCRequest(BaseModel):
job_ids: list[str]
notes: str
class BulkReturnToQCResponse(BaseModel):
returned_count: int
total_requested: int
errors: list[str]
class BulkDownloadRequest(BaseModel):
"""Request to download multiple jobs as a single zip file"""
job_ids: list[str]
class BlockedOnSourceRequest(BaseModel):
reason: str # brief description of what is wrong with the source video
class PromoteToQCRequest(BaseModel):
notes: str = "" # optional context for the QC team
# ── PR-3: Resumable / chunked upload ──────────────────────────────────────────
class UploadInitRequest(BaseModel):
filename: str
content_type: str
file_size: int # bytes — validated server-side against settings.upload_max_video_bytes
class UploadInitResponse(BaseModel):
job_id: str
upload_url: str # GCS resumable session URI — browser uploads chunks directly here
class UploadCompleteRequest(BaseModel):
job_id: str
title: str
original_filename: str
requested_outputs: dict
brand_context: str | None = None
project_id: str | None = None
brief_id: str | None = None
deadline: str | None = None
initial_linguist_id: str | None = None
initial_reviewer_id: str | None = None
class RetranslateLanguageRequest(BaseModel):
language: str
reason: str | None = None