"""Pydantic models for Sonarr webhook integration""" from pydantic import BaseModel, Field, validator from typing import Optional, Dict, Any, List from datetime import datetime from enum import Enum class SonarrEventType(str, Enum): """Sonarr event types""" GRAB = "Grab" DOWNLOAD = "Download" MOVIE_DELETE = "MovieDelete" MOVIE_FILE_DELETE = "MovieFileDelete" RENAME = "Rename" DELETE = "Delete" TEST = "Test" class SonarrQuality(BaseModel): """Quality information from Sonarr""" quality: Dict[str, Any] revision: Dict[str, Any] class SonarrRelease(BaseModel): """Release information from Sonarr""" indexer: str releaseTitle: str quality: SonarrQuality class SonarrEpisodeFile(BaseModel): """Episode file information""" id: int seriesId: int seasonNumber: int episodeNumber: int relativePath: str path: str size: int dateAdded: datetime quality: SonarrQuality mediaInfo: Optional[Dict[str, Any]] = None class SonarrSeries(BaseModel): """Series information from Sonarr""" tvdbId: int = Field(..., alias="tvdbId") title: str sortTitle: str status: str ended: bool overview: str network: str airTime: str images: List[Dict[str, Any]] seasons: List[int] year: int path: str qualityProfileId: int languageProfileId: int seasonFolder: bool monitored: bool useSceneNumbering: bool runtime: int tvRageId: Optional[int] = None tvMazeId: Optional[int] = None firstAired: Optional[datetime] = None seriesType: str = "standard" cleanTitle: str imdbId: str titleSlug: str certification: str genres: List[str] tags: List[int] added: datetime ratings: Dict[str, Any] id: int class Config: populate_by_name = True class SonarrEpisode(BaseModel): """Episode information from Sonarr""" seriesId: int episodeFileId: int seasonNumber: int episodeNumber: int title: str airDate: str airDateUtc: datetime overview: str hasFile: bool monitored: bool absoluteEpisodeNumber: Optional[int] = None unverifiedSceneNumbering: bool = False id: int class SonarrWebhookPayload(BaseModel): """Main Sonarr webhook payload""" eventType: SonarrEventType instanceName: str applicationUrl: str series: Optional[SonarrSeries] = None episodes: Optional[List[SonarrEpisode]] = None release: Optional[SonarrRelease] = None episodeFile: Optional[SonarrEpisodeFile] = None deletedFiles: Optional[List[str]] = None deleteEpisodeFiles: bool = False @validator('episodes') def validate_episodes(cls, v, values): """Ensure episodes are present for relevant event types""" event_type = values.get('eventType') if event_type in [SonarrEventType.GRAB, SonarrEventType.DOWNLOAD, SonarrEventType.RENAME]: if not v or len(v) == 0: raise ValueError(f"Event type {event_type} requires episodes") return v @validator('series') def validate_series(cls, v, values): """Ensure series is present for relevant event types""" event_type = values.get('eventType') if event_type in [SonarrEventType.GRAB, SonarrEventType.DOWNLOAD, SonarrEventType.RENAME, SonarrEventType.DELETE]: if not v: raise ValueError(f"Event type {event_type} requires series") return v class SonarrMapping(BaseModel): """Mapping between Sonarr series and anime providers""" sonarr_series_id: int sonarr_title: str anime_provider: str # 'anime-sama', 'neko-sama', etc. anime_url: str anime_title: str lang: str = "vostfr" quality_preference: Optional[str] = None # '1080p', '720p', etc. auto_download: bool = True created_at: datetime = Field(default_factory=datetime.now) updated_at: datetime = Field(default_factory=datetime.now) class Config: json_encoders = { datetime: lambda v: v.isoformat() } class SonarrConfig(BaseModel): """Sonarr webhook configuration""" webhook_enabled: bool = False webhook_secret: Optional[str] = None # HMAC SHA256 secret auto_download_enabled: bool = True default_language: str = "vostfr" default_quality: Optional[str] = None default_provider: str = "anime-sama" verify_hmac: bool = False log_webhooks: bool = True class Config: json_schema_extra = { "example": { "webhook_enabled": True, "webhook_secret": "your-secret-key-here", "auto_download_enabled": True, "default_language": "vostfr", "default_quality": "1080p", "default_provider": "anime-sama", "verify_hmac": True, "log_webhooks": True } } class SonarrDownloadRequest(BaseModel): """Request to download anime based on Sonarr event""" sonarr_series_id: int sonarr_title: str season_number: int episode_number: int quality: Optional[str] = None lang: str = "vostfr" provider: str = "anime-sama" class Config: json_schema_extra = { "example": { "sonarr_series_id": 123, "sonarr_title": "Naruto Shippuden", "season_number": 1, "episode_number": 1, "quality": "1080p", "lang": "vostfr", "provider": "anime-sama" } }