diff --git a/VERSION b/VERSION index 9fe31a5..6791995 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.10.0-skipped-tracking-v5 +2.10.0-skipped-imdb-edit diff --git a/api/web_routes.py b/api/web_routes.py index df0be48..5d19dcf 100644 --- a/api/web_routes.py +++ b/api/web_routes.py @@ -1524,7 +1524,42 @@ def register_web_routes(app, dependencies): except Exception as e: print(f"❌ Error deleting movie: {e}") raise HTTPException(status_code=500, detail=f"Failed to delete movie: {str(e)}") - + + @app.post("/api/movies/{old_imdb_id}/migrate-imdb") + async def api_migrate_movie_imdb(old_imdb_id: str, request: Request): + """Migrate a movie from placeholder IMDb ID to real IMDb ID""" + db = dependencies["db"] + + try: + data = await request.json() + new_imdb_id = data.get('new_imdb_id') + + if not new_imdb_id: + raise HTTPException(status_code=422, detail="new_imdb_id is required") + + # Normalize IMDb ID + if not new_imdb_id.startswith('tt'): + new_imdb_id = f"tt{new_imdb_id}" + + success = db.migrate_movie_imdb_id(old_imdb_id, new_imdb_id) + + if success: + return { + "success": True, + "status": "success", + "message": f"Migrated movie from {old_imdb_id} to {new_imdb_id}", + "old_imdb_id": old_imdb_id, + "new_imdb_id": new_imdb_id + } + else: + raise HTTPException(status_code=404, detail="Movie not found") + + except HTTPException: + raise + except Exception as e: + print(f"❌ Error migrating movie IMDb ID: {e}") + raise HTTPException(status_code=500, detail=f"Failed to migrate IMDb ID: {str(e)}") + # TV series endpoints @app.get("/api/series") async def api_series_list(skip: int = 0, limit: int = 50, search: str = None, @@ -1542,7 +1577,42 @@ def register_web_routes(app, dependencies): @app.get("/api/series/debug/date-distribution") async def api_debug_series_date_distribution(): return await debug_series_date_distribution(dependencies) - + + @app.post("/api/series/{old_imdb_id}/migrate-imdb") + async def api_migrate_series_imdb(old_imdb_id: str, request: Request): + """Migrate a series and all its episodes from placeholder IMDb ID to real IMDb ID""" + db = dependencies["db"] + + try: + data = await request.json() + new_imdb_id = data.get('new_imdb_id') + + if not new_imdb_id: + raise HTTPException(status_code=422, detail="new_imdb_id is required") + + # Normalize IMDb ID + if not new_imdb_id.startswith('tt'): + new_imdb_id = f"tt{new_imdb_id}" + + success = db.migrate_series_imdb_id(old_imdb_id, new_imdb_id) + + if success: + return { + "success": True, + "status": "success", + "message": f"Migrated series and episodes from {old_imdb_id} to {new_imdb_id}", + "old_imdb_id": old_imdb_id, + "new_imdb_id": new_imdb_id + } + else: + raise HTTPException(status_code=404, detail="Series not found") + + except HTTPException: + raise + except Exception as e: + print(f"❌ Error migrating series IMDb ID: {e}") + raise HTTPException(status_code=500, detail=f"Failed to migrate IMDb ID: {str(e)}") + # Episode endpoints @app.post("/api/episodes/{imdb_id}/{season}/{episode}/update-date") async def api_update_episode_date(imdb_id: str, season: int, episode: int, diff --git a/core/database.py b/core/database.py index b0efd92..ea3cd64 100644 --- a/core/database.py +++ b/core/database.py @@ -437,6 +437,81 @@ class NFOGuardDatabase: """, (imdb_id, media_type, event_type, datetime.utcnow().isoformat(), json.dumps(details) if details else None)) + def migrate_movie_imdb_id(self, old_imdb_id: str, new_imdb_id: str) -> bool: + """Migrate a movie from placeholder IMDb ID to real IMDb ID""" + with self.get_connection() as conn: + cursor = conn.cursor() + + # Check if old record exists + cursor.execute("SELECT * FROM movies WHERE imdb_id = %s", (old_imdb_id,)) + old_record = cursor.fetchone() + if not old_record: + return False + + old_data = dict(old_record) + + # Check if new IMDb ID already exists + cursor.execute("SELECT * FROM movies WHERE imdb_id = %s", (new_imdb_id,)) + if cursor.fetchone(): + # New IMDb ID already exists, just delete the old one + cursor.execute("DELETE FROM movies WHERE imdb_id = %s", (old_imdb_id,)) + return True + + # Create new record with correct IMDb ID + cursor.execute(""" + INSERT INTO movies (imdb_id, title, year, path, released, dateadded, source, + has_video_file, last_updated, skipped, skip_reason) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + """, (new_imdb_id, old_data.get('title'), old_data.get('year'), + old_data.get('path'), old_data.get('released'), old_data.get('dateadded'), + old_data.get('source'), old_data.get('has_video_file'), + datetime.utcnow(), False, None)) # Clear skipped status + + # Delete old placeholder record + cursor.execute("DELETE FROM movies WHERE imdb_id = %s", (old_imdb_id,)) + return True + + def migrate_series_imdb_id(self, old_imdb_id: str, new_imdb_id: str) -> bool: + """Migrate a series and all its episodes from placeholder IMDb ID to real IMDb ID""" + with self.get_connection() as conn: + cursor = conn.cursor() + + # Check if old series exists + cursor.execute("SELECT * FROM series WHERE imdb_id = %s", (old_imdb_id,)) + old_series = cursor.fetchone() + if not old_series: + return False + + old_series_data = dict(old_series) + + # Check if new series IMDb ID already exists + cursor.execute("SELECT * FROM series WHERE imdb_id = %s", (new_imdb_id,)) + if cursor.fetchone(): + # New series already exists, migrate episodes and delete old series + cursor.execute(""" + UPDATE episodes SET imdb_id = %s WHERE imdb_id = %s + """, (new_imdb_id, old_imdb_id)) + cursor.execute("DELETE FROM series WHERE imdb_id = %s", (old_imdb_id,)) + return True + + # Create new series record + cursor.execute(""" + INSERT INTO series (imdb_id, path, last_updated, metadata) + VALUES (%s, %s, %s, %s) + """, (new_imdb_id, old_series_data.get('path'), + datetime.utcnow(), old_series_data.get('metadata'))) + + # Migrate all episodes to new series IMDb ID and clear skipped status + cursor.execute(""" + UPDATE episodes + SET imdb_id = %s, skipped = FALSE, skip_reason = NULL + WHERE imdb_id = %s + """, (new_imdb_id, old_imdb_id)) + + # Delete old placeholder series + cursor.execute("DELETE FROM series WHERE imdb_id = %s", (old_imdb_id,)) + return True + def get_stats(self) -> Dict[str, Any]: """Get database statistics""" with self.get_connection() as conn: diff --git a/nfoguard-web/static/index.html b/nfoguard-web/static/index.html index c50f043..77e3b0b 100644 --- a/nfoguard-web/static/index.html +++ b/nfoguard-web/static/index.html @@ -4,7 +4,7 @@
Database Management & Reporting