video-accessibility/backend/app/models/job.py
michael 865fcdc246 feat: add TTS settings panel with model, speed, and style options
- Add model selection (flash vs pro) for quality control
- Add speed slider (0.5x - 2.0x) for pacing adjustment
- Add style presets (neutral, calm, energetic, professional, warm, documentary)
- Add custom style prompt option for advanced customization
- New /tts/options endpoint returns available TTS options
- Voice preview now tests all settings so users hear exact output
- Backward compatible: all new fields have sensible defaults

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 15:22:14 -06:00

119 lines
3.9 KiB
Python

from datetime import datetime
from enum import Enum
from typing import Any, Literal, Optional
from pydantic import BaseModel, Field, constr
class JobStatus(str, Enum):
CREATED = "created"
INGESTING = "ingesting"
AI_PROCESSING = "ai_processing"
PENDING_QC = "pending_qc"
APPROVED_ENGLISH = "approved_english" # For English source videos
APPROVED_SOURCE = "approved_source" # For non-English source videos
REJECTED = "rejected"
QC_FEEDBACK = "qc_feedback"
TRANSLATING = "translating"
TTS_GENERATING = "tts_generating"
PENDING_FINAL_REVIEW = "pending_final_review"
COMPLETED = "completed"
@classmethod
def is_approved(cls, status: str) -> bool:
"""Check if status indicates source approval (any language)"""
return status in [cls.APPROVED_ENGLISH.value, cls.APPROVED_SOURCE.value]
class Source(BaseModel):
filename: str
original_filename: Optional[str] = None
gcs_uri: str
duration_s: Optional[float] = None
language: constr(min_length=2, max_length=10) = "en" # Final source language (from detection or explicit)
language_hint: Optional[str] = None # User-provided hint for non-English videos
detected_language: Optional[str] = None # AI-detected language from Gemini
class TTSPreferences(BaseModel):
"""TTS voice preferences for audio description generation"""
provider: Literal["gemini", "google", "elevenlabs"] = "gemini"
default_voice: str = "Kore" # Default Gemini voice
voices_per_language: dict[str, str] = {} # {"en": "Kore", "es": "Aoede"}
# TTS quality and style settings
model: Literal["flash", "pro"] = "flash" # flash = fast/cheap, pro = higher quality
speed: float = Field(default=1.0, ge=0.5, le=2.0) # Speech rate multiplier
style_preset: Literal[
"neutral", "calm", "energetic", "professional", "warm", "documentary", "custom"
] = "neutral"
custom_style_prompt: Optional[str] = None # Used when style_preset is "custom"
class RequestedOutputs(BaseModel):
captions_vtt: bool = True
audio_description_vtt: bool = True
audio_description_mp3: bool = True
languages: list[str] = []
transcreation: list[str] = []
tts_preferences: Optional[TTSPreferences] = None
class LangOutput(BaseModel):
captions_vtt_gcs: Optional[str] = None
ad_vtt_gcs: Optional[str] = None
ad_mp3_gcs: Optional[str] = None
origin: Optional[Literal["translate", "transcreate"]] = None
qa_notes: Optional[str] = None
class ReviewHistoryItem(BaseModel):
at: datetime
status: str
by: Optional[str] = None
notes: Optional[str] = None
class Review(BaseModel):
notes: Optional[str] = ""
reviewer_id: Optional[str] = None
history: list[ReviewHistoryItem] = []
class AISection(BaseModel):
ingestion_json: Optional[dict[str, Any]] = None
confidence: Optional[float] = None
class Job(BaseModel):
id: Optional[str] = Field(None, alias="_id")
client_id: str
title: str
source: Source
requested_outputs: RequestedOutputs
status: JobStatus = JobStatus.CREATED
review: Review = Review()
outputs: Optional[dict[str, LangOutput]] = None
ai: Optional[AISection] = None
error: Optional[dict[str, Any]] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
class Config:
populate_by_name = True
use_enum_values = True
class JobCreate(BaseModel):
title: str
source_is_english: bool = True # True = English source, False = other language (auto-detect)
language_hint: Optional[str] = None # Optional hint when source_is_english=False
requested_outputs: RequestedOutputs
class JobUpdate(BaseModel):
title: Optional[str] = None
status: Optional[JobStatus] = None
review: Optional[Review] = None
outputs: Optional[dict[str, LangOutput]] = None
ai: Optional[AISection] = None
error: Optional[dict[str, Any]] = None