updates to api and logging
This commit is contained in:
@@ -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
|
||||
|
||||
+37
-44
@@ -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
|
||||
# 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
|
||||
|
||||
# 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"
|
||||
}
|
||||
# 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
|
||||
|
||||
# Check for grab events (fallback)
|
||||
if event_type == "grabbed" and not first_grab:
|
||||
try:
|
||||
event_type = int(event_type)
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
# 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
|
||||
|
||||
@@ -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}")
|
||||
Reference in New Issue
Block a user