This commit is contained in:
+3
-3
@@ -862,8 +862,8 @@ async def manual_scan(background_tasks: BackgroundTasks, path: Optional[str] = N
|
|||||||
try:
|
try:
|
||||||
# Determine force_scan based on scan mode
|
# Determine force_scan based on scan mode
|
||||||
force_scan = (scan_mode == "full")
|
force_scan = (scan_mode == "full")
|
||||||
print(f"DEBUG: Processing series {scan_path} with 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)
|
result = tv_processor.process_series(scan_path, force_scan=force_scan, scan_mode=scan_mode)
|
||||||
tv_series_total += 1
|
tv_series_total += 1
|
||||||
if result == "skipped":
|
if result == "skipped":
|
||||||
tv_series_skipped += 1
|
tv_series_skipped += 1
|
||||||
@@ -928,7 +928,7 @@ async def manual_scan(background_tasks: BackgroundTasks, path: Optional[str] = N
|
|||||||
try:
|
try:
|
||||||
# Determine force_scan based on scan mode
|
# Determine force_scan based on scan mode
|
||||||
force_scan = (scan_mode == "full")
|
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
|
tv_series_total += 1
|
||||||
if result == "skipped":
|
if result == "skipped":
|
||||||
tv_series_skipped += 1
|
tv_series_skipped += 1
|
||||||
|
|||||||
+134
-4
@@ -145,7 +145,7 @@ class TVProcessor:
|
|||||||
_log("ERROR", f"Error checking series completion for {imdb_id}: {e}")
|
_log("ERROR", f"Error checking series completion for {imdb_id}: {e}")
|
||||||
return False, f"Error checking completion: {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"""
|
"""Process a TV series directory"""
|
||||||
imdb_id = self.nfo_manager.parse_imdb_from_path(series_path)
|
imdb_id = self.nfo_manager.parse_imdb_from_path(series_path)
|
||||||
if not imdb_id:
|
if not imdb_id:
|
||||||
@@ -226,12 +226,17 @@ class TVProcessor:
|
|||||||
return extract_title_from_directory_name(series_path.name)
|
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]]:
|
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 database-first optimization"""
|
"""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")
|
_log("INFO", f"🎯 GATHERING EPISODE DATES for {imdb_id}: {len(disk_episodes)} episodes on disk (mode: {scan_mode})")
|
||||||
episode_dates = {}
|
episode_dates = {}
|
||||||
episodes_needing_lookup = []
|
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)
|
# 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")
|
_log("DEBUG", f"TIER 1 - Checking database for existing episode dates for {len(disk_episodes)} episodes")
|
||||||
db_cache_hits = 0
|
db_cache_hits = 0
|
||||||
@@ -378,6 +383,131 @@ class TVProcessor:
|
|||||||
|
|
||||||
return episode_dates
|
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]]:
|
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"""
|
"""Get episode information from Sonarr including import history - optimized to only fetch needed episodes"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user