diff --git a/api/routes.py b/api/routes.py index c5435c7..ac43d65 100644 --- a/api/routes.py +++ b/api/routes.py @@ -2579,6 +2579,9 @@ def register_routes(app, dependencies: dict): print("🔍 Looking up dateadded values from database for missing items...") # Enhance episodes with database data + episodes_with_dates = 0 + episodes_missing_db = 0 + for episode in missing_items["episodes"]: if episode["imdb_id"] != "unknown": try: @@ -2595,12 +2598,47 @@ def register_routes(app, dependencies: dict): if result: episode["dateadded"] = result["dateadded"] episode["source"] = result.get("source", "database") + episodes_with_dates += 1 + print(f"✅ Found database date for {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d}: {result['dateadded']}") else: - print(f"⚠️ No database entry for {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d}") + episodes_missing_db += 1 + # Try to find other episodes from same series to estimate date + cursor.execute(""" + SELECT dateadded, season, episode + FROM episodes + WHERE imdb_id = %s AND dateadded IS NOT NULL + ORDER BY season DESC, episode DESC + LIMIT 3 + """, (episode["imdb_id"],)) + + similar_episodes = cursor.fetchall() + if similar_episodes: + latest_date = similar_episodes[0]["dateadded"] + episode["dateadded"] = latest_date + episode["source"] = f"estimated_from_series_latest" + print(f"🔮 Estimated date for {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d} from latest episode: {latest_date}") + else: + # Use NFO file modification time as fallback + try: + import os + from datetime import datetime + if os.path.exists(episode["nfo_path"]): + file_mtime = os.path.getmtime(episode["nfo_path"]) + file_date = datetime.fromtimestamp(file_mtime) + episode["dateadded"] = file_date + episode["source"] = "nfo_file_mtime" + print(f"📅 Using NFO file date for {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d}: {file_date}") + else: + print(f"❌ No fallback date available for {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d}") + except Exception as fallback_error: + print(f"⚠️ Fallback date lookup failed for {episode['imdb_id']}: {fallback_error}") except Exception as e: print(f"⚠️ Database lookup failed for episode {episode['imdb_id']}: {e}") # Enhance movies with database data + movies_with_dates = 0 + movies_missing_db = 0 + for movie in missing_items["movies"]: if movie["imdb_id"] != "unknown": try: @@ -2617,11 +2655,34 @@ def register_routes(app, dependencies: dict): if result: movie["dateadded"] = result["dateadded"] movie["source"] = result.get("source", "database") + movies_with_dates += 1 + print(f"✅ Found database date for movie {movie['imdb_id']}: {result['dateadded']}") else: - print(f"⚠️ No database entry for movie {movie['imdb_id']}") + movies_missing_db += 1 + # Use NFO file modification time as fallback for movies + try: + import os + from datetime import datetime + if os.path.exists(movie["nfo_path"]): + file_mtime = os.path.getmtime(movie["nfo_path"]) + file_date = datetime.fromtimestamp(file_mtime) + movie["dateadded"] = file_date + movie["source"] = "nfo_file_mtime" + print(f"📅 Using NFO file date for movie {movie['imdb_id']}: {file_date}") + else: + print(f"❌ No fallback date available for movie {movie['imdb_id']}") + except Exception as fallback_error: + print(f"⚠️ Fallback date lookup failed for movie {movie['imdb_id']}: {fallback_error}") except Exception as e: print(f"⚠️ Database lookup failed for movie {movie['imdb_id']}: {e}") + # Print summary statistics + total_episodes = len(missing_items["episodes"]) + total_movies = len(missing_items["movies"]) + print(f"📊 Database lookup summary:") + print(f" Episodes: {episodes_with_dates}/{total_episodes} found in DB, {episodes_missing_db} missing from DB") + print(f" Movies: {movies_with_dates}/{total_movies} found in DB, {movies_missing_db} missing from DB") + total_missing = len(missing_items["episodes"]) + len(missing_items["movies"]) print(f"✅ NFO repair scan complete: {total_missing} items missing dateadded elements") @@ -2707,18 +2768,52 @@ def register_routes(app, dependencies: dict): # Write back to file tree.write(nfo_path, encoding='utf-8', xml_declaration=True) - fixed_count += 1 - results.append({ - "type": "episode", - "imdb_id": episode["imdb_id"], - "season": episode["season"], - "episode": episode["episode"], - "nfo_path": nfo_path, - "dateadded": nfo_dateadded, - "status": "fixed" - }) - - print(f"✅ Fixed episode {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d}") + # VERIFY the write was successful by re-reading the file + try: + verify_tree = ET.parse(nfo_path) + verify_root = verify_tree.getroot() + verify_dateadded = verify_root.find("dateadded") + + if verify_dateadded is not None and verify_dateadded.text and verify_dateadded.text.strip(): + # Verification successful + fixed_count += 1 + results.append({ + "type": "episode", + "imdb_id": episode["imdb_id"], + "season": episode["season"], + "episode": episode["episode"], + "nfo_path": nfo_path, + "dateadded": nfo_dateadded, + "verified_dateadded": verify_dateadded.text.strip(), + "status": "fixed_and_verified" + }) + print(f"✅ Fixed and verified episode {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d}") + else: + # Verification failed - dateadded missing after write + failed_count += 1 + results.append({ + "type": "episode", + "imdb_id": episode["imdb_id"], + "season": episode["season"], + "episode": episode["episode"], + "nfo_path": nfo_path, + "dateadded": nfo_dateadded, + "status": "write_failed_verification" + }) + print(f"❌ Write verification FAILED for episode {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d} - dateadded not found after write") + except Exception as verify_error: + # Verification failed due to parse error + failed_count += 1 + results.append({ + "type": "episode", + "imdb_id": episode["imdb_id"], + "season": episode["season"], + "episode": episode["episode"], + "nfo_path": nfo_path, + "status": "verification_parse_error", + "error": str(verify_error) + }) + print(f"❌ Verification parse error for episode {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d}: {verify_error}") else: failed_count += 1 print(f"❌ NFO file not found: {nfo_path}") @@ -2759,17 +2854,49 @@ def register_routes(app, dependencies: dict): # Write back to file tree.write(nfo_path, encoding='utf-8', xml_declaration=True) - fixed_count += 1 - results.append({ - "type": "movie", - "imdb_id": movie["imdb_id"], - "title": movie["title"], - "nfo_path": nfo_path, - "dateadded": nfo_dateadded, - "status": "fixed" - }) - - print(f"✅ Fixed movie {movie['imdb_id']} ({movie['title']})") + # VERIFY the write was successful by re-reading the file + try: + verify_tree = ET.parse(nfo_path) + verify_root = verify_tree.getroot() + verify_dateadded = verify_root.find("dateadded") + + if verify_dateadded is not None and verify_dateadded.text and verify_dateadded.text.strip(): + # Verification successful + fixed_count += 1 + results.append({ + "type": "movie", + "imdb_id": movie["imdb_id"], + "title": movie["title"], + "nfo_path": nfo_path, + "dateadded": nfo_dateadded, + "verified_dateadded": verify_dateadded.text.strip(), + "status": "fixed_and_verified" + }) + print(f"✅ Fixed and verified movie {movie['imdb_id']} ({movie['title']})") + else: + # Verification failed - dateadded missing after write + failed_count += 1 + results.append({ + "type": "movie", + "imdb_id": movie["imdb_id"], + "title": movie["title"], + "nfo_path": nfo_path, + "dateadded": nfo_dateadded, + "status": "write_failed_verification" + }) + print(f"❌ Write verification FAILED for movie {movie['imdb_id']} ({movie['title']}) - dateadded not found after write") + except Exception as verify_error: + # Verification failed due to parse error + failed_count += 1 + results.append({ + "type": "movie", + "imdb_id": movie["imdb_id"], + "title": movie["title"], + "nfo_path": nfo_path, + "status": "verification_parse_error", + "error": str(verify_error) + }) + print(f"❌ Verification parse error for movie {movie['imdb_id']} ({movie['title']}): {verify_error}") else: failed_count += 1 print(f"❌ NFO file not found: {nfo_path}") @@ -2780,11 +2907,24 @@ def register_routes(app, dependencies: dict): print(f"✅ NFO repair fix complete: {fixed_count} fixed, {failed_count} failed") + # Final summary with detailed statistics + verification_summary = { + "fixed_and_verified": sum(1 for r in results if r.get("status") == "fixed_and_verified"), + "write_failed_verification": sum(1 for r in results if r.get("status") == "write_failed_verification"), + "verification_parse_error": sum(1 for r in results if r.get("status") == "verification_parse_error") + } + + print(f"📊 Final summary: {fixed_count} fixed, {failed_count} failed") + print(f" ✅ Verified successful: {verification_summary['fixed_and_verified']}") + print(f" ❌ Write verification failed: {verification_summary['write_failed_verification']}") + print(f" ⚠️ Verification parse errors: {verification_summary['verification_parse_error']}") + return { "status": "success", "total_processed": total_episodes + total_movies, "fixed_count": fixed_count, "failed_count": failed_count, + "verification_summary": verification_summary, "results": results[:20] # Show first 20 for UI }