- Create NFO API models (11 Pydantic models)
- Implement 8 REST API endpoints for NFO management
- Register NFO router in FastAPI app
- Create 18 comprehensive API tests
- Add detailed status documentation
Endpoints:
- GET /api/nfo/{id}/check - Check NFO/media status
- POST /api/nfo/{id}/create - Create NFO & media
- PUT /api/nfo/{id}/update - Update NFO
- GET /api/nfo/{id}/content - Get NFO content
- GET /api/nfo/{id}/media/status - Media status
- POST /api/nfo/{id}/media/download - Download media
- POST /api/nfo/batch/create - Batch operations
- GET /api/nfo/missing - List missing NFOs
Remaining: Refactor to use series_app dependency pattern
358 lines
9.5 KiB
Python
358 lines
9.5 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"
|
|
)
|