fix(nfo): add year field to series and create missing NFO files
- Add missing year field when building series list in anime_service - Add _create_missing_nfo to generate minimal NFO for series without one - Update perform_nfo_repair_scan to detect and create missing NFOs - Add semaphore-protected async creation with TMDB rate limiting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -919,7 +919,8 @@ class AnimeService:
|
|||||||
name=anime_series.name,
|
name=anime_series.name,
|
||||||
site=anime_series.site,
|
site=anime_series.site,
|
||||||
folder=anime_series.folder,
|
folder=anime_series.folder,
|
||||||
episodeDict=episode_dict
|
episodeDict=episode_dict,
|
||||||
|
year=anime_series.year
|
||||||
)
|
)
|
||||||
series_list.append(serie)
|
series_list.append(serie)
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,36 @@ _POSTER_DOWNLOAD_SEMAPHORE: asyncio.Semaphore = asyncio.Semaphore(3)
|
|||||||
_NFO_REPAIR_SEMAPHORE: asyncio.Semaphore = asyncio.Semaphore(3)
|
_NFO_REPAIR_SEMAPHORE: asyncio.Semaphore = asyncio.Semaphore(3)
|
||||||
|
|
||||||
|
|
||||||
|
async def _create_missing_nfo(series_dir: Path, series_name: str) -> None:
|
||||||
|
"""Create minimal NFO for series without one.
|
||||||
|
|
||||||
|
Creates a fresh :class:`NFOService` per invocation so concurrent
|
||||||
|
tasks cannot interfere with each other.
|
||||||
|
|
||||||
|
A module-level semaphore limits concurrent TMDB operations to 3.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
series_dir: Absolute path to the series folder.
|
||||||
|
series_name: Human-readable series name for log messages.
|
||||||
|
"""
|
||||||
|
from src.core.services.nfo_factory import NFOServiceFactory
|
||||||
|
|
||||||
|
async with _NFO_REPAIR_SEMAPHORE:
|
||||||
|
try:
|
||||||
|
factory = NFOServiceFactory()
|
||||||
|
nfo_service = factory.create()
|
||||||
|
await nfo_service.create_minimal_nfo(
|
||||||
|
serie_name=series_name,
|
||||||
|
serie_folder=series_dir.name,
|
||||||
|
)
|
||||||
|
except Exception as exc: # pylint: disable=broad-except
|
||||||
|
logger.error(
|
||||||
|
"NFO creation failed for %s: %s",
|
||||||
|
series_name,
|
||||||
|
exc,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _repair_one_series(series_dir: Path, series_name: str) -> None:
|
async def _repair_one_series(series_dir: Path, series_name: str) -> None:
|
||||||
"""Repair a single series NFO in isolation.
|
"""Repair a single series NFO in isolation.
|
||||||
|
|
||||||
@@ -63,12 +93,13 @@ async def _repair_one_series(series_dir: Path, series_name: str) -> None:
|
|||||||
|
|
||||||
|
|
||||||
async def perform_nfo_repair_scan(background_loader=None) -> None:
|
async def perform_nfo_repair_scan(background_loader=None) -> None:
|
||||||
"""Scan all series folders and repair incomplete tvshow.nfo files.
|
"""Scan all series folders, repair incomplete and create missing NFO files.
|
||||||
|
|
||||||
Called from ``FolderScanService.run_folder_scan()`` during the scheduled
|
Called from ``FolderScanService.run_folder_scan()`` during the scheduled
|
||||||
daily folder scan (not on every startup). Checks each subfolder of
|
daily folder scan (not on every startup). Checks each subfolder of
|
||||||
``settings.anime_directory`` for a ``tvshow.nfo`` and calls
|
``settings.anime_directory`` for a ``tvshow.nfo``:
|
||||||
``_repair_one_series`` for every file with absent or empty required tags.
|
- Missing NFOs: creates minimal NFO via ``_create_missing_nfo``
|
||||||
|
- Incomplete NFOs: repairs via ``_repair_one_series``
|
||||||
|
|
||||||
Each repair task creates its own isolated :class:`NFOService` /
|
Each repair task creates its own isolated :class:`NFOService` /
|
||||||
:class:`TMDBClient` so concurrent tasks never share an ``aiohttp``
|
:class:`TMDBClient` so concurrent tasks never share an ``aiohttp``
|
||||||
@@ -97,26 +128,33 @@ async def perform_nfo_repair_scan(background_loader=None) -> None:
|
|||||||
|
|
||||||
queued = 0
|
queued = 0
|
||||||
total = 0
|
total = 0
|
||||||
|
missing_nfo_count = 0
|
||||||
for series_dir in sorted(anime_dir.iterdir()):
|
for series_dir in sorted(anime_dir.iterdir()):
|
||||||
if not series_dir.is_dir():
|
if not series_dir.is_dir():
|
||||||
continue
|
continue
|
||||||
nfo_path = series_dir / "tvshow.nfo"
|
nfo_path = series_dir / "tvshow.nfo"
|
||||||
|
series_name = series_dir.name
|
||||||
if not nfo_path.exists():
|
if not nfo_path.exists():
|
||||||
|
# Create minimal NFO for series without one
|
||||||
|
missing_nfo_count += 1
|
||||||
|
asyncio.create_task(
|
||||||
|
_create_missing_nfo(series_dir, series_name),
|
||||||
|
name=f"nfo_create:{series_name}",
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
total += 1
|
total += 1
|
||||||
series_name = series_dir.name
|
|
||||||
if nfo_needs_repair(nfo_path):
|
if nfo_needs_repair(nfo_path):
|
||||||
queued += 1
|
queued += 1
|
||||||
# Each task creates its own NFOService so connectors are isolated.
|
|
||||||
asyncio.create_task(
|
asyncio.create_task(
|
||||||
_repair_one_series(series_dir, series_name),
|
_repair_one_series(series_dir, series_name),
|
||||||
name=f"nfo_repair:{series_name}",
|
name=f"nfo_repair:{series_name}",
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"NFO repair scan complete: %d of %d series queued for repair",
|
"NFO repair scan complete: %d of %d series queued for repair, %d missing NFOs queued for creation",
|
||||||
queued,
|
queued,
|
||||||
total,
|
total,
|
||||||
|
missing_nfo_count,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user