This commit is contained in:
+165
-207
@@ -2300,228 +2300,186 @@ def register_routes(app, dependencies: dict):
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Scan episodes
|
# Use filesystem-first approach: find all NFO files missing dateadded
|
||||||
print("📺 Scanning episodes for missing dateadded elements")
|
print("📺 Scanning NFO files directly for missing dateadded elements")
|
||||||
episode_query = """
|
|
||||||
SELECT DISTINCT e.imdb_id, e.season, e.episode, s.path as series_path, e.dateadded, e.has_video_file, e.source
|
|
||||||
FROM episodes e
|
|
||||||
JOIN series s ON e.imdb_id = s.imdb_id
|
|
||||||
WHERE e.has_video_file = TRUE
|
|
||||||
ORDER BY e.imdb_id, e.season, e.episode
|
|
||||||
"""
|
|
||||||
|
|
||||||
with db.get_connection() as conn:
|
# Get TV and Movie library paths from config
|
||||||
cursor = conn.cursor()
|
tv_path = getattr(config, 'tv_library_path', '/media/TV')
|
||||||
cursor.execute(episode_query)
|
movie_path = getattr(config, 'movie_library_path', '/media/Movies')
|
||||||
episodes = [dict(row) for row in cursor.fetchall()]
|
|
||||||
|
|
||||||
print(f"📊 Found {len(episodes)} episodes in database")
|
print(f"📁 Scanning TV path: {tv_path}")
|
||||||
|
print(f"📁 Scanning Movie path: {movie_path}")
|
||||||
|
|
||||||
processed_count = 0
|
import subprocess
|
||||||
for episode in episodes:
|
import re
|
||||||
processed_count += 1
|
|
||||||
|
|
||||||
# Progress logging every 1000 items
|
# Find all NFO files and check which ones are missing dateadded
|
||||||
if processed_count % 1000 == 0:
|
# This is much faster than database-driven approach
|
||||||
elapsed = time.time() - scan_start_time
|
missing_nfo_files = []
|
||||||
print(f"📊 Progress: {processed_count}/{len(episodes)} episodes processed in {elapsed:.1f}s, {len(missing_items['episodes'])} missing found so far")
|
|
||||||
|
|
||||||
# Time-based exit (max 3 minutes for episodes)
|
# Find all NFO files in TV directory
|
||||||
if time.time() - scan_start_time > 180:
|
if os.path.exists(tv_path):
|
||||||
print(f"⏰ Episode scan timeout after 3 minutes - processed {processed_count}/{len(episodes)} episodes")
|
print("🔍 Finding all TV NFO files...")
|
||||||
break
|
|
||||||
|
|
||||||
# Early exit if we find too many missing items (performance safety)
|
|
||||||
if len(missing_items['episodes']) > 1000:
|
|
||||||
print(f"⚠️ Found {len(missing_items['episodes'])} missing episodes - stopping scan to prevent performance issues")
|
|
||||||
break
|
|
||||||
# Build NFO path using series path
|
|
||||||
series_name = os.path.basename(episode["series_path"])
|
|
||||||
|
|
||||||
# Debug specific episodes we know should be missing
|
|
||||||
if "Hudson" in series_name and episode['season'] == 8 and episode['episode'] == 5:
|
|
||||||
print(f"🔍 DEBUG: Processing Hudson & Rex S08E05")
|
|
||||||
print(f" Series path: {episode['series_path']}")
|
|
||||||
print(f" DB dateadded: {episode['dateadded']}")
|
|
||||||
print(f" DB source: {episode.get('source', 'unknown')}")
|
|
||||||
|
|
||||||
if "Star Trek" in series_name and episode['season'] == 3 and episode['episode'] == 7:
|
|
||||||
print(f"🔍 DEBUG: Processing Star Trek SNW S03E07")
|
|
||||||
print(f" Series path: {episode['series_path']}")
|
|
||||||
print(f" DB dateadded: {episode['dateadded']}")
|
|
||||||
print(f" DB source: {episode.get('source', 'unknown')}")
|
|
||||||
|
|
||||||
# Try multiple NFO file naming patterns that might exist
|
|
||||||
season_dir = os.path.join(episode["series_path"], f"Season {episode['season']:02d}")
|
|
||||||
|
|
||||||
# Handle Season 0 (Specials) naming
|
|
||||||
if episode['season'] == 0:
|
|
||||||
season_dir = os.path.join(episode["series_path"], "Specials")
|
|
||||||
|
|
||||||
nfo_patterns = [
|
|
||||||
f"S{episode['season']:02d}E{episode['episode']:02d}.nfo",
|
|
||||||
f"{series_name}-S{episode['season']:02d}E{episode['episode']:02d}*.nfo"
|
|
||||||
]
|
|
||||||
|
|
||||||
nfo_path = None
|
|
||||||
# Find the actual NFO file that exists
|
|
||||||
if os.path.exists(season_dir):
|
|
||||||
for pattern in nfo_patterns:
|
|
||||||
if '*' in pattern:
|
|
||||||
# Use glob for wildcard patterns
|
|
||||||
import glob
|
|
||||||
matches = glob.glob(os.path.join(season_dir, pattern))
|
|
||||||
if matches:
|
|
||||||
nfo_path = matches[0] # Use first match
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# Direct file check
|
|
||||||
test_path = os.path.join(season_dir, pattern)
|
|
||||||
if os.path.exists(test_path):
|
|
||||||
nfo_path = test_path
|
|
||||||
break
|
|
||||||
|
|
||||||
# Skip if no NFO file found (already checked above)
|
|
||||||
if not nfo_path:
|
|
||||||
# Debug specific episodes
|
|
||||||
if "Hudson" in series_name and episode['season'] == 8 and episode['episode'] == 5:
|
|
||||||
print(f"🔍 DEBUG: Hudson & Rex S08E05 - NFO file not found at expected paths")
|
|
||||||
print(f" Series path: {episode['series_path']}")
|
|
||||||
print(f" Season dir: {season_dir}")
|
|
||||||
print(f" DB source: {episode.get('source', 'unknown')}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check if NFO file has dateadded element
|
|
||||||
try:
|
try:
|
||||||
tree = ET.parse(nfo_path)
|
# Use find to get all NFO files
|
||||||
root = tree.getroot()
|
find_result = subprocess.run(
|
||||||
dateadded_elem = root.find("dateadded")
|
['find', tv_path, '-name', '*.nfo', '-type', 'f'],
|
||||||
|
capture_output=True, text=True, timeout=60
|
||||||
|
)
|
||||||
|
|
||||||
# NFO file is missing dateadded element
|
if find_result.returncode == 0:
|
||||||
if dateadded_elem is None or not dateadded_elem.text or not dateadded_elem.text.strip():
|
tv_nfo_files = find_result.stdout.strip().split('\n')
|
||||||
# Special logging for database sync issues
|
tv_nfo_files = [f for f in tv_nfo_files if f] # Remove empty strings
|
||||||
db_source = episode.get("source", "unknown")
|
print(f"📊 Found {len(tv_nfo_files)} TV NFO files")
|
||||||
if db_source == "Nfo_File_Existing":
|
|
||||||
print(f"🔍 DATABASE SYNC ISSUE: {series_name} S{episode['season']:02d}E{episode['episode']:02d} - DB says 'Nfo_File_Existing' but NFO missing dateadded")
|
# Check each NFO file for missing dateadded
|
||||||
print(f" NFO Path: {nfo_path}")
|
for i, nfo_file in enumerate(tv_nfo_files):
|
||||||
print(f" DB dateadded: {episode['dateadded']}")
|
if i % 1000 == 0 and i > 0:
|
||||||
|
elapsed = time.time() - scan_start_time
|
||||||
|
print(f"📊 Progress: {i}/{len(tv_nfo_files)} TV NFO files checked in {elapsed:.1f}s, {len(missing_nfo_files)} missing found")
|
||||||
|
|
||||||
|
# Timeout check
|
||||||
|
if time.time() - scan_start_time > 180: # 3 minutes
|
||||||
|
print(f"⏰ TV NFO scan timeout after 3 minutes - checked {i}/{len(tv_nfo_files)} files")
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Use grep to quickly check if dateadded exists
|
||||||
|
grep_result = subprocess.run(
|
||||||
|
['grep', '-l', '<dateadded>', nfo_file],
|
||||||
|
capture_output=True, text=True, timeout=5
|
||||||
|
)
|
||||||
|
|
||||||
|
# If grep returns non-zero, dateadded is missing
|
||||||
|
if grep_result.returncode != 0:
|
||||||
|
missing_nfo_files.append(nfo_file)
|
||||||
|
|
||||||
|
# Log specific files we're looking for
|
||||||
|
if 'Hudson' in nfo_file and 'S08E05' in nfo_file:
|
||||||
|
print(f"🔍 FOUND MISSING: Hudson & Rex S08E05 at {nfo_file}")
|
||||||
|
if 'Star Trek' in nfo_file and 'S03E07' in nfo_file:
|
||||||
|
print(f"🔍 FOUND MISSING: Star Trek SNW S03E07 at {nfo_file}")
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
print(f"⏰ Timeout checking {nfo_file}")
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Error checking {nfo_file}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(f"❌ Error finding TV NFO files: {find_result.stderr}")
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
print("⏰ Find command timeout for TV files")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error scanning TV directory: {e}")
|
||||||
|
else:
|
||||||
|
print(f"❌ TV path does not exist: {tv_path}")
|
||||||
|
|
||||||
|
print(f"📊 Found {len(missing_nfo_files)} NFO files missing dateadded elements")
|
||||||
|
|
||||||
|
# Convert file paths to episode/movie information
|
||||||
|
for nfo_file in missing_nfo_files:
|
||||||
|
try:
|
||||||
|
# Parse episode info from file path
|
||||||
|
# Example: /media/TV/tv/Series Name/Season 08/Series-S08E05-Episode.nfo
|
||||||
|
path_parts = nfo_file.split('/')
|
||||||
|
|
||||||
|
# Look for season/episode pattern
|
||||||
|
season_match = re.search(r'Season\s+(\d+)', nfo_file, re.IGNORECASE)
|
||||||
|
episode_match = re.search(r'S(\d+)E(\d+)', os.path.basename(nfo_file), re.IGNORECASE)
|
||||||
|
|
||||||
|
if season_match and episode_match:
|
||||||
|
season = int(episode_match.group(1))
|
||||||
|
episode = int(episode_match.group(2))
|
||||||
|
|
||||||
|
# Extract series name from path
|
||||||
|
series_path = os.path.dirname(os.path.dirname(nfo_file))
|
||||||
|
series_name = os.path.basename(series_path)
|
||||||
|
|
||||||
missing_items["episodes"].append({
|
missing_items["episodes"].append({
|
||||||
"imdb_id": episode["imdb_id"],
|
"imdb_id": "unknown", # We'll lookup later if needed
|
||||||
"season": episode["season"],
|
"season": season,
|
||||||
"episode": episode["episode"],
|
"episode": episode,
|
||||||
"series_name": series_name,
|
"series_name": series_name,
|
||||||
"series_path": episode["series_path"],
|
"series_path": series_path,
|
||||||
"dateadded": episode["dateadded"],
|
"dateadded": None,
|
||||||
"nfo_path": nfo_path,
|
"nfo_path": nfo_file,
|
||||||
"db_source": db_source,
|
"reason": "NFO missing dateadded element (filesystem scan)"
|
||||||
"reason": "NFO missing dateadded element"
|
|
||||||
})
|
})
|
||||||
except ET.ParseError as e:
|
|
||||||
print(f"⚠️ Could not parse NFO file {nfo_path}: {e}")
|
|
||||||
missing_items["episodes"].append({
|
|
||||||
"imdb_id": episode["imdb_id"],
|
|
||||||
"season": episode["season"],
|
|
||||||
"episode": episode["episode"],
|
|
||||||
"series_name": series_name,
|
|
||||||
"series_path": episode["series_path"],
|
|
||||||
"dateadded": episode["dateadded"],
|
|
||||||
"nfo_path": nfo_path,
|
|
||||||
"error": f"Parse error: {str(e)}"
|
|
||||||
})
|
|
||||||
|
|
||||||
# Scan movies
|
except Exception as e:
|
||||||
print("🎬 Scanning movies for missing dateadded elements")
|
print(f"⚠️ Error parsing NFO file path {nfo_file}: {e}")
|
||||||
movie_query = """
|
|
||||||
SELECT DISTINCT imdb_id, path, dateadded, has_video_file
|
|
||||||
FROM movies
|
|
||||||
WHERE has_video_file = TRUE
|
|
||||||
ORDER BY path
|
|
||||||
"""
|
|
||||||
|
|
||||||
with db.get_connection() as conn:
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute(movie_query)
|
|
||||||
movies = [dict(row) for row in cursor.fetchall()]
|
|
||||||
|
|
||||||
print(f"📊 Found {len(movies)} movies in database")
|
|
||||||
|
|
||||||
processed_count = 0
|
|
||||||
for movie in movies:
|
|
||||||
processed_count += 1
|
|
||||||
|
|
||||||
# Progress logging every 500 items
|
|
||||||
if processed_count % 500 == 0:
|
|
||||||
elapsed = time.time() - scan_start_time
|
|
||||||
print(f"📊 Progress: {processed_count}/{len(movies)} movies processed in {elapsed:.1f}s, {len(missing_items['movies'])} missing found so far")
|
|
||||||
|
|
||||||
# Time-based exit (max 5 minutes total)
|
|
||||||
if time.time() - scan_start_time > 300:
|
|
||||||
print(f"⏰ Total scan timeout after 5 minutes - processed {processed_count}/{len(movies)} movies")
|
|
||||||
break
|
|
||||||
|
|
||||||
# Early exit if we find too many missing items (performance safety)
|
|
||||||
if len(missing_items['movies']) > 500:
|
|
||||||
print(f"⚠️ Found {len(missing_items['movies'])} missing movies - stopping scan to prevent performance issues")
|
|
||||||
break
|
|
||||||
# Build NFO path using movie path
|
|
||||||
movie_title = os.path.basename(movie["path"])
|
|
||||||
|
|
||||||
# Try multiple NFO file naming patterns that might exist
|
|
||||||
nfo_patterns = [
|
|
||||||
f"{movie_title}.nfo",
|
|
||||||
"movie.nfo",
|
|
||||||
f"{movie_title}*.nfo"
|
|
||||||
]
|
|
||||||
|
|
||||||
nfo_path = None
|
|
||||||
# Find the actual NFO file that exists
|
|
||||||
if os.path.exists(movie["path"]):
|
|
||||||
for pattern in nfo_patterns:
|
|
||||||
if '*' in pattern:
|
|
||||||
# Use glob for wildcard patterns
|
|
||||||
import glob
|
|
||||||
matches = glob.glob(os.path.join(movie["path"], pattern))
|
|
||||||
if matches:
|
|
||||||
nfo_path = matches[0] # Use first match
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# Direct file check
|
|
||||||
test_path = os.path.join(movie["path"], pattern)
|
|
||||||
if os.path.exists(test_path):
|
|
||||||
nfo_path = test_path
|
|
||||||
break
|
|
||||||
|
|
||||||
# Skip if no NFO file found
|
|
||||||
if not nfo_path:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check if NFO file has dateadded element
|
# Scan movies using filesystem grep approach
|
||||||
try:
|
print("🎬 Scanning movie NFO files for missing dateadded elements")
|
||||||
tree = ET.parse(nfo_path)
|
|
||||||
root = tree.getroot()
|
|
||||||
dateadded_elem = root.find("dateadded")
|
|
||||||
|
|
||||||
# NFO file is missing dateadded element
|
# Find all NFO files in Movie directory
|
||||||
if dateadded_elem is None or not dateadded_elem.text or not dateadded_elem.text.strip():
|
if os.path.exists(movie_path):
|
||||||
missing_items["movies"].append({
|
print(f"🔍 Finding all Movie NFO files in {movie_path}...")
|
||||||
"imdb_id": movie["imdb_id"],
|
try:
|
||||||
"title": movie_title,
|
# Use find to get all NFO files
|
||||||
"path": movie["path"],
|
find_result = subprocess.run(
|
||||||
"dateadded": movie["dateadded"],
|
['find', movie_path, '-name', '*.nfo', '-type', 'f'],
|
||||||
"nfo_path": nfo_path,
|
capture_output=True, text=True, timeout=60
|
||||||
"reason": "NFO missing dateadded element"
|
)
|
||||||
})
|
|
||||||
except ET.ParseError as e:
|
if find_result.returncode == 0:
|
||||||
print(f"⚠️ Could not parse NFO file {nfo_path}: {e}")
|
movie_nfo_files = find_result.stdout.strip().split('\n')
|
||||||
missing_items["movies"].append({
|
movie_nfo_files = [f for f in movie_nfo_files if f] # Remove empty strings
|
||||||
"imdb_id": movie["imdb_id"],
|
print(f"📊 Found {len(movie_nfo_files)} Movie NFO files")
|
||||||
"title": movie_title,
|
|
||||||
"path": movie["path"],
|
# Check each NFO file for missing dateadded
|
||||||
"dateadded": movie["dateadded"],
|
for i, nfo_file in enumerate(movie_nfo_files):
|
||||||
"nfo_path": nfo_path,
|
if i % 500 == 0 and i > 0:
|
||||||
"error": f"Parse error: {str(e)}"
|
elapsed = time.time() - scan_start_time
|
||||||
})
|
print(f"📊 Progress: {i}/{len(movie_nfo_files)} Movie NFO files checked in {elapsed:.1f}s, {len(missing_items['movies'])} missing found")
|
||||||
|
|
||||||
|
# Timeout check
|
||||||
|
if time.time() - scan_start_time > 300: # 5 minutes total
|
||||||
|
print(f"⏰ Movie NFO scan timeout after 5 minutes - checked {i}/{len(movie_nfo_files)} files")
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Use grep to quickly check if dateadded exists
|
||||||
|
grep_result = subprocess.run(
|
||||||
|
['grep', '-l', '<dateadded>', nfo_file],
|
||||||
|
capture_output=True, text=True, timeout=5
|
||||||
|
)
|
||||||
|
|
||||||
|
# If grep returns non-zero, dateadded is missing
|
||||||
|
if grep_result.returncode != 0:
|
||||||
|
# Extract movie info from file path
|
||||||
|
movie_dir = os.path.dirname(nfo_file)
|
||||||
|
movie_title = os.path.basename(movie_dir)
|
||||||
|
|
||||||
|
missing_items["movies"].append({
|
||||||
|
"imdb_id": "unknown", # We'll lookup later if needed
|
||||||
|
"title": movie_title,
|
||||||
|
"path": movie_dir,
|
||||||
|
"dateadded": None,
|
||||||
|
"nfo_path": nfo_file,
|
||||||
|
"reason": "NFO missing dateadded element (filesystem scan)"
|
||||||
|
})
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
print(f"⏰ Timeout checking {nfo_file}")
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Error checking {nfo_file}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(f"❌ Error finding Movie NFO files: {find_result.stderr}")
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
print("⏰ Find command timeout for Movie files")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error scanning Movie directory: {e}")
|
||||||
|
else:
|
||||||
|
print(f"❌ Movie path does not exist: {movie_path}")
|
||||||
|
|
||||||
total_missing = len(missing_items["episodes"]) + len(missing_items["movies"])
|
total_missing = len(missing_items["episodes"]) + len(missing_items["movies"])
|
||||||
print(f"✅ NFO repair scan complete: {total_missing} items missing dateadded elements")
|
print(f"✅ NFO repair scan complete: {total_missing} items missing dateadded elements")
|
||||||
|
|||||||
Reference in New Issue
Block a user