- Add PUT /anime/{key} endpoint for updating anime key, tmdb_id, tvdb_id
- Add NFO diagnostics and repair endpoints (GET/POST /nfo/diagnostics)
- Add edit modal UI with context menu integration
- Add frontend JS modules for context-menu and edit-modal
- Add comprehensive tests for edit, rename, and NFO repair flows
384 lines
10 KiB
Python
384 lines
10 KiB
Python
"""NFO API request and response models.
|
|
|
|
This module defines Pydantic models for NFO management API operations.
|
|
"""
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class MediaFilesStatus(BaseModel):
|
|
"""Status of media files (poster, logo, fanart) for a series.
|
|
|
|
Attributes:
|
|
has_poster: Whether poster.jpg exists
|
|
has_logo: Whether logo.png exists
|
|
has_fanart: Whether fanart.jpg exists
|
|
poster_path: Path to poster file if exists
|
|
logo_path: Path to logo file if exists
|
|
fanart_path: Path to fanart file if exists
|
|
"""
|
|
has_poster: bool = Field(
|
|
default=False,
|
|
description="Whether poster.jpg exists"
|
|
)
|
|
has_logo: bool = Field(
|
|
default=False,
|
|
description="Whether logo.png exists"
|
|
)
|
|
has_fanart: bool = Field(
|
|
default=False,
|
|
description="Whether fanart.jpg exists"
|
|
)
|
|
poster_path: Optional[str] = Field(
|
|
default=None,
|
|
description="Path to poster file if exists"
|
|
)
|
|
logo_path: Optional[str] = Field(
|
|
default=None,
|
|
description="Path to logo file if exists"
|
|
)
|
|
fanart_path: Optional[str] = Field(
|
|
default=None,
|
|
description="Path to fanart file if exists"
|
|
)
|
|
|
|
|
|
class NFOCheckResponse(BaseModel):
|
|
"""Response for NFO existence check.
|
|
|
|
Attributes:
|
|
serie_id: Series identifier
|
|
serie_folder: Series folder name
|
|
has_nfo: Whether tvshow.nfo exists
|
|
nfo_path: Path to NFO file if exists
|
|
media_files: Status of media files
|
|
"""
|
|
serie_id: str = Field(
|
|
...,
|
|
description="Series identifier"
|
|
)
|
|
serie_folder: str = Field(
|
|
...,
|
|
description="Series folder name"
|
|
)
|
|
has_nfo: bool = Field(
|
|
...,
|
|
description="Whether tvshow.nfo exists"
|
|
)
|
|
nfo_path: Optional[str] = Field(
|
|
default=None,
|
|
description="Path to NFO file if exists"
|
|
)
|
|
media_files: MediaFilesStatus = Field(
|
|
...,
|
|
description="Status of media files"
|
|
)
|
|
|
|
|
|
class NFOCreateRequest(BaseModel):
|
|
"""Request to create NFO file.
|
|
|
|
Attributes:
|
|
serie_name: Name to search in TMDB
|
|
year: Optional year to narrow search
|
|
download_poster: Whether to download poster.jpg
|
|
download_logo: Whether to download logo.png
|
|
download_fanart: Whether to download fanart.jpg
|
|
overwrite_existing: Whether to overwrite existing NFO
|
|
"""
|
|
serie_name: Optional[str] = Field(
|
|
default=None,
|
|
description="Name to search in TMDB (defaults to folder name)"
|
|
)
|
|
year: Optional[int] = Field(
|
|
default=None,
|
|
description="Optional year to narrow search"
|
|
)
|
|
download_poster: bool = Field(
|
|
default=True,
|
|
description="Whether to download poster.jpg"
|
|
)
|
|
download_logo: bool = Field(
|
|
default=True,
|
|
description="Whether to download logo.png"
|
|
)
|
|
download_fanart: bool = Field(
|
|
default=True,
|
|
description="Whether to download fanart.jpg"
|
|
)
|
|
overwrite_existing: bool = Field(
|
|
default=False,
|
|
description="Whether to overwrite existing NFO"
|
|
)
|
|
|
|
|
|
class NFOCreateResponse(BaseModel):
|
|
"""Response after NFO creation.
|
|
|
|
Attributes:
|
|
serie_id: Series identifier
|
|
serie_folder: Series folder name
|
|
nfo_path: Path to created NFO file
|
|
media_files: Status of downloaded media files
|
|
tmdb_id: TMDB ID of matched series
|
|
message: Success message
|
|
"""
|
|
serie_id: str = Field(
|
|
...,
|
|
description="Series identifier"
|
|
)
|
|
serie_folder: str = Field(
|
|
...,
|
|
description="Series folder name"
|
|
)
|
|
nfo_path: str = Field(
|
|
...,
|
|
description="Path to created NFO file"
|
|
)
|
|
media_files: MediaFilesStatus = Field(
|
|
...,
|
|
description="Status of downloaded media files"
|
|
)
|
|
tmdb_id: Optional[int] = Field(
|
|
default=None,
|
|
description="TMDB ID of matched series"
|
|
)
|
|
message: str = Field(
|
|
...,
|
|
description="Success message"
|
|
)
|
|
|
|
|
|
class NFOContentResponse(BaseModel):
|
|
"""Response containing NFO XML content.
|
|
|
|
Attributes:
|
|
serie_id: Series identifier
|
|
serie_folder: Series folder name
|
|
content: NFO XML content
|
|
file_size: Size of NFO file in bytes
|
|
last_modified: Last modification timestamp
|
|
"""
|
|
serie_id: str = Field(
|
|
...,
|
|
description="Series identifier"
|
|
)
|
|
serie_folder: str = Field(
|
|
...,
|
|
description="Series folder name"
|
|
)
|
|
content: str = Field(
|
|
...,
|
|
description="NFO XML content"
|
|
)
|
|
file_size: int = Field(
|
|
...,
|
|
description="Size of NFO file in bytes"
|
|
)
|
|
last_modified: Optional[datetime] = Field(
|
|
default=None,
|
|
description="Last modification timestamp"
|
|
)
|
|
|
|
|
|
class MediaDownloadRequest(BaseModel):
|
|
"""Request to download specific media files.
|
|
|
|
Attributes:
|
|
download_poster: Whether to download poster.jpg
|
|
download_logo: Whether to download logo.png
|
|
download_fanart: Whether to download fanart.jpg
|
|
overwrite_existing: Whether to overwrite existing files
|
|
"""
|
|
download_poster: bool = Field(
|
|
default=False,
|
|
description="Whether to download poster.jpg"
|
|
)
|
|
download_logo: bool = Field(
|
|
default=False,
|
|
description="Whether to download logo.png"
|
|
)
|
|
download_fanart: bool = Field(
|
|
default=False,
|
|
description="Whether to download fanart.jpg"
|
|
)
|
|
overwrite_existing: bool = Field(
|
|
default=False,
|
|
description="Whether to overwrite existing files"
|
|
)
|
|
|
|
|
|
class NFOBatchCreateRequest(BaseModel):
|
|
"""Request to batch create NFOs for multiple series.
|
|
|
|
Attributes:
|
|
serie_ids: List of series IDs to process
|
|
download_media: Whether to download media files
|
|
skip_existing: Whether to skip series with existing NFOs
|
|
max_concurrent: Maximum concurrent creations
|
|
"""
|
|
serie_ids: List[str] = Field(
|
|
...,
|
|
description="List of series IDs to process"
|
|
)
|
|
download_media: bool = Field(
|
|
default=True,
|
|
description="Whether to download media files"
|
|
)
|
|
skip_existing: bool = Field(
|
|
default=True,
|
|
description="Whether to skip series with existing NFOs"
|
|
)
|
|
max_concurrent: int = Field(
|
|
default=3,
|
|
ge=1,
|
|
le=10,
|
|
description="Maximum concurrent creations (1-10)"
|
|
)
|
|
|
|
|
|
class NFOBatchResult(BaseModel):
|
|
"""Result for a single series in batch operation.
|
|
|
|
Attributes:
|
|
serie_id: Series identifier
|
|
serie_folder: Series folder name
|
|
success: Whether operation succeeded
|
|
message: Success or error message
|
|
nfo_path: Path to NFO file if successful
|
|
"""
|
|
serie_id: str = Field(
|
|
...,
|
|
description="Series identifier"
|
|
)
|
|
serie_folder: str = Field(
|
|
...,
|
|
description="Series folder name"
|
|
)
|
|
success: bool = Field(
|
|
...,
|
|
description="Whether operation succeeded"
|
|
)
|
|
message: str = Field(
|
|
...,
|
|
description="Success or error message"
|
|
)
|
|
nfo_path: Optional[str] = Field(
|
|
default=None,
|
|
description="Path to NFO file if successful"
|
|
)
|
|
|
|
|
|
class NFOBatchCreateResponse(BaseModel):
|
|
"""Response after batch NFO creation.
|
|
|
|
Attributes:
|
|
total: Total number of series processed
|
|
successful: Number of successful creations
|
|
failed: Number of failed creations
|
|
skipped: Number of skipped series
|
|
results: Detailed results for each series
|
|
"""
|
|
total: int = Field(
|
|
...,
|
|
description="Total number of series processed"
|
|
)
|
|
successful: int = Field(
|
|
...,
|
|
description="Number of successful creations"
|
|
)
|
|
failed: int = Field(
|
|
...,
|
|
description="Number of failed creations"
|
|
)
|
|
skipped: int = Field(
|
|
...,
|
|
description="Number of skipped series"
|
|
)
|
|
results: List[NFOBatchResult] = Field(
|
|
...,
|
|
description="Detailed results for each series"
|
|
)
|
|
|
|
|
|
class NFOMissingSeries(BaseModel):
|
|
"""Information about a series missing NFO.
|
|
|
|
Attributes:
|
|
serie_id: Series identifier
|
|
serie_folder: Series folder name
|
|
serie_name: Display name
|
|
has_media: Whether any media files exist
|
|
media_files: Status of media files
|
|
"""
|
|
serie_id: str = Field(
|
|
...,
|
|
description="Series identifier"
|
|
)
|
|
serie_folder: str = Field(
|
|
...,
|
|
description="Series folder name"
|
|
)
|
|
serie_name: str = Field(
|
|
...,
|
|
description="Display name"
|
|
)
|
|
has_media: bool = Field(
|
|
default=False,
|
|
description="Whether any media files exist"
|
|
)
|
|
media_files: MediaFilesStatus = Field(
|
|
...,
|
|
description="Status of media files"
|
|
)
|
|
|
|
|
|
class NFOMissingResponse(BaseModel):
|
|
"""Response listing series without NFOs.
|
|
|
|
Attributes:
|
|
total_series: Total number of series in library
|
|
missing_nfo_count: Number of series without NFO
|
|
series: List of series missing NFO
|
|
"""
|
|
total_series: int = Field(
|
|
...,
|
|
description="Total number of series in library"
|
|
)
|
|
missing_nfo_count: int = Field(
|
|
...,
|
|
description="Number of series without NFO"
|
|
)
|
|
series: List[NFOMissingSeries] = Field(
|
|
...,
|
|
description="List of series missing NFO"
|
|
)
|
|
|
|
|
|
class NfoDiagnosticsResponse(BaseModel):
|
|
"""Response for NFO diagnostics showing missing required tags."""
|
|
|
|
has_nfo: bool = Field(..., description="Whether tvshow.nfo exists")
|
|
nfo_path: Optional[str] = Field(None, description="Path to NFO file if exists")
|
|
missing_tags: List[str] = Field(
|
|
default_factory=list,
|
|
description="List of missing required tag names"
|
|
)
|
|
required_tags: List[str] = Field(
|
|
default_factory=list,
|
|
description="All required tag names for reference"
|
|
)
|
|
|
|
|
|
class NfoRepairResponse(BaseModel):
|
|
"""Response after NFO repair attempt."""
|
|
|
|
success: bool = Field(..., description="Whether repair succeeded")
|
|
message: str = Field(..., description="Human-readable result message")
|
|
repaired_tags: List[str] = Field(
|
|
default_factory=list,
|
|
description="Tags that were missing before repair"
|
|
)
|