feat: Task 4 - Add NFO check to download flow

- Integrate NFO checking into SeriesApp.download() method
- Auto-create NFO and media files when missing (if configured)
- Add progress events: nfo_creating, nfo_completed, nfo_failed
- NFO failures don't block episode downloads
- Add 11 comprehensive integration tests (all passing)
- Respect all NFO configuration settings
- No regression in existing tests (1284 passing)
This commit is contained in:
2026-01-15 19:58:16 +01:00
parent 45a37a8c08
commit b27cd5fb82
4 changed files with 800 additions and 2 deletions

View File

@@ -18,10 +18,13 @@ from typing import Any, Dict, List, Optional
from events import Events
from src.config.settings import settings
from src.core.entities.SerieList import SerieList
from src.core.entities.series import Serie
from src.core.providers.provider_factory import Loaders
from src.core.SerieScanner import SerieScanner
from src.core.services.nfo_service import NFOService
from src.core.services.tmdb_client import TMDBAPIError
logger = logging.getLogger(__name__)
@@ -166,6 +169,23 @@ class SeriesApp:
# Synchronous init used during constructor to avoid awaiting
# in __init__
self._init_list_sync()
# Initialize NFO service if TMDB API key is configured
self.nfo_service: Optional[NFOService] = None
if settings.tmdb_api_key:
try:
self.nfo_service = NFOService(
tmdb_api_key=settings.tmdb_api_key,
anime_directory=directory_to_search,
image_size=settings.nfo_image_size,
auto_create=settings.nfo_auto_create
)
logger.info("NFO service initialized successfully")
except Exception as e: # pylint: disable=broad-except
logger.warning(
"Failed to initialize NFO service: %s", str(e)
)
self.nfo_service = None
logger.info(
"SeriesApp initialized for directory: %s",
@@ -348,6 +368,95 @@ class SeriesApp:
)
return False
# Check and create NFO files if needed
if self.nfo_service and settings.nfo_auto_create:
try:
# Check if NFO exists
nfo_exists = await self.nfo_service.check_nfo_exists(
serie_folder
)
if not nfo_exists:
logger.info(
"NFO not found for %s, creating metadata...",
serie_folder
)
# Fire NFO creation started event
self._events.download_status(
DownloadStatusEventArgs(
serie_folder=serie_folder,
key=key,
season=season,
episode=episode,
status="nfo_creating",
message="Creating NFO metadata...",
item_id=item_id,
)
)
# Create NFO and download media files
try:
# Use folder name as series name
await self.nfo_service.create_tvshow_nfo(
serie_name=serie_folder,
serie_folder=serie_folder,
download_poster=settings.nfo_download_poster,
download_logo=settings.nfo_download_logo,
download_fanart=settings.nfo_download_fanart
)
logger.info(
"NFO and media files created for %s",
serie_folder
)
# Fire NFO creation completed event
self._events.download_status(
DownloadStatusEventArgs(
serie_folder=serie_folder,
key=key,
season=season,
episode=episode,
status="nfo_completed",
message="NFO metadata created",
item_id=item_id,
)
)
except TMDBAPIError as tmdb_error:
logger.warning(
"Failed to create NFO for %s: %s",
serie_folder,
str(tmdb_error)
)
# Fire failed event (but continue with download)
self._events.download_status(
DownloadStatusEventArgs(
serie_folder=serie_folder,
key=key,
season=season,
episode=episode,
status="nfo_failed",
message=(
f"NFO creation failed: "
f"{str(tmdb_error)}"
),
item_id=item_id,
)
)
else:
logger.debug("NFO already exists for %s", serie_folder)
except Exception as nfo_error: # pylint: disable=broad-except
logger.error(
"Error checking/creating NFO for %s: %s",
serie_folder,
str(nfo_error),
exc_info=True
)
# Don't fail the download if NFO creation fails
try:
def download_progress_handler(progress_info):
"""Handle download progress events from loader."""