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]
## [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
### Changed
- 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
"""
Enhanced Radarr API client with improved history event detection and path mapping
"""
"""Enhanced Radarr API client with improved import date detection"""
import json
import time
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.error import URLError, HTTPError
from .logging import _log
from ..core.logging import _log
# Import path mapper for proper path handling
try:
@@ -31,32 +30,16 @@ except ImportError:
class RadarrClient:
"""Enhanced Radarr API client with improved import date detection"""
# Radarr API numeric event types:
# 1 = "grabbed" (when release is grabbed/downloaded)
# 3 = "imported" (when file is imported to library)
# These come from Radarr's EventType enum in the API/database
# Known Radarr event types that indicate actual imports
REAL_IMPORT_EVENTS = {
# Primary events to check first
"moviefileimported",
"importevent",
# Secondary events
"downloadfolderimported",
"imported",
"downloadimported",
"fileimported",
"movieimported",
# Legacy events
"downloadedepisode",
"downloadedmovie",
}
# Events that should be checked first
PRIMARY_IMPORT_EVENTS = {
"moviefileimported",
"importevent"
}
# Radarr History API event types (HistoryEventType enum)
# From: https://github.com/Radarr/Radarr/blob/develop/src/NzbDrone.Core/History/HistoryEventType.cs
EVENT_TYPE_GRABBED = 1 # Movie was grabbed from indexer
EVENT_TYPE_IMPORTED = 3 # Movie was imported to final library
EVENT_TYPE_FAILED = 4 # Download or import failed
EVENT_TYPE_RETAGGED = 6 # Files were tagged
EVENT_TYPE_RENAMED = 8 # Files were renamed
# Event types that indicate real imports
REAL_IMPORT_EVENT_TYPES = [EVENT_TYPE_IMPORTED] # Only trust actual "imported" events
# These are now handled by path_mapper, but keeping for backward compatibility
DOWNLOAD_PATH_INDICATORS = [
@@ -156,7 +139,7 @@ class RadarrClient:
Returns:
(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")
event_data = event.get("data", {})
@@ -171,8 +154,14 @@ class RadarrClient:
if not date_iso:
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
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
# Get all possible source paths/titles
@@ -265,18 +254,27 @@ class RadarrClient:
for event in items:
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)
if event_type == "grabbed" and not first_grab:
# Check for grab events (type 1)
if event_type == self.EVENT_TYPE_GRABBED and not first_grab:
if event.get("date"):
try:
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:
pass
# Get imported path from Data field
# Only process import events (type 3)
if event_type != self.EVENT_TYPE_IMPORTED:
continue
imported_path = None
try:
data = json.loads(event.get("data", "{}"))
@@ -306,19 +304,14 @@ class RadarrClient:
# Then try title/year match with fuzzy path cleaning
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_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
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")
_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}")
earliest_real_import = date_iso
break
@@ -351,7 +344,7 @@ class RadarrClient:
_log("INFO", f"✅ FOUND IMPORT: Title/year match at {date_iso}")
earliest_real_import = date_iso
break
elif event_type in self.REAL_IMPORT_EVENTS:
elif event_type == 3:
_log("DEBUG", f"⚠️ Skipped import event: {reason}")
# 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}")