"""Pydantic-модели и перечисления доменного слоя.""" from __future__ import annotations import uuid from datetime import datetime, timezone from enum import Enum from pydantic import BaseModel, Field class DownloadStatus(str, Enum): PENDING = "pending" DOWNLOADING = "downloading" DONE = "done" FAILED = "failed" PAUSED = "paused" # задел на будущее def _now() -> datetime: return datetime.now(timezone.utc) def _new_id() -> str: return uuid.uuid4().hex class Download(BaseModel): id: str = Field(default_factory=_new_id) url: str filename: str | None = None status: DownloadStatus = DownloadStatus.PENDING progress: float = 0.0 # 0.0–100.0 size_bytes: int | None = None downloaded_bytes: int = 0 speed: float | None = None # байт/сек eta: float | None = None # секунд до конца, оценка error: str | None = None created_at: datetime = Field(default_factory=_now) finished_at: datetime | None = None # момент перехода в done/failed class CreateDownloads(BaseModel): """Тело запроса POST /api/downloads — список ссылок (по одной на строку или массивом).""" urls: list[str] = Field(default_factory=list, min_length=1) class DownloaderInfo(BaseModel): """Зарегистрированная стратегия скачивания — для страницы «Загрузчики».""" name: str kind: str # "direct" | "extractor" | "fallback" priority: int description: str | None = None # --- События шины (то, что улетает в SSE) --- class DownloadEvent(BaseModel): """Обёртка над снимком загрузки, транслируемая в браузер через SSE.""" type: str # "created" | "progress" | "done" | "failed" | "deleted" download: Download | None = None id: str | None = None # для type="deleted" хватает id