115 lines
2.9 KiB
Python
115 lines
2.9 KiB
Python
|
|
from datetime import datetime
|
||
|
|
from typing import List, Optional
|
||
|
|
from pydantic import BaseModel, Field
|
||
|
|
|
||
|
|
from .db_models import ThemeType, DownloadStatus
|
||
|
|
|
||
|
|
|
||
|
|
# ============== Shikimori Search ==============
|
||
|
|
|
||
|
|
class AnimeSearchResult(BaseModel):
|
||
|
|
"""Single anime search result from Shikimori."""
|
||
|
|
shikimori_id: int
|
||
|
|
title_russian: Optional[str] = None
|
||
|
|
title_english: Optional[str] = None
|
||
|
|
title_japanese: Optional[str] = None
|
||
|
|
year: Optional[int] = None
|
||
|
|
poster_url: Optional[str] = None
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
|
||
|
|
class SearchResponse(BaseModel):
|
||
|
|
"""Response for anime search."""
|
||
|
|
results: List[AnimeSearchResult]
|
||
|
|
total: int
|
||
|
|
|
||
|
|
|
||
|
|
# ============== Anime Themes ==============
|
||
|
|
|
||
|
|
class ThemeInfo(BaseModel):
|
||
|
|
"""Information about a single anime theme (OP/ED)."""
|
||
|
|
id: int
|
||
|
|
theme_type: ThemeType
|
||
|
|
sequence: int
|
||
|
|
full_name: str # "OP1", "ED2"
|
||
|
|
song_title: Optional[str] = None
|
||
|
|
artist: Optional[str] = None
|
||
|
|
video_url: Optional[str] = None
|
||
|
|
is_downloaded: bool
|
||
|
|
download_status: Optional[DownloadStatus] = None
|
||
|
|
file_size_bytes: Optional[int] = None
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
|
||
|
|
class AnimeDetailResponse(BaseModel):
|
||
|
|
"""Detailed anime info with themes."""
|
||
|
|
id: int
|
||
|
|
shikimori_id: int
|
||
|
|
title_russian: Optional[str] = None
|
||
|
|
title_english: Optional[str] = None
|
||
|
|
title_japanese: Optional[str] = None
|
||
|
|
year: Optional[int] = None
|
||
|
|
poster_url: Optional[str] = None
|
||
|
|
themes: List[ThemeInfo]
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
|
||
|
|
# ============== Download Queue ==============
|
||
|
|
|
||
|
|
class AddToQueueRequest(BaseModel):
|
||
|
|
"""Request to add specific themes to download queue."""
|
||
|
|
theme_ids: List[int] = Field(..., min_length=1)
|
||
|
|
|
||
|
|
|
||
|
|
class AddAllThemesRequest(BaseModel):
|
||
|
|
"""Request to add all themes from an anime."""
|
||
|
|
anime_id: int
|
||
|
|
|
||
|
|
|
||
|
|
class QueueTaskResponse(BaseModel):
|
||
|
|
"""Single task in the download queue."""
|
||
|
|
id: int
|
||
|
|
theme_id: int
|
||
|
|
anime_title: str
|
||
|
|
theme_name: str # "OP1", "ED2"
|
||
|
|
song_title: Optional[str] = None
|
||
|
|
status: DownloadStatus
|
||
|
|
progress_percent: int
|
||
|
|
error_message: Optional[str] = None
|
||
|
|
estimated_size_bytes: int
|
||
|
|
created_at: datetime
|
||
|
|
started_at: Optional[datetime] = None
|
||
|
|
completed_at: Optional[datetime] = None
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
|
||
|
|
class QueueStatusResponse(BaseModel):
|
||
|
|
"""Current status of the download queue."""
|
||
|
|
tasks: List[QueueTaskResponse]
|
||
|
|
total_queued: int
|
||
|
|
total_downloading: int
|
||
|
|
total_done: int
|
||
|
|
total_failed: int
|
||
|
|
estimated_queue_size_bytes: int
|
||
|
|
worker_running: bool = False # Indicates if download worker is currently active
|
||
|
|
|
||
|
|
|
||
|
|
# ============== Storage Stats ==============
|
||
|
|
|
||
|
|
class StorageStatsResponse(BaseModel):
|
||
|
|
"""S3 storage usage statistics."""
|
||
|
|
used_bytes: int
|
||
|
|
limit_bytes: int
|
||
|
|
used_percent: float
|
||
|
|
available_bytes: int
|
||
|
|
can_download: bool # False if limit exceeded
|
||
|
|
openings_count: int
|