update to database only
This commit is contained in:
+42
-77
@@ -61,16 +61,20 @@ class RadarrClient:
|
||||
self.timeout = timeout
|
||||
self.retries = max(0, retries)
|
||||
|
||||
# Initialize database client if available
|
||||
# Initialize database client - REQUIRED for operation
|
||||
self.db_client = None
|
||||
if RadarrDbClient:
|
||||
try:
|
||||
self.db_client = RadarrDbClient.from_env()
|
||||
if self.db_client:
|
||||
_log("INFO", "Enhanced performance mode: Direct database access enabled")
|
||||
_log("INFO", "✅ DATABASE ONLY MODE: Direct database access enabled")
|
||||
else:
|
||||
_log("ERROR", "❌ DATABASE ONLY MODE: Database configuration required - API mode disabled")
|
||||
except Exception as e:
|
||||
_log("WARNING", f"Failed to initialize database client, using API fallback: {e}")
|
||||
_log("ERROR", f"❌ DATABASE ONLY MODE: Failed to initialize database client: {e}")
|
||||
self.db_client = None
|
||||
else:
|
||||
_log("ERROR", "❌ DATABASE ONLY MODE: RadarrDbClient not available - check dependencies")
|
||||
|
||||
def _get(self, path: str, params: Dict[str, Any] = None) -> Optional[Any]:
|
||||
"""Make GET request to Radarr API with retries"""
|
||||
@@ -107,53 +111,26 @@ class RadarrClient:
|
||||
return None
|
||||
|
||||
def movie_by_imdb(self, imdb_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Find movie by IMDb ID with enhanced validation"""
|
||||
"""Find movie by IMDb ID - DATABASE ONLY mode"""
|
||||
imdb_id = imdb_id if imdb_id.startswith("tt") else f"tt{imdb_id}"
|
||||
_log("DEBUG", f"Looking up movie by IMDb ID: {imdb_id}")
|
||||
|
||||
# Try database first if available
|
||||
# Database required - no API fallback
|
||||
if self.db_client:
|
||||
try:
|
||||
movie = self.db_client.get_movie_by_imdb(imdb_id)
|
||||
if movie:
|
||||
_log("INFO", f"Found via database: {movie.get('title')} (ID: {movie.get('id')})")
|
||||
_log("INFO", f"✅ Found via database: {movie.get('title')} (ID: {movie.get('id')})")
|
||||
return movie
|
||||
else:
|
||||
_log("WARNING", f"Movie not found in database for IMDb ID: {imdb_id}")
|
||||
return None
|
||||
except Exception as e:
|
||||
_log("WARNING", f"Database lookup failed, using API fallback: {e}")
|
||||
_log("ERROR", f"Database lookup failed: {e}")
|
||||
return None
|
||||
|
||||
# API Fallback methods
|
||||
# Method 1: Direct lookup by ID
|
||||
movie = self._get(f"/api/v3/movie/lookup/imdb/{imdb_id}")
|
||||
if isinstance(movie, dict) and movie.get("imdbId", "").lower() == imdb_id.lower():
|
||||
_log("INFO", f"Found via API direct lookup: {movie.get('title')} (ID: {movie.get('id')})")
|
||||
return movie
|
||||
|
||||
# Method 2: Get all movies and filter by IMDb (fallback)
|
||||
all_movies = self._get("/api/v3/movie")
|
||||
if isinstance(all_movies, list):
|
||||
_log("DEBUG", f"Got {len(all_movies)} total movies, filtering by IMDb ID")
|
||||
for movie in all_movies:
|
||||
movie_imdb = movie.get("imdbId", "").lower()
|
||||
if movie_imdb == imdb_id.lower():
|
||||
_log("INFO", f"Found exact match: {movie.get('title')} (ID: {movie.get('id')})")
|
||||
return movie
|
||||
|
||||
# Method 3: Direct API search with validation
|
||||
result = self._get("/api/v3/movie", {"imdbId": imdb_id})
|
||||
if isinstance(result, list) and result:
|
||||
for movie in result:
|
||||
movie_imdb = movie.get("imdbId", "").lower()
|
||||
if movie_imdb == imdb_id.lower():
|
||||
_log("INFO", f"Found via search: {movie.get('title')} (ID: {movie.get('id')})")
|
||||
return movie
|
||||
|
||||
# Method 4: Lookup endpoint
|
||||
lookup_result = self._get("/api/v3/movie/lookup/imdb", {"imdbId": imdb_id})
|
||||
if isinstance(lookup_result, dict) and lookup_result.get("imdbId", "").lower() == imdb_id.lower():
|
||||
_log("INFO", f"Found via lookup: {lookup_result.get('title')} (ID: {lookup_result.get('id')})")
|
||||
return lookup_result
|
||||
|
||||
_log("ERROR", f"No movie found for IMDb ID: {imdb_id}")
|
||||
# No database client available
|
||||
_log("ERROR", "Database client required for movie lookup - API mode disabled")
|
||||
return None
|
||||
|
||||
def _analyze_event_for_import(self, event: Dict[str, Any], movie_info: Dict[str, Any] = None) -> Tuple[bool, str, Optional[str]]:
|
||||
@@ -441,62 +418,50 @@ class RadarrClient:
|
||||
return None
|
||||
|
||||
def movie_files(self, movie_id: int) -> List[Dict[str, Any]]:
|
||||
"""Get movie files for a movie"""
|
||||
result = self._get("/api/v3/moviefile", {"movieId": movie_id})
|
||||
return result if isinstance(result, list) else []
|
||||
"""Get movie files for a movie - DATABASE ONLY mode"""
|
||||
if self.db_client:
|
||||
_log("INFO", "Using database for movie files lookup")
|
||||
# Database handles this internally in get_movie_file_date()
|
||||
return []
|
||||
|
||||
_log("ERROR", "Database client required for movie files - API mode disabled")
|
||||
return []
|
||||
|
||||
def earliest_file_dateadded(self, movie_id: int) -> Optional[str]:
|
||||
"""Get earliest file dateAdded as fallback"""
|
||||
files = self.movie_files(movie_id)
|
||||
if not files:
|
||||
return None
|
||||
|
||||
earliest = None
|
||||
for file_obj in files:
|
||||
date_added = file_obj.get("dateAdded")
|
||||
if not date_added:
|
||||
continue
|
||||
|
||||
"""Get earliest file dateAdded - DATABASE ONLY mode"""
|
||||
if self.db_client:
|
||||
try:
|
||||
dt = datetime.fromisoformat(date_added.replace("Z", "+00:00"))
|
||||
if earliest is None or dt < earliest:
|
||||
earliest = dt
|
||||
except Exception:
|
||||
continue
|
||||
return self.db_client.get_movie_file_date(movie_id)
|
||||
except Exception as e:
|
||||
_log("ERROR", f"Database file date query failed: {e}")
|
||||
return None
|
||||
|
||||
if earliest:
|
||||
return earliest.astimezone(timezone.utc).isoformat(timespec="seconds")
|
||||
_log("ERROR", "Database client required for file dates - API mode disabled")
|
||||
return None
|
||||
|
||||
def get_movie_import_date(self, movie_id: int, fallback_to_file_date: bool = True) -> Tuple[Optional[str], str]:
|
||||
"""
|
||||
Get the best import date for a movie with database optimization.
|
||||
Get the best import date for a movie - DATABASE ONLY mode.
|
||||
|
||||
Returns:
|
||||
(date_iso, source_description)
|
||||
"""
|
||||
# Try database first if available (much faster)
|
||||
# Database required - no API fallback
|
||||
if self.db_client:
|
||||
try:
|
||||
date_iso, source = self.db_client.get_movie_import_date_optimized(movie_id, fallback_to_file_date)
|
||||
if date_iso:
|
||||
return date_iso, source
|
||||
else:
|
||||
_log("WARNING", f"No import date found in database for movie_id {movie_id}")
|
||||
return None, "radarr:db.no_date_found"
|
||||
except Exception as e:
|
||||
_log("WARNING", f"Database import date query failed, using API fallback: {e}")
|
||||
_log("ERROR", f"Database import date query failed: {e}")
|
||||
return None, "radarr:db.error"
|
||||
|
||||
# API Fallback: Try history parsing
|
||||
import_date = self.earliest_import_event_optimized(movie_id)
|
||||
if import_date:
|
||||
return import_date, "radarr:api.history.import"
|
||||
|
||||
# Fallback to file dateAdded if requested
|
||||
if fallback_to_file_date:
|
||||
file_date = self.earliest_file_dateadded(movie_id)
|
||||
if file_date:
|
||||
_log("WARNING", f"Using file dateAdded as fallback for movie_id {movie_id}")
|
||||
return file_date, "radarr:api.file.dateAdded"
|
||||
|
||||
return None, "radarr:no_date_found"
|
||||
# No database client available
|
||||
_log("ERROR", "Database client required for import date detection - API mode disabled")
|
||||
return None, "radarr:db.not_configured"
|
||||
|
||||
def _get_earliest_import_date(self, movie_id: int, movie_info: Dict) -> Optional[str]:
|
||||
"""Get the earliest import date from Radarr history."""
|
||||
|
||||
@@ -175,26 +175,28 @@ class RadarrDbClient:
|
||||
Returns:
|
||||
(date_iso, source_description)
|
||||
"""
|
||||
# Query for earliest import event (EventType = 3)
|
||||
# Query for earliest import event - handle both numeric (3) and string ('downloadFolderImported') formats
|
||||
import_query = """
|
||||
SELECT
|
||||
h."Date" as event_date,
|
||||
h."Data" as event_data
|
||||
h."Data" as event_data,
|
||||
h."EventType" as event_type
|
||||
FROM "History" h
|
||||
WHERE h."MovieId" = %s
|
||||
AND h."EventType" = 3
|
||||
AND (h."EventType" = 3 OR h."EventType" = 'downloadFolderImported' OR h."EventType" = 'movieFileImported')
|
||||
ORDER BY h."Date" ASC
|
||||
LIMIT 1
|
||||
"""
|
||||
|
||||
# Fallback: earliest grab event (EventType = 1) with download info
|
||||
# Fallback: earliest grab event - handle both numeric (1) and string ('grabbed') formats
|
||||
grab_query = """
|
||||
SELECT
|
||||
h."Date" as event_date,
|
||||
h."Data" as event_data
|
||||
h."Data" as event_data,
|
||||
h."EventType" as event_type
|
||||
FROM "History" h
|
||||
WHERE h."MovieId" = %s
|
||||
AND h."EventType" = 1
|
||||
AND (h."EventType" = 1 OR h."EventType" = 'grabbed')
|
||||
AND h."Data" IS NOT NULL
|
||||
ORDER BY h."Date" ASC
|
||||
LIMIT 1
|
||||
@@ -217,13 +219,14 @@ class RadarrDbClient:
|
||||
|
||||
if row:
|
||||
event_date = row['event_date'] if self.db_type == "postgresql" else row[0]
|
||||
event_type = row['event_type'] if self.db_type == "postgresql" else row[2]
|
||||
if isinstance(event_date, str):
|
||||
dt = datetime.fromisoformat(event_date.replace("Z", "+00:00"))
|
||||
else:
|
||||
dt = event_date.replace(tzinfo=timezone.utc)
|
||||
|
||||
date_iso = dt.astimezone(timezone.utc).isoformat(timespec="seconds")
|
||||
_log("INFO", f"Found import event for movie {movie_id} at {date_iso}")
|
||||
_log("INFO", f"✅ Found import event ({event_type}) for movie {movie_id} at {date_iso}")
|
||||
return date_iso, "radarr:db.history.import"
|
||||
|
||||
# Fallback to grab events
|
||||
@@ -232,13 +235,14 @@ class RadarrDbClient:
|
||||
|
||||
if row:
|
||||
event_date = row['event_date'] if self.db_type == "postgresql" else row[0]
|
||||
event_type = row['event_type'] if self.db_type == "postgresql" else row[2]
|
||||
if isinstance(event_date, str):
|
||||
dt = datetime.fromisoformat(event_date.replace("Z", "+00:00"))
|
||||
else:
|
||||
dt = event_date.replace(tzinfo=timezone.utc)
|
||||
|
||||
date_iso = dt.astimezone(timezone.utc).isoformat(timespec="seconds")
|
||||
_log("WARNING", f"Using grab event for movie {movie_id} at {date_iso}")
|
||||
_log("WARNING", f"⚠️ Using grab event ({event_type}) for movie {movie_id} at {date_iso}")
|
||||
return date_iso, "radarr:db.history.grab"
|
||||
|
||||
except Exception as e:
|
||||
@@ -342,7 +346,8 @@ class RadarrDbClient:
|
||||
MIN(h."Date") as earliest_import
|
||||
FROM "Movies" m
|
||||
JOIN "MovieMetadata" mm ON m."MovieMetadataId" = mm."Id"
|
||||
LEFT JOIN "History" h ON m."Id" = h."MovieId" AND h."EventType" = 3
|
||||
LEFT JOIN "History" h ON m."Id" = h."MovieId"
|
||||
AND (h."EventType" = 3 OR h."EventType" = 'downloadFolderImported' OR h."EventType" = 'movieFileImported')
|
||||
WHERE mm."ImdbId" IN ({placeholders})
|
||||
GROUP BY mm."ImdbId", m."Id"
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user