updates to api and logging

This commit is contained in:
2025-09-08 15:04:20 -04:00
parent e40e80bfb5
commit 4e4ddbf92a
4 changed files with 57 additions and 46 deletions
+11
View File
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
## [Unreleased] ## [Unreleased]
## [0.4.0] - 2025-09-08
### Changed
- Switched to using Radarr's numeric event types for more reliable import detection
- Added constants for EVENT_TYPE_GRABBED (1), IMPORTED (3), etc.
- Removed legacy string-based event type checks
- Improved event type parsing and validation
### Changed
- Added documentation for Radarr API event types (1=grabbed, 3=imported)
- Improved event type detection using Radarr's internal event type IDs
- Removed old string-based event type checks in favor of numeric IDs
## [0.3.8] - 2025-09-08 ## [0.3.8] - 2025-09-08
### Changed ### Changed
- Use Radarr's numeric EventType (3="Imported") for more accurate import date detection - Use Radarr's numeric EventType (3="Imported") for more accurate import date detection
+1 -1
View File
@@ -1 +1 @@
0.3.8 0.4.0
+38 -45
View File
@@ -1,7 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """Enhanced Radarr API client with improved import date detection"""
Enhanced Radarr API client with improved history event detection and path mapping
"""
import json import json
import time import time
from datetime import datetime, timezone from datetime import datetime, timezone
@@ -11,7 +10,7 @@ from urllib.parse import urlencode, urljoin
from urllib.request import Request as UrlRequest, urlopen from urllib.request import Request as UrlRequest, urlopen
from urllib.error import URLError, HTTPError from urllib.error import URLError, HTTPError
from .logging import _log from ..core.logging import _log
# Import path mapper for proper path handling # Import path mapper for proper path handling
try: try:
@@ -31,32 +30,16 @@ except ImportError:
class RadarrClient: class RadarrClient:
"""Enhanced Radarr API client with improved import date detection""" """Enhanced Radarr API client with improved import date detection"""
# Radarr API numeric event types: # Radarr History API event types (HistoryEventType enum)
# 1 = "grabbed" (when release is grabbed/downloaded) # From: https://github.com/Radarr/Radarr/blob/develop/src/NzbDrone.Core/History/HistoryEventType.cs
# 3 = "imported" (when file is imported to library) EVENT_TYPE_GRABBED = 1 # Movie was grabbed from indexer
# These come from Radarr's EventType enum in the API/database EVENT_TYPE_IMPORTED = 3 # Movie was imported to final library
EVENT_TYPE_FAILED = 4 # Download or import failed
# Known Radarr event types that indicate actual imports EVENT_TYPE_RETAGGED = 6 # Files were tagged
REAL_IMPORT_EVENTS = { EVENT_TYPE_RENAMED = 8 # Files were renamed
# Primary events to check first
"moviefileimported", # Event types that indicate real imports
"importevent", REAL_IMPORT_EVENT_TYPES = [EVENT_TYPE_IMPORTED] # Only trust actual "imported" events
# Secondary events
"downloadfolderimported",
"imported",
"downloadimported",
"fileimported",
"movieimported",
# Legacy events
"downloadedepisode",
"downloadedmovie",
}
# Events that should be checked first
PRIMARY_IMPORT_EVENTS = {
"moviefileimported",
"importevent"
}
# These are now handled by path_mapper, but keeping for backward compatibility # These are now handled by path_mapper, but keeping for backward compatibility
DOWNLOAD_PATH_INDICATORS = [ DOWNLOAD_PATH_INDICATORS = [
@@ -156,7 +139,7 @@ class RadarrClient:
Returns: Returns:
(is_real_import, reason, date_iso) (is_real_import, reason, date_iso)
""" """
event_type = (event.get("eventType") or event.get("type") or "").lower() event_type = event.get("eventType")
date_str = event.get("date") date_str = event.get("date")
event_data = event.get("data", {}) event_data = event.get("data", {})
@@ -171,8 +154,14 @@ class RadarrClient:
if not date_iso: if not date_iso:
return False, "no_valid_date", None return False, "no_valid_date", None
# Convert event type to int if needed
try:
event_type_int = int(event_type) if isinstance(event_type, str) and event_type.isdigit() else event_type
except (ValueError, TypeError):
event_type_int = None
# Check if event type indicates import # Check if event type indicates import
if event_type not in self.REAL_IMPORT_EVENTS: if event_type_int not in self.REAL_IMPORT_EVENT_TYPES:
return False, f"event_type_not_import({event_type})", date_iso return False, f"event_type_not_import({event_type})", date_iso
# Get all possible source paths/titles # Get all possible source paths/titles
@@ -265,18 +254,27 @@ class RadarrClient:
for event in items: for event in items:
total_processed += 1 total_processed += 1
event_type = (event.get("eventType") or "").lower() event_type = event.get("eventType")
if not event_type:
continue
try:
event_type = int(event_type)
except (ValueError, TypeError):
continue
# Check for grab events (fallback) # Check for grab events (type 1)
if event_type == "grabbed" and not first_grab: if event_type == self.EVENT_TYPE_GRABBED and not first_grab:
if event.get("date"): if event.get("date"):
try: try:
first_grab = datetime.fromisoformat(event["date"].replace("Z", "+00:00")).astimezone(timezone.utc).isoformat(timespec="seconds") first_grab = datetime.fromisoformat(event["date"].replace("Z", "+00:00")).astimezone(timezone.utc).isoformat(timespec="seconds")
_log("DEBUG", f"Found grab event at {first_grab}") _log("DEBUG", f"Found grab event (type {self.EVENT_TYPE_GRABBED}) at {first_grab}")
except Exception: except Exception:
pass pass
# Get imported path from Data field # Only process import events (type 3)
if event_type != self.EVENT_TYPE_IMPORTED:
continue
imported_path = None imported_path = None
try: try:
data = json.loads(event.get("data", "{}")) data = json.loads(event.get("data", "{}"))
@@ -306,19 +304,14 @@ class RadarrClient:
# Then try title/year match with fuzzy path cleaning # Then try title/year match with fuzzy path cleaning
if movie_title and movie_year: if movie_title and movie_year:
# Clean strings for comparison by replacing common separators # Clean strings for comparison
clean_title = movie_title.replace(" ", ".").replace(":", ".").replace("-", ".").replace("_", ".").lower() clean_title = movie_title.replace(" ", ".").replace(":", ".").replace("-", ".").replace("_", ".").lower()
clean_path = imported_path.replace(" ", ".").replace("-", ".").replace("_", ".").replace("[", "").replace("]", "").lower() clean_path = imported_path.replace(" ", ".").replace("-", ".").replace("_", ".").replace("[", "").replace("]", "").lower()
# Remove common words for better matching
for word in ["the", "a", "an"]:
clean_title = clean_title.replace(f".{word}.", ".").replace(f"{word}.", "").replace(f".{word}", "")
clean_path = clean_path.replace(f".{word}.", ".").replace(f"{word}.", "").replace(f".{word}", "")
# Look for both title and year in the path # Look for both title and year in the path
if clean_title in clean_path and movie_year in clean_path: if clean_title in clean_path and movie_year in clean_path:
date_iso = datetime.fromisoformat(event["date"].replace("Z", "+00:00")).astimezone(timezone.utc).isoformat(timespec="seconds") date_iso = datetime.fromisoformat(event["date"].replace("Z", "+00:00")).astimezone(timezone.utc).isoformat(timespec="seconds")
_log("INFO", f"Found potential title/year match in {event_type} event: {clean_title} ({movie_year})") _log("INFO", f"Found potential title/year match for event type {event_type}: {clean_title} ({movie_year})")
_log("INFO", f"✅ FOUND IMPORT at {date_iso}") _log("INFO", f"✅ FOUND IMPORT at {date_iso}")
earliest_real_import = date_iso earliest_real_import = date_iso
break break
@@ -351,7 +344,7 @@ class RadarrClient:
_log("INFO", f"✅ FOUND IMPORT: Title/year match at {date_iso}") _log("INFO", f"✅ FOUND IMPORT: Title/year match at {date_iso}")
earliest_real_import = date_iso earliest_real_import = date_iso
break break
elif event_type in self.REAL_IMPORT_EVENTS: elif event_type == 3:
_log("DEBUG", f"⚠️ Skipped import event: {reason}") _log("DEBUG", f"⚠️ Skipped import event: {reason}")
# If we found a real import, no need to continue # If we found a real import, no need to continue
+7
View File
@@ -0,0 +1,7 @@
"""Logging utilities for NFOguard"""
from datetime import datetime, timezone
def _log(level: str, msg: str):
"""Basic logging function that writes to console"""
print(f"[{datetime.now(timezone.utc).isoformat(timespec='seconds')}] {level}: {msg}")