diff --git a/api/routes.py b/api/routes.py index a1f8876..e46409d 100644 --- a/api/routes.py +++ b/api/routes.py @@ -135,6 +135,7 @@ async def sonarr_webhook(request: Request, background_tasks: BackgroundTasks, de "episodeNumber": episode_num, "id": episode_file.get("id"), "title": episode_file.get("title") + # Note: Not including dateAdded - we use database-first approach with Sonarr fallback }] print(f"INFO: Extracted episode info from episodeFile for {webhook.eventType}: S{season_num:02d}E{episode_num:02d}") else: diff --git a/processors/tv_processor.py b/processors/tv_processor.py index 957f7c6..3859fd7 100644 --- a/processors/tv_processor.py +++ b/processors/tv_processor.py @@ -797,7 +797,7 @@ class TVProcessor: # Get episode date information - webhook processing prioritizes existing DB entries _log("DEBUG", f"Processing webhook episode: IMDb={imdb_id}, S{season_num:02d}E{episode_num:02d}") - aired, dateadded, source = self._get_webhook_episode_date(imdb_id, season_num, episode_num, series_metadata) + aired, dateadded, source = self._get_webhook_episode_date(imdb_id, season_num, episode_num, series_metadata, webhook_episode) enhanced_metadata = self._get_episode_metadata(series_metadata, season_num, episode_num, season_dir) if series_metadata else self._get_episode_metadata(None, season_num, episode_num, season_dir) # Create NFO @@ -942,17 +942,46 @@ class TVProcessor: return None - def _get_webhook_episode_date(self, imdb_id: str, season_num: int, episode_num: int, series_metadata: Optional[Dict[str, Any]] = None) -> Tuple[Optional[str], Optional[str], str]: + def _get_webhook_episode_date(self, imdb_id: str, season_num: int, episode_num: int, series_metadata: Optional[Dict[str, Any]] = None, webhook_episode: Optional[Dict[str, Any]] = None) -> Tuple[Optional[str], Optional[str], str]: """ - Get episode date for webhook processing - avoid treating webhook as gospel. + Get episode date for webhook processing - database-first approach. Logic: - 1. Check Sonarr import history FIRST (any history = show has existed for a while) - 2. If ANY import history exists → Use air date (webhook is likely upgrade/rename) - 3. If NO import history → Use webhook date (truly new show) + 1. Check NFOGuard database FIRST - if we have a date, use it (no re-processing) + 2. No date in DB? Check Sonarr import history with our rules: + - Import before rename = use import date + - Rename before import = use airdate + - First import matches webhook = valid new episode + 3. Final fallback = airdate - This prevents upgrades from overriding dates for shows you've had for months/years. + This prevents webhooks from overriding existing good data. """ + + # STEP 1: Check NFOGuard database first + existing_entry = None + existing_date = None + try: + existing_entry = self.db.get_episode(imdb_id, season_num, episode_num) + if existing_entry and existing_entry.get('date_added'): + existing_date = existing_entry['date_added'] + _log("INFO", f"Found existing date in database for S{season_num:02d}E{episode_num:02d}: {existing_date}") + + # For webhook processing, use existing data (fast path) + # Manual scans will validate below + # Still get aired date for NFO + aired = None + if series_metadata and 'episodes' in series_metadata: + episode_data = series_metadata['episodes'].get((season_num, episode_num)) + if episode_data: + aired = episode_data.get('airDate') + + return aired, str(existing_date), "database:existing" + + except Exception as e: + _log("DEBUG", f"Database check failed for S{season_num:02d}E{episode_num:02d}: {e}") + + _log("INFO", f"No existing date in database for S{season_num:02d}E{episode_num:02d}, checking Sonarr import history") + # Get aired date and episode ID from Sonarr aired = None episode_id = None @@ -963,8 +992,8 @@ class TVProcessor: episode_id = episode_data.get('id') _log("DEBUG", f"Got aired date from Sonarr for S{season_num:02d}E{episode_num:02d}: {aired}") - # STEP 1: Check Sonarr import history FIRST (this is the key check) - _log("INFO", f"Checking Sonarr import history for S{season_num:02d}E{episode_num:02d} to detect import vs rename events") + # STEP 2: Check Sonarr import history (authoritative source) + _log("INFO", f"Checking Sonarr import history for S{season_num:02d}E{episode_num:02d}") if episode_id and hasattr(self, 'sonarr'): try: @@ -973,9 +1002,8 @@ class TVProcessor: _log("DEBUG", f"Import history result: {import_history}") if import_history: - # Found actual import event - use this date - _log("INFO", f"Found real import event for S{season_num:02d}E{episode_num:02d}: {import_history}") - _log("INFO", f"Using first import date (not webhook): {import_history}") + # Found actual import event from Sonarr + _log("INFO", f"Found Sonarr import event for S{season_num:02d}E{episode_num:02d}: {import_history}") return aired, import_history, "sonarr:import_history" else: # No import events found - this means only renames/moves exist in history