From 8b683a9fadb8bedbfcde00e91b6cd38033f81ccf Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 20 Oct 2025 16:45:15 -0400 Subject: [PATCH] fix: Store actual movie release dates in database - Movie processor now stores release dates in 'released' field - Added backfill endpoint for existing movies missing release dates - Fixes empty "Movie Released" column in web interface --- VERSION | 2 +- api/routes.py | 99 +++++++++++++++++++++++++++++++++++ processors/movie_processor.py | 12 +++-- 3 files changed, 108 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index d48d370..a04abec 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.9 +2.6.10 diff --git a/api/routes.py b/api/routes.py index 05f0641..f7b710f 100644 --- a/api/routes.py +++ b/api/routes.py @@ -1233,6 +1233,101 @@ async def cleanup_orphaned_series(dependencies: dict): } +async def backfill_movie_release_dates(dependencies: dict): + """Backfill missing release dates for existing movies""" + db = dependencies["db"] + movie_processor = dependencies["movie_processor"] + + try: + # Get all movies with missing release dates + with db.get_connection() as conn: + cursor = conn.cursor() + + # Find movies where released is NULL but source suggests we found a release date + query = """ + SELECT imdb_id, path, dateadded, source + FROM movies + WHERE released IS NULL + AND (source LIKE '%tmdb%' OR source LIKE '%omdb%' OR source LIKE '%premiered%') + ORDER BY last_updated DESC + """ + cursor.execute(query) + movies_to_backfill = cursor.fetchall() + + if not movies_to_backfill: + return { + "success": True, + "message": "No movies found that need release date backfill", + "movies_processed": 0, + "movies_updated": 0 + } + + processed_count = 0 + updated_count = 0 + + print(f"🔄 BACKFILL STARTED: Found {len(movies_to_backfill)} movies needing release date backfill") + + for movie in movies_to_backfill: + imdb_id = movie['imdb_id'] + source = movie['source'] + + try: + print(f"🔍 Processing {imdb_id} (source: {source})") + + # Try to get release date based on the source + release_date = None + + if 'tmdb' in source or 'omdb' in source: + # Re-fetch digital release date + digital_date, _ = movie_processor._get_digital_release_date(imdb_id) + if digital_date: + release_date = digital_date + print(f"✅ Found release date for {imdb_id}: {release_date}") + else: + print(f"⚠️ Could not re-fetch release date for {imdb_id}") + + elif 'premiered' in source: + # Use the dateadded as the release date for premiered sources + release_date = movie['dateadded'] + if hasattr(release_date, 'isoformat'): + release_date = release_date.isoformat() + print(f"✅ Using premiered date for {imdb_id}: {release_date}") + + # Update the database if we found a release date + if release_date: + db.upsert_movie_dates(imdb_id, release_date, movie['dateadded'], source, True) + updated_count += 1 + print(f"📝 Updated release date for {imdb_id}") + + processed_count += 1 + + # Small delay to avoid overwhelming APIs + import time + time.sleep(0.1) + + except Exception as e: + print(f"❌ Error processing {imdb_id}: {e}") + processed_count += 1 + continue + + print(f"✅ BACKFILL COMPLETED: Processed {processed_count} movies, updated {updated_count} with release dates") + + return { + "success": True, + "message": f"Backfill completed: processed {processed_count} movies, updated {updated_count} with release dates", + "movies_processed": processed_count, + "movies_updated": updated_count, + "movies_found": len(movies_to_backfill) + } + + except Exception as e: + return { + "success": False, + "error": str(e), + "message": "Failed to backfill movie release dates" + } + + # --------------------------- # Route Registration # --------------------------- @@ -1307,6 +1402,10 @@ def register_routes(app, dependencies: dict): async def _cleanup_orphaned_series(): return await cleanup_orphaned_series(dependencies) + @app.post("/database/backfill/movie-release-dates") + async def _backfill_movie_release_dates(): + return await backfill_movie_release_dates(dependencies) + @app.post("/manual/scan") async def _manual_scan(background_tasks: BackgroundTasks, path: Optional[str] = None, scan_type: str = "both", scan_mode: str = "smart"): return await manual_scan(background_tasks, path, scan_type, scan_mode, dependencies) diff --git a/processors/movie_processor.py b/processors/movie_processor.py index 692acac..4b18192 100644 --- a/processors/movie_processor.py +++ b/processors/movie_processor.py @@ -530,7 +530,8 @@ class MovieProcessor: # Compare dates - prefer release date if it's reasonable if self._should_prefer_release_over_file_date(digital_date, digital_source, released, imdb_id): _log("INFO", f"✅ Movie {imdb_id}: Preferring digital release date {digital_date} over file date") - return digital_date, digital_source, released + # When using digital release date, store it as both dateadded and released + return digital_date, digital_source, digital_date else: # Convert file date to local timezone for NFO files local_file_date = convert_utc_to_local(import_date) @@ -545,7 +546,8 @@ class MovieProcessor: return local_import_date, import_source, released elif digital_date: _log("INFO", f"✅ Movie {imdb_id}: Using digital release date {digital_date} from {digital_source}") - return digital_date, digital_source, released + # When using digital release date, store it as both dateadded and released + return digital_date, digital_source, digital_date else: _log("WARNING", f"⚠️ Movie {imdb_id}: No import date OR digital release date found - trying additional fallbacks") @@ -553,13 +555,15 @@ class MovieProcessor: radarr_premiered = self._get_radarr_nfo_premiered_date(movie_path) if radarr_premiered: _log("INFO", f"✅ Movie {imdb_id}: Using Radarr NFO premiered date {radarr_premiered}") - return radarr_premiered, "radarr:nfo.premiered", released + # When using Radarr NFO premiered date, store it as both dateadded and released + return radarr_premiered, "radarr:nfo.premiered", radarr_premiered else: # digital_then_import # Try digital release first digital_date, digital_source = self._get_digital_release_date(imdb_id) if digital_date: - return digital_date, digital_source, released + # When using digital release date, store it as both dateadded and released + return digital_date, digital_source, digital_date # Fall back to import history if radarr_movie: