from __future__ import annotations from datetime import datetime # noqa: TC003 from typing import Any from pydantic import BaseModel, ConfigDict, Field, field_validator from app.features.core.schemas import Pagination class Task(BaseModel): model_config = ConfigDict(from_attributes=True, str_strip_whitespace=True) id: int | None = None name: str = Field(min_length=1) url: str = Field(min_length=1) folder: str = "" preset: str = "" timer: str = "" template: str = "" cli: str = "" auto_start: bool = True handler_enabled: bool = True enabled: bool = True created_at: datetime | None = None updated_at: datetime | None = None @field_validator("name", mode="before") @classmethod def _normalize_name(cls, value: Any) -> str: if not isinstance(value, str): msg: str = "Name must be a string." raise ValueError(msg) value = value.strip() if not value: msg = "Name cannot be empty." raise ValueError(msg) return value @field_validator("url", mode="before") @classmethod def _normalize_url(cls, value: Any) -> str: if not isinstance(value, str): msg: str = "URL must be a string." raise ValueError(msg) value = value.strip() if not value: msg = "URL cannot be empty." raise ValueError(msg) from app.library.Utils import validate_url try: validate_url(value, allow_internal=True) except ValueError as e: msg = f"Invalid URL format: {e!s}" raise ValueError(msg) from e return value @field_validator("timer", mode="before") @classmethod def _validate_timer(cls, value: Any) -> str: if not value: return "" if not isinstance(value, str): msg: str = "Timer must be a string." raise ValueError(msg) value = value.strip() if not value: return "" from datetime import UTC, datetime try: from cronsim import CronSim CronSim(value, datetime.now(UTC)) except Exception as e: msg = f"Invalid timer format: {e!s}" raise ValueError(msg) from e return value @field_validator("cli", mode="before") @classmethod def _validate_cli(cls, value: Any) -> str: if not value: return "" if not isinstance(value, str): msg: str = "CLI must be a string." raise ValueError(msg) value = value.strip() if not value: return "" try: from app.features.ytdlp.utils import arg_converter arg_converter(args=value) except Exception as e: msg = f"Invalid command options for yt-dlp: {e!s}" raise ValueError(msg) from e return value class TaskPatch(BaseModel): model_config = ConfigDict(str_strip_whitespace=True) name: str | None = None url: str | None = None folder: str | None = None preset: str | None = None timer: str | None = None template: str | None = None cli: str | None = None auto_start: bool | None = None handler_enabled: bool | None = None enabled: bool | None = None class TaskList(BaseModel): model_config = ConfigDict(from_attributes=True) items: list[Task] = Field(default_factory=list) pagination: Pagination