From 9f05e4cb92cb530f625924d6c42412239d8fc62f Mon Sep 17 00:00:00 2001 From: sbcrumb Date: Wed, 15 Oct 2025 10:42:38 -0400 Subject: [PATCH] web: performance improvments --- api/web_routes.py | 200 ++++++++++++++++++++++++++++++++++++++++-- core/nfo_manager.py | 2 +- static/css/styles.css | 5 ++ 3 files changed, 197 insertions(+), 10 deletions(-) diff --git a/api/web_routes.py b/api/web_routes.py index c3ea217..deba68f 100644 --- a/api/web_routes.py +++ b/api/web_routes.py @@ -383,13 +383,13 @@ async def get_dashboard_stats(dependencies: dict): cursor.execute("SELECT COUNT(*) FROM movies WHERE dateadded IS NOT NULL AND dateadded != ''") movies_with_dates = cursor.fetchone()[0] - cursor.execute("SELECT COUNT(*) FROM movies WHERE dateadded IS NULL OR dateadded = ''") + cursor.execute("SELECT COUNT(*) FROM movies WHERE dateadded IS NULL OR dateadded = '' OR source = 'no_valid_date_source'") movies_without_dates = cursor.fetchone()[0] cursor.execute("SELECT COUNT(*) FROM episodes WHERE dateadded IS NOT NULL AND dateadded != ''") episodes_with_dates = cursor.fetchone()[0] - cursor.execute("SELECT COUNT(*) FROM episodes WHERE dateadded IS NULL OR dateadded = ''") + cursor.execute("SELECT COUNT(*) FROM episodes WHERE dateadded IS NULL OR dateadded = '' OR source = 'no_valid_date_source'") episodes_without_dates = cursor.fetchone()[0] cursor.execute("SELECT COUNT(*) FROM movies WHERE source = 'no_valid_date_source'") @@ -681,13 +681,82 @@ async def get_movie_date_options(dependencies: dict, imdb_id: str): "description": "Enter custom date and time" }) - # Try to get additional metadata from external sources + # Option 4: Active lookup from external sources try: - # This would integrate with your existing TMDB/OMDB logic - # For now, we'll use the existing released date as an example - pass - except Exception: - pass + # Get movie processor and clients from dependencies + movie_processor = dependencies.get("movie_processor") + if movie_processor and hasattr(movie_processor, 'external_clients'): + external_clients = movie_processor.external_clients + + # Check Radarr for import dates + if movie_processor.radarr and movie_processor.radarr.enabled: + try: + radarr_movie = movie_processor.radarr.movie_by_imdb(imdb_id) + if radarr_movie: + movie_id = radarr_movie.get('id') + if movie_id: + import_date, source = movie_processor.radarr.get_movie_import_date(movie_id, fallback_to_file_date=True) + if import_date and source != "no_valid_date_source": + # Check if this is different from current date + current_dateadded = movie.get('dateadded', '') + if not current_dateadded or not current_dateadded.startswith(import_date[:10]): + options.append({ + "type": "radarr_import", + "label": f"Radarr Import Date ({source})", + "date": import_date, + "source": f"radarr:{source}", + "description": f"Import date from Radarr: {import_date[:10]} (source: {source})" + }) + except Exception as e: + print(f"⚠️ Failed to get Radarr import date for {imdb_id}: {e}") + + # Check TMDB for digital release dates + if external_clients.tmdb.enabled: + try: + digital_release = external_clients.tmdb.get_digital_release_date(imdb_id) + if digital_release: + # Check if this is different from current date + current_dateadded = movie.get('dateadded', '') + if not current_dateadded or not current_dateadded.startswith(digital_release[:10]): + options.append({ + "type": "tmdb_digital", + "label": "TMDB Digital Release", + "date": f"{digital_release}T00:00:00", + "source": "tmdb:digital_release", + "description": f"Digital release date from TMDB: {digital_release}" + }) + except Exception as e: + print(f"⚠️ Failed to get TMDB digital release for {imdb_id}: {e}") + + # Check OMDb for additional release info + if external_clients.omdb.enabled: + try: + omdb_details = external_clients.omdb.get_movie_details(imdb_id) + if omdb_details and omdb_details.get('Released') and omdb_details['Released'] != 'N/A': + from datetime import datetime + try: + # Parse OMDb date format (e.g., "27 Jul 2018") + omdb_date = datetime.strptime(omdb_details['Released'], '%d %b %Y') + omdb_iso = omdb_date.strftime('%Y-%m-%d') + + # Check if this is different from current date + current_dateadded = movie.get('dateadded', '') + if not current_dateadded or not current_dateadded.startswith(omdb_iso): + options.append({ + "type": "omdb_release", + "label": "OMDb Release Date", + "date": f"{omdb_iso}T00:00:00", + "source": "omdb:release", + "description": f"Release date from OMDb: {omdb_iso}" + }) + except ValueError: + # Skip if date parsing fails + pass + except Exception as e: + print(f"⚠️ Failed to get OMDb details for {imdb_id}: {e}") + + except Exception as e: + print(f"⚠️ External source lookup failed for {imdb_id}: {e}") print(f"🔍 DEBUG: Generated {len(options)} options for {imdb_id}:") for i, option in enumerate(options): @@ -702,11 +771,35 @@ async def get_movie_date_options(dependencies: dict, imdb_id: str): async def get_episode_date_options(dependencies: dict, imdb_id: str, season: int, episode: int): """Get available date options for an episode""" + print(f"🔍 DEBUG: get_episode_date_options called with imdb_id={imdb_id}, season={season}, episode={episode}") db = dependencies["db"] + # Validate parameters with enhanced checking + try: + if not imdb_id or not imdb_id.strip(): + print(f"❌ Invalid imdb_id: '{imdb_id}'") + raise HTTPException(status_code=422, detail="Invalid imdb_id parameter") + + # Convert and validate season + season = int(season) if isinstance(season, str) else season + if season < 0: + print(f"❌ Invalid season: {season}") + raise HTTPException(status_code=422, detail="Season must be >= 0") + + # Convert and validate episode + episode = int(episode) if isinstance(episode, str) else episode + if episode < 1: + print(f"❌ Invalid episode: {episode}") + raise HTTPException(status_code=422, detail="Episode must be >= 1") + except ValueError as e: + print(f"❌ Parameter conversion error: {e}") + raise HTTPException(status_code=422, detail=f"Invalid parameter types: {e}") + # Get current episode data episode_data = db.get_episode_date(imdb_id, season, episode) + print(f"🔍 DEBUG: Episode data from DB: {episode_data}") if not episode_data: + print(f"❌ Episode not found in database: {imdb_id} S{season:02d}E{episode:02d}") raise HTTPException(status_code=404, detail="Episode not found") options = [] @@ -731,7 +824,92 @@ async def get_episode_date_options(dependencies: dict, imdb_id: str, season: int "description": f"Use original air date: {episode_data['aired']}" }) - # Option 3: Manual entry + # Option 3: Active lookup from external sources + try: + # Get TV processor and clients from dependencies + tv_processor = dependencies.get("tv_processor") + if tv_processor and hasattr(tv_processor, 'external_clients'): + external_clients = tv_processor.external_clients + + # Check Sonarr for import dates + if tv_processor.sonarr and tv_processor.sonarr.enabled: + try: + # Look up the series and episode in Sonarr + series_data = tv_processor.sonarr.series_by_imdb(imdb_id) + if series_data: + series_id = series_data.get('id') + if series_id: + # Get episodes for the series + episodes = tv_processor.sonarr.get_episodes(series_id) + for ep in episodes: + if ep.get('seasonNumber') == season and ep.get('episodeNumber') == episode: + episode_id = ep.get('id') + if episode_id: + # Get import history for this specific episode + import_date = tv_processor.sonarr.get_episode_import_history(episode_id) + if import_date: + # Check if this is different from current date + current_dateadded = episode_data.get('dateadded', '') + if not current_dateadded or not current_dateadded.startswith(import_date[:10]): + options.append({ + "type": "sonarr_import", + "label": "Sonarr Import Date", + "date": import_date, + "source": "sonarr:import_history", + "description": f"Import date from Sonarr: {import_date[:10]}" + }) + break + except Exception as e: + print(f"⚠️ Failed to get Sonarr import date for {imdb_id} S{season:02d}E{episode:02d}: {e}") + + # Check TMDB for episode air dates + if external_clients.tmdb.enabled: + try: + # Get TMDB TV series ID from IMDb ID + tv_info = external_clients.tmdb.find_tv_by_imdb(imdb_id) + if tv_info: + tmdb_id = tv_info.get('id') + if tmdb_id: + # Get episode air date from TMDB + episodes = external_clients.tmdb.get_tv_season_episodes(tmdb_id, season) + if episode in episodes: + air_date = episodes[episode] + if air_date: + # Check if this is different from current aired date + current_aired = episode_data.get('aired', '') + if not current_aired or current_aired != air_date: + options.append({ + "type": "tmdb_air", + "label": "TMDB Air Date", + "date": f"{air_date}T20:00:00", # Default to 8 PM + "source": "tmdb:airdate", + "description": f"Air date from TMDB: {air_date}" + }) + except Exception as e: + print(f"⚠️ Failed to get TMDB air date for {imdb_id} S{season:02d}E{episode:02d}: {e}") + + # Check external clients for episode air dates (TVDB, OMDb) + if hasattr(external_clients, 'get_episode_air_date'): + try: + air_date = external_clients.get_episode_air_date(imdb_id, season, episode) + if air_date: + # Check if this is different from current aired date + current_aired = episode_data.get('aired', '') + if not current_aired or current_aired != air_date: + options.append({ + "type": "external_air", + "label": "External Air Date", + "date": f"{air_date}T20:00:00", # Default to 8 PM + "source": "external:airdate", + "description": f"Air date from external sources: {air_date}" + }) + except Exception as e: + print(f"⚠️ Failed to get external air date for {imdb_id} S{season:02d}E{episode:02d}: {e}") + + except Exception as e: + print(f"⚠️ External source lookup failed for {imdb_id} S{season:02d}E{episode:02d}: {e}") + + # Option 4: Manual entry options.append({ "type": "manual", "label": "Manual Entry", @@ -740,6 +918,10 @@ async def get_episode_date_options(dependencies: dict, imdb_id: str, season: int "description": "Enter custom date and time" }) + print(f"🔍 DEBUG: Generated {len(options)} options for {imdb_id} S{season:02d}E{episode:02d}:") + for i, option in enumerate(options): + print(f" Option {i}: {option}") + return { "imdb_id": imdb_id, "season": season, diff --git a/core/nfo_manager.py b/core/nfo_manager.py index 0fc13e6..0951d1e 100644 --- a/core/nfo_manager.py +++ b/core/nfo_manager.py @@ -663,7 +663,7 @@ class NFOManager: if preserved_fields: print(f" 🔍 Content preserved after cleanup: {', '.join(preserved_fields)}") else: - print(f" ⚠️ No content fields found after cleanup!") + print(f" ℹ️ NFO contains only NFOGuard metadata (no additional content fields)") except (ET.ParseError, ValueError) as e: print(f"⚠️ Corrupted episode NFO detected: {nfo_path} - {str(e)[:100]}...") diff --git a/static/css/styles.css b/static/css/styles.css index c947b31..9dd6eba 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -504,6 +504,11 @@ body { padding: 1.5rem; } +/* Higher z-index for edit modals that appear on top of other modals */ +#edit-modal, #smart-fix-modal { + z-index: 1100 !important; +} + /* Reports */ .report-summary { display: grid;