This commit is contained in:
+3
-3
@@ -862,8 +862,8 @@ async def manual_scan(background_tasks: BackgroundTasks, path: Optional[str] = N
|
||||
try:
|
||||
# Determine force_scan based on scan mode
|
||||
force_scan = (scan_mode == "full")
|
||||
print(f"DEBUG: Processing series {scan_path} with force_scan={force_scan}")
|
||||
result = tv_processor.process_series(scan_path, force_scan=force_scan)
|
||||
print(f"DEBUG: Processing series {scan_path} with force_scan={force_scan}, scan_mode={scan_mode}")
|
||||
result = tv_processor.process_series(scan_path, force_scan=force_scan, scan_mode=scan_mode)
|
||||
tv_series_total += 1
|
||||
if result == "skipped":
|
||||
tv_series_skipped += 1
|
||||
@@ -928,7 +928,7 @@ async def manual_scan(background_tasks: BackgroundTasks, path: Optional[str] = N
|
||||
try:
|
||||
# Determine force_scan based on scan mode
|
||||
force_scan = (scan_mode == "full")
|
||||
result = tv_processor.process_series(item, force_scan=force_scan)
|
||||
result = tv_processor.process_series(item, force_scan=force_scan, scan_mode=scan_mode)
|
||||
tv_series_total += 1
|
||||
if result == "skipped":
|
||||
tv_series_skipped += 1
|
||||
|
||||
+134
-4
@@ -145,7 +145,7 @@ class TVProcessor:
|
||||
_log("ERROR", f"Error checking series completion for {imdb_id}: {e}")
|
||||
return False, f"Error checking completion: {e}"
|
||||
|
||||
def process_series(self, series_path: Path, force_scan: bool = False) -> str:
|
||||
def process_series(self, series_path: Path, force_scan: bool = False, scan_mode: str = "smart") -> str:
|
||||
"""Process a TV series directory"""
|
||||
imdb_id = self.nfo_manager.parse_imdb_from_path(series_path)
|
||||
if not imdb_id:
|
||||
@@ -226,12 +226,17 @@ class TVProcessor:
|
||||
return extract_title_from_directory_name(series_path.name)
|
||||
|
||||
|
||||
def _gather_episode_dates(self, series_path: Path, imdb_id: str, disk_episodes: Dict[Tuple[int, int], List[Path]]) -> Dict[Tuple[int, int], Tuple[Optional[str], Optional[str], str]]:
|
||||
"""Gather episode air dates and date added information with database-first optimization"""
|
||||
_log("INFO", f"🎯 GATHERING EPISODE DATES for {imdb_id}: {len(disk_episodes)} episodes on disk")
|
||||
def _gather_episode_dates(self, series_path: Path, imdb_id: str, disk_episodes: Dict[Tuple[int, int], List[Path]], scan_mode: str = "smart") -> Dict[Tuple[int, int], Tuple[Optional[str], Optional[str], str]]:
|
||||
"""Gather episode air dates and date added information with optimization based on scan mode"""
|
||||
_log("INFO", f"🎯 GATHERING EPISODE DATES for {imdb_id}: {len(disk_episodes)} episodes on disk (mode: {scan_mode})")
|
||||
episode_dates = {}
|
||||
episodes_needing_lookup = []
|
||||
|
||||
# For incomplete mode: Start with NFO check to find missing dateadded elements
|
||||
if scan_mode == "incomplete":
|
||||
return self._gather_episode_dates_nfo_first(series_path, imdb_id, disk_episodes)
|
||||
|
||||
# For smart/full modes: Use database-first optimization
|
||||
# TIER 1: Check database first for existing dates (fastest)
|
||||
_log("DEBUG", f"TIER 1 - Checking database for existing episode dates for {len(disk_episodes)} episodes")
|
||||
db_cache_hits = 0
|
||||
@@ -378,6 +383,131 @@ class TVProcessor:
|
||||
|
||||
return episode_dates
|
||||
|
||||
def _gather_episode_dates_nfo_first(self, series_path: Path, imdb_id: str, disk_episodes: Dict[Tuple[int, int], List[Path]]) -> Dict[Tuple[int, int], Tuple[Optional[str], Optional[str], str]]:
|
||||
"""Gather episode dates for incomplete mode: Check NFO files first for missing dateadded elements"""
|
||||
_log("INFO", f"🔍 NFO-FIRST MODE: Checking {len(disk_episodes)} episodes for missing dateadded in NFO files")
|
||||
episode_dates = {}
|
||||
episodes_needing_db_check = []
|
||||
episodes_needing_api_lookup = []
|
||||
|
||||
# STEP 1: Check each NFO file to determine if dateadded is missing
|
||||
_log("DEBUG", f"STEP 1 - Checking NFO files for missing dateadded elements")
|
||||
nfo_complete_count = 0
|
||||
|
||||
for (season, episode) in disk_episodes:
|
||||
season_dir = series_path / config.get_season_dir_name(season)
|
||||
episode_files = disk_episodes[(season, episode)]
|
||||
|
||||
nfo_has_dateadded = False
|
||||
nfo_data = None
|
||||
|
||||
# Look for NFO file for this episode
|
||||
for episode_file in episode_files:
|
||||
nfo_path = episode_file.with_suffix('.nfo')
|
||||
if nfo_path.exists():
|
||||
# Extract NFOGuard data from episode NFO
|
||||
nfo_data = self.nfo_manager.extract_nfoguard_dates_from_nfo(nfo_path)
|
||||
if nfo_data and nfo_data.get('dateadded'):
|
||||
# NFO has dateadded - this episode is complete
|
||||
aired = nfo_data.get('aired')
|
||||
dateadded = nfo_data.get('dateadded')
|
||||
source = nfo_data.get('source', 'nfo_cache')
|
||||
episode_dates[(season, episode)] = (aired, dateadded, source)
|
||||
nfo_complete_count += 1
|
||||
nfo_has_dateadded = True
|
||||
_log("DEBUG", f"S{season:02d}E{episode:02d}: NFO has dateadded={dateadded}, marked as complete")
|
||||
break
|
||||
|
||||
if not nfo_has_dateadded:
|
||||
# NFO missing or missing dateadded - needs further processing
|
||||
if nfo_data:
|
||||
# NFO exists but missing dateadded
|
||||
_log("DEBUG", f"S{season:02d}E{episode:02d}: NFO exists but missing dateadded - needs DB/API lookup")
|
||||
else:
|
||||
# No NFO file found
|
||||
_log("DEBUG", f"S{season:02d}E{episode:02d}: No NFO file found - needs DB/API lookup")
|
||||
episodes_needing_db_check.append((season, episode))
|
||||
|
||||
_log("INFO", f"NFO files with dateadded: {nfo_complete_count}/{len(disk_episodes)} episodes. Need DB check: {len(episodes_needing_db_check)}")
|
||||
|
||||
# STEP 2: For episodes missing dateadded in NFO, check database
|
||||
if episodes_needing_db_check:
|
||||
_log("DEBUG", f"STEP 2 - Checking database for {len(episodes_needing_db_check)} episodes missing dateadded")
|
||||
db_hits = 0
|
||||
|
||||
for (season, episode) in episodes_needing_db_check:
|
||||
db_result = self.db.get_episode_date(imdb_id, season, episode)
|
||||
|
||||
if db_result and db_result.get('dateadded'):
|
||||
# Found in database - use cached data and will add to NFO later
|
||||
aired = db_result.get('aired')
|
||||
dateadded = db_result.get('dateadded')
|
||||
source = db_result.get('source', 'database_cache')
|
||||
episode_dates[(season, episode)] = (aired, dateadded, source)
|
||||
db_hits += 1
|
||||
_log("DEBUG", f"S{season:02d}E{episode:02d}: Database has dateadded={dateadded} - will add to NFO")
|
||||
else:
|
||||
# Not in database or incomplete - needs API lookup
|
||||
episodes_needing_api_lookup.append((season, episode))
|
||||
|
||||
_log("INFO", f"Database hits: {db_hits}/{len(episodes_needing_db_check)} episodes. Need API lookup: {len(episodes_needing_api_lookup)}")
|
||||
|
||||
# STEP 3: For episodes still missing dateadded, query Sonarr
|
||||
if episodes_needing_api_lookup:
|
||||
_log("DEBUG", f"STEP 3 - Querying Sonarr for {len(episodes_needing_api_lookup)} episodes missing from NFO and database")
|
||||
sonarr_episodes = self._get_sonarr_episodes(imdb_id, episodes_needing_api_lookup)
|
||||
|
||||
for (season, episode) in episodes_needing_api_lookup:
|
||||
aired = None
|
||||
dateadded = None
|
||||
source = "unknown"
|
||||
|
||||
# Try Sonarr first
|
||||
if (season, episode) in sonarr_episodes:
|
||||
sonarr_data = sonarr_episodes[(season, episode)]
|
||||
aired = sonarr_data.get('airDate')
|
||||
dateadded = sonarr_data.get('dateAdded')
|
||||
|
||||
if dateadded:
|
||||
source = "sonarr:history.import"
|
||||
_log("DEBUG", f"S{season:02d}E{episode:02d}: Got Sonarr import date: {dateadded}")
|
||||
else:
|
||||
source = "sonarr:no_import_date"
|
||||
_log("DEBUG", f"S{season:02d}E{episode:02d}: Sonarr has data but no dateAdded (aired: {aired})")
|
||||
|
||||
# Fallback to external sources if needed
|
||||
if not aired:
|
||||
_log("DEBUG", f"S{season:02d}E{episode:02d}: No aired date from Sonarr, trying external APIs")
|
||||
external_aired = self.external_clients.get_episode_air_date(imdb_id, season, episode)
|
||||
if external_aired:
|
||||
aired = external_aired
|
||||
if not dateadded:
|
||||
source = "external"
|
||||
_log("INFO", f"S{season:02d}E{episode:02d}: Found aired date from external APIs: {aired}")
|
||||
|
||||
# Use air date as fallback for dateadded if no import date found
|
||||
if not dateadded and aired:
|
||||
dateadded = aired
|
||||
if source == "sonarr:no_import_date":
|
||||
source = "sonarr:aired_fallback"
|
||||
elif source == "external":
|
||||
source = "external:aired_fallback"
|
||||
else:
|
||||
source = f"{source}_fallback" if source != "unknown" else "aired_fallback"
|
||||
_log("DEBUG", f"S{season:02d}E{episode:02d}: Using aired date as dateadded fallback: {dateadded}")
|
||||
|
||||
# Save to database for future lookups
|
||||
if dateadded or aired:
|
||||
self.db.upsert_episode_date(imdb_id, season, episode, aired, dateadded, source, True)
|
||||
|
||||
episode_dates[(season, episode)] = (aired, dateadded, source)
|
||||
|
||||
_log("INFO", f"🔍 NFO-FIRST COMPLETE: {len(episode_dates)} episodes processed")
|
||||
for (s, e), (aired, dateadded, source) in episode_dates.items():
|
||||
_log("INFO", f" S{s:02d}E{e:02d}: aired={aired}, dateadded={dateadded}, source={source}")
|
||||
|
||||
return episode_dates
|
||||
|
||||
def _get_sonarr_episodes(self, imdb_id: str, episodes_filter: List[Tuple[int, int]] = None) -> Dict[Tuple[int, int], Dict[str, Any]]:
|
||||
"""Get episode information from Sonarr including import history - optimized to only fetch needed episodes"""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user