"""Request models for API endpoints.""" from typing import Optional, List from pydantic import BaseModel, Field, validator class TracklistEntry(BaseModel): """A single track entry from the tracklist.""" ts: str = Field(..., description="Timestamp (e.g., '00:00' or '00:00-01:30')") tn: Optional[str] = Field("", description="Track name") an: Optional[str] = Field("", description="Author/artist") al: Optional[str] = Field("", description="Album") date: Optional[str] = Field("", description="Date/year") ext: Optional[str] = Field("", description="File extension") @validator("ts") def validate_timestamp(cls, v: str) -> str: v = v.strip() if not v: raise ValueError("Timestamp cannot be empty") if "-" in v: parts = v.split("-", 1) start = parts[0].strip() end = parts[1].strip() if not start or not end: raise ValueError("Invalid range format. Expected 'start-end'") for ts in [start, end]: cls._validate_single_timestamp(ts) else: cls._validate_single_timestamp(v) return v @staticmethod def _validate_single_timestamp(ts: str) -> None: parts = ts.split(":") if len(parts) not in (2, 3): raise ValueError(f"Invalid timestamp format: {ts}. Expected mm:ss or HH:MM:SS") try: for p in parts: int(p) except ValueError: raise ValueError(f"Invalid timestamp: {ts}. Must contain only numbers.") class SplitRequest(BaseModel): """Request model for the split endpoint.""" task_id: str = Field(..., description="Task ID from upload") tracklist: List[TracklistEntry] = Field(..., description="List of tracks") # Container and codec options container: Optional[str] = Field(None, description="Output container (auto-detect if None)") audio_codec: Optional[str] = Field("copy", description="Audio codec (copy or encoder name)") video_codec: Optional[str] = Field("copy", description="Video codec (copy or encoder name)") subtitle_codec: Optional[str] = Field("copy", description="Subtitle codec (copy or encoder name)") video_quality: Optional[int] = Field(None, description="Video quality (encoder-specific integer)") # Stream handling drop_video: bool = Field(False, description="Remove video streams") drop_subs: bool = Field(False, description="Remove subtitle streams") # Filename options number_tracks: bool = Field(False, description="Prepend track numbers") replace_bad_chars: bool = Field(False, description="Replace bad characters") replacement_char: str = Field("_", description="Replacement character") bad_chars: str = Field(r'!@#№$;:%^&?*(){}[]\/<>+=~`\' ', description="Bad characters to replace") skip_existing: bool = Field(False, description="Skip existing files") output_template: str = Field("%an-%tn.%ext", description="Output filename template") # Metadata options album: Optional[str] = Field(None, description="Album name") comment: Optional[str] = Field(None, description="Comment") no_comment: bool = Field(False, description="Ignore comment") comment_stream: Optional[int] = Field(None, description="Comment stream index") merge_comments: bool = Field(False, description="Merge all comments") comment_separator: str = Field("; ", description="Separator for merged comments")