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]
|
## [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
|
||||||
|
|||||||
+38
-45
@@ -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
|
||||||
|
|||||||
@@ -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