fix: isolate movie and TV webhook processing to prevent cross-contamination
- Add prefixed batch keys (movie:imdbid, tv:imdbid) to prevent IMDb ID collisions - Add path existence validation for Radarr webhooks to reject invalid mappings early - Remove duplicate Radarr webhook handler code - Add debug scripts for troubleshooting webhook and path mapping issues - Create corrected .env template with fixed TV_PATHS and SONARR_ROOT_FOLDERS This fixes the issue where TV path mapping failures caused movie webhooks to process wrong movies due to shared batch queue corruption.
This commit is contained in:
+46
-43
@@ -942,7 +942,7 @@ class MovieProcessor:
|
||||
_log("ERROR", f"No IMDb ID found in movie path: {movie_path}")
|
||||
return
|
||||
|
||||
_log("INFO", f"Processing movie: {movie_path.name}")
|
||||
_log("INFO", f"Processing movie: {movie_path.name} (IMDb: {imdb_id})")
|
||||
|
||||
# Update database
|
||||
self.db.upsert_movie(imdb_id, str(movie_path))
|
||||
@@ -1259,6 +1259,7 @@ class WebhookBatcher:
|
||||
webhook_data['media_type'] = media_type
|
||||
self.pending[key] = webhook_data
|
||||
_log("INFO", f"Batched {media_type} webhook for {key}")
|
||||
_log("DEBUG", f"Batch added - key: {key}, media_type: {media_type}, timer scheduled for {config.batch_delay}s")
|
||||
|
||||
timer = threading.Timer(config.batch_delay, self._process_item, args=[key])
|
||||
self.timers[key] = timer
|
||||
@@ -1424,7 +1425,8 @@ async def sonarr_webhook(request: Request, background_tasks: BackgroundTasks):
|
||||
_log("ERROR", f"Could not find series directory: {series_title} ({imdb_id})")
|
||||
return {"status": "error", "reason": "Series directory not found"}
|
||||
|
||||
# Add to batch queue
|
||||
# Add to batch queue with TV-prefixed key to avoid movie conflicts
|
||||
tv_batch_key = f"tv:{imdb_id}"
|
||||
webhook_dict = {
|
||||
'path': str(series_path),
|
||||
'series_info': series_info,
|
||||
@@ -1432,9 +1434,9 @@ async def sonarr_webhook(request: Request, background_tasks: BackgroundTasks):
|
||||
'episodes': webhook.episodes or [], # Include episode data for targeted processing
|
||||
'processing_mode': config.tv_webhook_processing_mode
|
||||
}
|
||||
batcher.add_webhook(imdb_id, webhook_dict, 'tv')
|
||||
batcher.add_webhook(tv_batch_key, webhook_dict, 'tv')
|
||||
|
||||
return {"status": "accepted", "message": "Sonarr webhook queued"}
|
||||
return {"status": "accepted", "message": f"Sonarr webhook queued for {tv_batch_key}"}
|
||||
|
||||
except Exception as e:
|
||||
_log("ERROR", f"Sonarr webhook error: {e}")
|
||||
@@ -1445,55 +1447,56 @@ async def radarr_webhook(request: Request, background_tasks: BackgroundTasks):
|
||||
"""Handle Radarr webhooks"""
|
||||
try:
|
||||
payload = await _read_payload(request)
|
||||
if not payload:
|
||||
raise HTTPException(status_code=422, detail="Empty Radarr payload")
|
||||
_log("INFO", f"Received Radarr webhook: {payload.get('eventType', 'Unknown')}")
|
||||
_log("DEBUG", f"Full Radarr webhook payload: {payload}")
|
||||
|
||||
webhook = RadarrWebhook(**payload)
|
||||
_log("INFO", f"Received Radarr webhook: {webhook.eventType}")
|
||||
# Extract movie info
|
||||
movie_data = payload.get("movie", {})
|
||||
if not movie_data:
|
||||
_log("WARNING", "No movie data in Radarr webhook")
|
||||
return {"status": "error", "message": "No movie data"}
|
||||
|
||||
if webhook.eventType not in ["Download", "Upgrade", "Rename", "Test"]:
|
||||
return {"status": "ignored", "reason": f"Event type {webhook.eventType} not processed"}
|
||||
|
||||
if webhook.eventType == "Test":
|
||||
return {"status": "ok", "message": "Test received"}
|
||||
|
||||
if not webhook.movie:
|
||||
return {"status": "ignored", "reason": "No movie data"}
|
||||
|
||||
movie_info = webhook.movie
|
||||
movie_title = movie_info.get("title", "")
|
||||
imdb_id = (movie_info.get("imdbId") or "").strip()
|
||||
if imdb_id:
|
||||
imdb_id = f"tt{imdb_id.replace('tt','')}"
|
||||
radarr_path = movie_info.get("folderPath") or movie_info.get("path", "")
|
||||
|
||||
# Find movie path
|
||||
movie_path = movie_processor.find_movie_path(movie_title, imdb_id, radarr_path)
|
||||
if not movie_path:
|
||||
_log("ERROR", f"Could not find movie directory: {movie_title} ({imdb_id})")
|
||||
return {"status": "error", "reason": "Movie directory not found"}
|
||||
|
||||
# Extract IMDb ID from path if not in webhook
|
||||
# Get IMDb ID for batching key
|
||||
imdb_id = movie_data.get("imdbId", "").lower()
|
||||
if not imdb_id:
|
||||
imdb_id = nfo_manager.parse_imdb_from_path(movie_path)
|
||||
_log("WARNING", "No IMDb ID in Radarr webhook movie data")
|
||||
return {"status": "error", "message": "No IMDb ID"}
|
||||
|
||||
if not imdb_id:
|
||||
_log("ERROR", f"No IMDb ID available for movie: {movie_title}")
|
||||
return {"status": "error", "reason": "No IMDb ID"}
|
||||
# Get movie path for verification
|
||||
movie_path = movie_data.get("path", "")
|
||||
if movie_path:
|
||||
container_path = path_mapper.radarr_path_to_container_path(movie_path)
|
||||
_log("DEBUG", f"Mapped Radarr path {movie_path} -> {container_path}")
|
||||
|
||||
# CRITICAL: Verify the mapped path actually exists
|
||||
from pathlib import Path
|
||||
if not Path(container_path).exists():
|
||||
_log("ERROR", f"RADARR WEBHOOK REJECTED: Mapped path does not exist: {container_path}")
|
||||
_log("ERROR", f"This prevents processing wrong movies due to path mapping issues")
|
||||
return {"status": "error", "message": f"Mapped movie path does not exist: {container_path}"}
|
||||
|
||||
# Verify the path contains the expected IMDb ID
|
||||
if imdb_id not in container_path.lower():
|
||||
_log("WARNING", f"IMDb ID {imdb_id} not found in container path {container_path}")
|
||||
|
||||
# Add to batch queue
|
||||
webhook_dict = {
|
||||
'path': str(movie_path),
|
||||
'movie_info': movie_info,
|
||||
'event_type': webhook.eventType
|
||||
# Create movie-specific webhook data with proper path validation
|
||||
movie_webhook_data = {
|
||||
'path': container_path, # Use verified container path
|
||||
'movie_info': movie_data,
|
||||
'event_type': payload.get('eventType'),
|
||||
'original_payload': payload
|
||||
}
|
||||
batcher.add_webhook(imdb_id, webhook_dict, 'movie')
|
||||
|
||||
return {"status": "accepted", "message": "Radarr webhook queued"}
|
||||
# Add to batch queue with movie-prefixed key to avoid TV conflicts
|
||||
movie_batch_key = f"movie:{imdb_id}"
|
||||
_log("DEBUG", f"Adding Radarr webhook to batch: key={movie_batch_key}, movie_title={movie_data.get('title', 'Unknown')}")
|
||||
batcher.add_webhook(movie_batch_key, movie_webhook_data, "movie")
|
||||
|
||||
return {"status": "success", "message": f"Radarr webhook queued for {movie_batch_key}"}
|
||||
|
||||
except Exception as e:
|
||||
_log("ERROR", f"Radarr webhook error: {e}")
|
||||
raise HTTPException(status_code=422, detail=f"Invalid webhook: {e}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
# ---------------------------
|
||||
# API Endpoints
|
||||
|
||||
Reference in New Issue
Block a user