From d8f304c35208dbf576a34ca1cdf4980e4e545d08 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 8 Sep 2025 08:57:29 -0400 Subject: [PATCH] updates --- webhook_server.py | 156 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 132 insertions(+), 24 deletions(-) diff --git a/webhook_server.py b/webhook_server.py index a8fd660..03ae738 100644 --- a/webhook_server.py +++ b/webhook_server.py @@ -348,23 +348,28 @@ class RadarrClient: imdb_id = imdb_id if imdb_id.startswith("tt") else f"tt{imdb_id}" _log("DEBUG", f"Radarr API: Looking up movie by IMDb ID: {imdb_id}") - # Radarr's IMDb filtering is unreliable, so fetch all movies and filter client-side - res = self._get("/api/v3/movie") - if isinstance(res, list): - # Filter results to find exact IMDb ID match + # Try direct movie search with IMDb filter (even if unreliable) + res = self._get("/api/v3/movie", {"imdbId": imdb_id}) + if isinstance(res, list) and len(res) > 0: + # CRITICAL: Validate that returned movie actually matches requested IMDb ID for movie in res: - if movie.get("imdbId") == imdb_id: + movie_imdb = movie.get("imdbId", "").lower() + if movie_imdb == imdb_id.lower(): _log("DEBUG", f"Radarr API: Found exact match: {movie.get('title')} (ID: {movie.get('id')})") return movie - _log("WARNING", f"Radarr API: Searched {len(res)} movies but none match IMDb {imdb_id}") + + # Log the mismatch for debugging + returned_ids = [m.get("imdbId", "None") for m in res[:3]] # Show first 3 + _log("ERROR", f"Radarr API returned WRONG movies for {imdb_id}! Got: {returned_ids}") + _log("DEBUG", f"Will reject mismatched results and try lookup endpoint") - # Fallback: Try lookup endpoint + # Fallback: Try lookup endpoint which might be more reliable res = self._get("/api/v3/movie/lookup/imdb", {"imdbId": imdb_id}) - if isinstance(res, dict) and res.get("id") and res.get("imdbId") == imdb_id: + if isinstance(res, dict) and res.get("imdbId", "").lower() == imdb_id.lower(): _log("DEBUG", f"Radarr API: Found movie via lookup: {res.get('title')} (ID: {res.get('id')})") return res - _log("DEBUG", f"Radarr API: No movie found for IMDb ID: {imdb_id}") + _log("WARNING", f"Radarr API: No valid movie found for IMDb ID: {imdb_id}") return None def movie_files(self, movie_id: int) -> List[Dict[str, Any]]: @@ -394,6 +399,8 @@ class RadarrClient: page = 1 page_size = 1000 + _log("INFO", f"Starting history lookup for movie_id {movie_id}") + while True: data = self._get("/api/v3/history", { "movieId": movie_id, "page": page, "pageSize": page_size, @@ -419,11 +426,23 @@ class RadarrClient: _log("WARNING", f"Stopped pagination at page 10 for movie_id {movie_id} - may have missed some history") break - _log("DEBUG", f"Found {len(all_items)} total history items across {page} pages for movie_id {movie_id}") - items = all_items + _log("INFO", f"Found {len(all_items)} total history items across {page} pages for movie_id {movie_id}") + + # Log ALL events first for debugging + _log("INFO", f"=== COMPLETE HISTORY ANALYSIS for movie_id {movie_id} ===") + for i, it in enumerate(all_items): + ev = (it.get("eventType") or it.get("type") or "").lower() + date_str = it.get("date", "NO_DATE") + event_data = it.get('data', {}) + source_path = event_data.get('sourcePath', '') or event_data.get('path', '') or event_data.get('sourceTitle', '') + + _log("INFO", f"Event #{i+1}: type='{ev}', date='{date_str}', sourcePath='{source_path}'") + _log("DEBUG", f" Full data: {json.dumps(event_data, indent=2) if event_data else 'No data'}") first_grab = None - for it in items: + earliest_real_import = None + + for it in all_items: ev = (it.get("eventType") or it.get("type") or "").lower() date_iso = None if it.get("date"): @@ -432,8 +451,6 @@ class RadarrClient: except Exception: date_iso = None - _log("DEBUG", f"History event: type='{ev}', date={date_iso}, data={it.get('data', {})}") - # Expanded event types for Radarr v4/v5 compatibility import_events = { "downloadfolderimported", "moviefileimported", "imported", @@ -443,27 +460,41 @@ class RadarrClient: if ev in import_events: # Check if this is a real import (from downloads) vs existing file rename event_data = it.get('data', {}) - source_path = event_data.get('sourcePath', '') or event_data.get('path', '') + source_path = event_data.get('sourcePath', '') or event_data.get('path', '') or event_data.get('sourceTitle', '') + + _log("INFO", f"Analyzing import event '{ev}' with source: '{source_path}'") # Real imports typically come from download folders - is_real_import = any(download_indicator in source_path.lower() for download_indicator in [ + download_indicators = [ '/downloads/', '/download/', '/completed/', '/nzbs/', '/torrents/', - 'sabnzbd', 'nzbget', 'deluge', 'qbittorrent', 'transmission' - ]) + 'sabnzbd', 'nzbget', 'deluge', 'qbittorrent', 'transmission', + '/radarr/', 'completed/' # Add more specific patterns + ] + + is_real_import = any(indicator in source_path.lower() for indicator in download_indicators) + + _log("INFO", f"Import analysis: is_real_import={is_real_import}, date={date_iso}") if is_real_import and date_iso: - _log("INFO", f"Found REAL import event '{ev}' from downloads at {date_iso} (source: {source_path})") - return date_iso + _log("INFO", f"✅ FOUND REAL IMPORT EVENT '{ev}' from downloads at {date_iso} (source: {source_path})") + if earliest_real_import is None: + earliest_real_import = date_iso elif date_iso: - _log("DEBUG", f"Skipping non-import event '{ev}' (rename/existing file) from {source_path}") - if ev == "grabbed" and date_iso and first_grab is None: + _log("INFO", f"⚠️ Skipping non-download import '{ev}' from {source_path}") + + elif ev == "grabbed" and date_iso and first_grab is None: first_grab = date_iso + _log("INFO", f"Found grab event at {date_iso}") + if earliest_real_import: + _log("INFO", f"✅ USING EARLIEST REAL IMPORT DATE: {earliest_real_import}") + return earliest_real_import + if first_grab: - _log("INFO", f"Using grab date {first_grab} as fallback for movie_id {movie_id}") + _log("INFO", f"⚠️ Using grab date {first_grab} as fallback for movie_id {movie_id}") return first_grab - _log("WARNING", f"No real import/grab events found for movie_id {movie_id} (only renames/existing files)") + _log("ERROR", f"❌ No real import/grab events found for movie_id {movie_id}") return None # --------------------------- @@ -1650,6 +1681,83 @@ async def get_stats(): async def get_batch_status(): return batcher.get_status() +@app.get("/debug/movie/{imdb_id}") +async def debug_movie_import_date(imdb_id: str): + """Debug endpoint to analyze movie import date detection""" + try: + if not imdb_id.startswith("tt"): + imdb_id = f"tt{imdb_id}" + + _log("INFO", f"=== DEBUG MOVIE IMPORT DATE: {imdb_id} ===") + + if not (config.radarr_url and config.radarr_api_key): + return { + "error": "Radarr not configured", + "imdb_id": imdb_id, + "radarr_configured": False + } + + # Create Radarr client + rc = RadarrClient(config.radarr_url, config.radarr_api_key, config.radarr_timeout, config.radarr_retries) + + # Look up movie + movie_obj = rc.movie_by_imdb(imdb_id) + if not movie_obj: + return { + "error": f"Movie not found in Radarr for IMDb ID {imdb_id}", + "imdb_id": imdb_id, + "radarr_configured": True, + "movie_found": False + } + + movie_id = movie_obj.get("id") + movie_title = movie_obj.get("title") + + _log("INFO", f"Found movie: {movie_title} (Radarr ID: {movie_id})") + + # Get import history with detailed logging + import_date = rc.earliest_import_event(movie_id) + + # Get movie files for additional context + movie_files = rc.movie_files(movie_id) + file_dates = [] + for f in movie_files: + file_dates.append({ + "id": f.get("id"), + "relativePath": f.get("relativePath"), + "dateAdded": f.get("dateAdded"), + "quality": f.get("quality", {}).get("quality", {}).get("name", "Unknown") + }) + + return { + "imdb_id": imdb_id, + "radarr_configured": True, + "movie_found": True, + "movie_title": movie_title, + "movie_id": movie_id, + "detected_import_date": import_date, + "movie_files": file_dates, + "analysis": { + "import_date_found": import_date is not None, + "expected_july_7_2025": "2025-07-07" in (import_date or ""), + "method_used": "radarr:history.import" if import_date else "none" + }, + "debug_info": { + "radarr_url": config.radarr_url, + "movie_digital_release": movie_obj.get("digitalRelease"), + "movie_in_cinemas": movie_obj.get("inCinemas"), + "movie_physical_release": movie_obj.get("physicalRelease") + } + } + + except Exception as e: + _log("ERROR", f"Debug endpoint error for {imdb_id}: {e}") + return { + "error": str(e), + "imdb_id": imdb_id, + "success": False + } + # --------------------------- # Manual scans # ---------------------------