fix(radarr): improve event type parsing and grab event validation
- Fix string-based event type parsing (grabbed, downloadFolderImported, etc.) - Filter grab events to only count actual downloads with metadata - Fix JSON parsing errors for dict-type event data - Enhanced chronological processing for accurate import date detection - Validate grab events require sourceTitle or indexer info Fixes issue where library addition events were mistaken for download grabs, ensuring only real download timestamps are used for
This commit is contained in:
+15
-1
@@ -4,7 +4,21 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [0.4.0] - 2025-09-08
|
# Changelog
|
||||||
|
|
||||||
|
## [0.4.1] - 2025-01-09
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Radarr Client**: Fixed event type parsing for string-based event types (grabbed, downloadFolderImported, etc.)
|
||||||
|
- **Radarr Client**: Improved grab event detection to filter out library addition events and only count actual download grabs
|
||||||
|
- **Radarr Client**: Fixed JSON parsing errors when handling event data that's already a dictionary
|
||||||
|
- **Radarr Client**: Enhanced validation of grab events to require download metadata (sourceTitle or indexer)
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
- **Radarr Client**: Better chronological event processing to find earliest actual download dates
|
||||||
|
- **Radarr Client**: More accurate import date detection by validating grab events have real download information
|
||||||
|
|
||||||
|
## [0.4.0] - 2025-01-08
|
||||||
### Changed
|
### Changed
|
||||||
- Switched to using Radarr's numeric event types for more reliable import detection
|
- Switched to using Radarr's numeric event types for more reliable import detection
|
||||||
- Added constants for EVENT_TYPE_GRABBED (1), IMPORTED (3), etc.
|
- Added constants for EVENT_TYPE_GRABBED (1), IMPORTED (3), etc.
|
||||||
|
|||||||
+33
-17
@@ -278,39 +278,55 @@ class RadarrClient:
|
|||||||
_log("DEBUG", f"Unknown event type: {event_type}")
|
_log("DEBUG", f"Unknown event type: {event_type}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check for grab events (type 1)
|
# Check for grab events (type 1) - but validate it's a real download
|
||||||
if event_type == self.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")
|
# Get event data to check if this is a real grab with download info
|
||||||
_log("DEBUG", f"Found grab event (type {self.EVENT_TYPE_GRABBED}) at {first_grab}")
|
event_data = event.get("data", {})
|
||||||
except Exception:
|
if isinstance(event_data, str):
|
||||||
pass
|
try:
|
||||||
|
event_data = json.loads(event_data)
|
||||||
|
except (json.JSONDecodeError, AttributeError):
|
||||||
|
event_data = {}
|
||||||
|
|
||||||
# Track the first grab event we find
|
# Check if this grab has actual download/indexer info
|
||||||
if event_type == self.EVENT_TYPE_GRABBED and not first_grab:
|
source_title = event_data.get("sourceTitle", "")
|
||||||
if event.get("date"):
|
indexer = event_data.get("indexer", "")
|
||||||
try:
|
|
||||||
first_grab = datetime.fromisoformat(event["date"].replace("Z", "+00:00")).astimezone(timezone.utc).isoformat(timespec="seconds")
|
# Only count grabs that have actual download metadata
|
||||||
_log("DEBUG", f"Found first grab event at {first_grab}")
|
if source_title or indexer:
|
||||||
|
first_grab = datetime.fromisoformat(event["date"].replace("Z", "+00:00")).astimezone(timezone.utc).isoformat(timespec="seconds")
|
||||||
|
_log("DEBUG", f"Found real grab event with source '{source_title}' from '{indexer}' at {first_grab}")
|
||||||
|
else:
|
||||||
|
_log("DEBUG", f"Skipping grab event without download info at {event.get('date')}")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
continue
|
|
||||||
|
|
||||||
# Only process import events (type 3)
|
# Only process import events (type 3)
|
||||||
if event_type != self.EVENT_TYPE_IMPORTED:
|
if event_type != self.EVENT_TYPE_IMPORTED:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Get imported path from event data
|
||||||
imported_path = None
|
imported_path = None
|
||||||
try:
|
event_data = event.get("data", {})
|
||||||
data = json.loads(event.get("data", "{}"))
|
|
||||||
imported_path = data.get("importedPath", "").lower()
|
# Handle both string and dict data
|
||||||
except (json.JSONDecodeError, AttributeError) as e:
|
if isinstance(event_data, str):
|
||||||
_log("DEBUG", f"Failed to get imported path: {e}")
|
try:
|
||||||
|
event_data = json.loads(event_data)
|
||||||
|
except (json.JSONDecodeError, AttributeError) as e:
|
||||||
|
_log("DEBUG", f"Failed to parse event data JSON: {e}")
|
||||||
|
continue
|
||||||
|
elif not isinstance(event_data, dict):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
imported_path = event_data.get("importedPath", "")
|
||||||
if not imported_path:
|
if not imported_path:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
imported_path = imported_path.lower()
|
||||||
|
|
||||||
movie_imdb = (movie_info.get("imdbId", "") or "").lower()
|
movie_imdb = (movie_info.get("imdbId", "") or "").lower()
|
||||||
movie_title = (movie_info.get("title", "") or "").lower()
|
movie_title = (movie_info.get("title", "") or "").lower()
|
||||||
movie_year = str(movie_info.get("year", ""))
|
movie_year = str(movie_info.get("year", ""))
|
||||||
|
|||||||
Reference in New Issue
Block a user