fix: ensure series loaded from DB before NFO scan
- Call _load_series_into_memory() before NFO scan phases to sync DB to SeriesApp memory, fixing missing NFO for recently resolved folders - Add TMDB lookup for series without cached tmdb_id during NFO creation - Add get_tmdb_client() factory and get_tmdb_image_base_url() helpers - Fix: use get_tv_show_details instead of deprecated get_series_details - Fix tests: mock _load_series_into_memory in NFO scan tests
This commit is contained in:
@@ -422,3 +422,32 @@ class TMDBClient:
|
||||
if expired_keys:
|
||||
logger.debug("Removed %d expired negative cache entries", len(expired_keys))
|
||||
return len(expired_keys)
|
||||
|
||||
|
||||
def get_tmdb_client() -> TMDBClient:
|
||||
"""Factory function to create a TMDBClient with settings configuration.
|
||||
|
||||
Returns:
|
||||
TMDBClient instance configured with settings.tmdb_api_key
|
||||
|
||||
Raises:
|
||||
ValueError: If TMDB API key is not configured
|
||||
"""
|
||||
from src.config.settings import settings
|
||||
|
||||
if not settings.tmdb_api_key:
|
||||
raise ValueError("TMDB API key is not configured")
|
||||
|
||||
return TMDBClient(api_key=settings.tmdb_api_key)
|
||||
|
||||
|
||||
def get_tmdb_image_base_url(tmdb_id: int) -> str:
|
||||
"""Get the base URL for TMDB images.
|
||||
|
||||
Args:
|
||||
tmdb_id: TMDB show ID (used for account-specific URLs)
|
||||
|
||||
Returns:
|
||||
Base URL string for TMDB images
|
||||
"""
|
||||
return "https://image.tmdb.org/t/p/"
|
||||
|
||||
@@ -541,6 +541,8 @@ async def perform_nfo_scan_if_needed(progress_service=None):
|
||||
|
||||
# Execute the NFO scan
|
||||
try:
|
||||
# Ensure any newly created series are loaded from DB into SeriesApp memory
|
||||
await _load_series_into_memory(progress_service=None)
|
||||
await _execute_nfo_scan(progress_service)
|
||||
await _mark_nfo_scan_completed()
|
||||
except Exception as e:
|
||||
@@ -615,6 +617,9 @@ async def perform_nfo_scan_phase(progress_service=None):
|
||||
|
||||
# Execute the NFO scan
|
||||
try:
|
||||
# Ensure any newly created series (e.g., from resolving unresolved folders)
|
||||
# are loaded from DB into SeriesApp memory before scanning
|
||||
await _load_series_into_memory(progress_service=None)
|
||||
await _execute_nfo_scan(progress_service)
|
||||
await _mark_nfo_scan_completed()
|
||||
|
||||
|
||||
@@ -326,6 +326,22 @@ class NfoScanService:
|
||||
|
||||
nfo_exists = os.path.isfile(nfo_path)
|
||||
|
||||
# If tmdb_id is missing, try to look it up by series name
|
||||
if not series_data.get("tmdb_id"):
|
||||
logger.debug("No tmdb_id for %s — attempting TMDB lookup", key)
|
||||
name = series_data.get("name", "")
|
||||
found_tmdb_id = await self._lookup_tmdb_id_by_name(name)
|
||||
if found_tmdb_id:
|
||||
series_data["tmdb_id"] = found_tmdb_id
|
||||
await self._save_tmdb_id(key, found_tmdb_id)
|
||||
logger.info("Found and saved tmdb_id %s for %s", found_tmdb_id, key)
|
||||
else:
|
||||
logger.warning(
|
||||
"Could not resolve tmdb_id for %s (%s)",
|
||||
key,
|
||||
name,
|
||||
)
|
||||
|
||||
if not nfo_exists:
|
||||
# Create new NFO
|
||||
logger.info("Creating NFO for series: %s (%s)", key, folder)
|
||||
@@ -526,6 +542,53 @@ class NfoScanService:
|
||||
logger.info("Regenerated NFO for %s", key)
|
||||
return True
|
||||
|
||||
async def _save_tmdb_id(self, key: str, tmdb_id: int) -> None:
|
||||
"""Save tmdb_id to the database for a series.
|
||||
|
||||
Args:
|
||||
key: Series key (primary identifier)
|
||||
tmdb_id: TMDB series ID to save
|
||||
"""
|
||||
try:
|
||||
from src.server.database.connection import get_db_session
|
||||
from src.server.database.service import AnimeSeriesService
|
||||
|
||||
async with get_db_session() as db:
|
||||
series = await AnimeSeriesService.get_by_key(db, key)
|
||||
if series:
|
||||
series.tmdb_id = tmdb_id
|
||||
await db.flush()
|
||||
logger.debug("Saved tmdb_id %s for series: %s", tmdb_id, key)
|
||||
else:
|
||||
logger.warning("Series not found for tmdb_id save: %s", key)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to save tmdb_id for %s: %s", key, exc)
|
||||
|
||||
async def _lookup_tmdb_id_by_name(self, name: str) -> Optional[int]:
|
||||
"""Look up a TMDB series ID by series name.
|
||||
|
||||
Args:
|
||||
name: Series name to search for
|
||||
|
||||
Returns:
|
||||
TMDB series ID or None if not found.
|
||||
"""
|
||||
if not name:
|
||||
return None
|
||||
|
||||
try:
|
||||
from src.server.nfo.tmdb_client import get_tmdb_client
|
||||
|
||||
client = get_tmdb_client()
|
||||
results = await client.search_tv_show(name)
|
||||
if results and results.get("results"):
|
||||
first_result = results["results"][0]
|
||||
return first_result.get("id")
|
||||
return None
|
||||
except Exception as exc:
|
||||
logger.warning("TMDB lookup failed for %s: %s", name, exc)
|
||||
return None
|
||||
|
||||
async def _fetch_tmdb_data(self, tmdb_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""Fetch series metadata from TMDB API.
|
||||
|
||||
@@ -539,7 +602,7 @@ class NfoScanService:
|
||||
from src.server.nfo.tmdb_client import get_tmdb_client
|
||||
|
||||
client = get_tmdb_client()
|
||||
data = await client.get_series_details(tmdb_id)
|
||||
data = await client.get_tv_show_details(tmdb_id)
|
||||
return data
|
||||
except Exception as exc:
|
||||
logger.warning("TMDB fetch failed for TMDB ID %s: %s", tmdb_id, exc)
|
||||
|
||||
@@ -532,6 +532,8 @@ class TestPerformNFOScan:
|
||||
new_callable=AsyncMock, return_value=False), \
|
||||
patch('src.server.services.initialization_service._is_nfo_scan_configured',
|
||||
new_callable=AsyncMock, return_value=True), \
|
||||
patch('src.server.services.initialization_service._load_series_into_memory',
|
||||
new_callable=AsyncMock), \
|
||||
patch('src.server.services.initialization_service._execute_nfo_scan',
|
||||
new_callable=AsyncMock), \
|
||||
patch('src.server.services.initialization_service._mark_nfo_scan_completed',
|
||||
@@ -549,6 +551,8 @@ class TestPerformNFOScan:
|
||||
new_callable=AsyncMock, return_value=False), \
|
||||
patch('src.server.services.initialization_service._is_nfo_scan_configured',
|
||||
new_callable=AsyncMock, return_value=True), \
|
||||
patch('src.server.services.initialization_service._load_series_into_memory',
|
||||
new_callable=AsyncMock), \
|
||||
patch('src.server.services.initialization_service._execute_nfo_scan',
|
||||
new_callable=AsyncMock), \
|
||||
patch('src.server.services.initialization_service._mark_nfo_scan_completed',
|
||||
|
||||
Reference in New Issue
Block a user