web: table update and nfo scan update
Local Docker Build (Dev) / build-dev (push) Successful in 5s

This commit is contained in:
2025-10-26 15:23:35 -04:00
parent 9fbb9e37ab
commit c6b6b0ef64
4 changed files with 168 additions and 42 deletions
+38 -23
View File
@@ -2391,7 +2391,7 @@ def register_routes(app, dependencies: dict):
imdb_id = "unknown" imdb_id = "unknown"
try: try:
# Parse the NFO file we already know exists (since grep found it missing dateadded) # Try to parse the NFO file we already know exists (since grep found it missing dateadded)
tree = ET.parse(nfo_file) tree = ET.parse(nfo_file)
root = tree.getroot() root = tree.getroot()
@@ -2418,26 +2418,43 @@ def register_routes(app, dependencies: dict):
imdb_text = content_match.group(1) imdb_text = content_match.group(1)
print(f"✅ Found IMDb {imdb_text} in NFO content: {os.path.basename(nfo_file)}") print(f"✅ Found IMDb {imdb_text} in NFO content: {os.path.basename(nfo_file)}")
return imdb_text return imdb_text
# Method 4: Fallback - check folder structure for episodes
if is_episode:
series_folder = os.path.dirname(os.path.dirname(nfo_file))
folder_name = os.path.basename(series_folder)
folder_match = re.search(r'\[imdb-(tt\d+)\]', folder_name, re.IGNORECASE)
if folder_match:
print(f"✅ Found IMDb {folder_match.group(1)} in folder: {folder_name}")
return folder_match.group(1)
else:
# For movies, check movie folder
movie_folder = os.path.dirname(nfo_file)
folder_name = os.path.basename(movie_folder)
folder_match = re.search(r'\[imdb-(tt\d+)\]', folder_name, re.IGNORECASE)
if folder_match:
print(f"✅ Found IMDb {folder_match.group(1)} in folder: {folder_name}")
return folder_match.group(1)
except ET.ParseError as e:
# Handle malformed XML by trying text-based search
print(f"⚠️ NFO XML parse error in {os.path.basename(nfo_file)}: {e}")
try:
# Fallback: Read as text and search for IMDb patterns
with open(nfo_file, 'r', encoding='utf-8', errors='ignore') as f:
nfo_text = f.read()
# Search for IMDb patterns in raw text
text_match = re.search(r'(tt\d{7,})', nfo_text)
if text_match:
imdb_text = text_match.group(1)
print(f"✅ Found IMDb {imdb_text} in NFO text content: {os.path.basename(nfo_file)}")
return imdb_text
except Exception as text_error:
print(f"⚠️ Error reading NFO as text {nfo_file}: {text_error}")
except Exception as e: except Exception as e:
print(f"⚠️ Error parsing NFO for IMDb in {nfo_file}: {e}") print(f"⚠️ Unexpected error parsing NFO {nfo_file}: {e}")
# Method 4: Fallback - check folder structure
if is_episode:
series_folder = os.path.dirname(os.path.dirname(nfo_file))
folder_name = os.path.basename(series_folder)
folder_match = re.search(r'\[imdb-(tt\d+)\]', folder_name, re.IGNORECASE)
if folder_match:
print(f"✅ Found IMDb {folder_match.group(1)} in folder: {folder_name}")
return folder_match.group(1)
else:
# For movies, check movie folder
movie_folder = os.path.dirname(nfo_file)
folder_name = os.path.basename(movie_folder)
folder_match = re.search(r'\[imdb-(tt\d+)\]', folder_name, re.IGNORECASE)
if folder_match:
print(f"✅ Found IMDb {folder_match.group(1)} in folder: {folder_name}")
return folder_match.group(1)
print(f"❌ No IMDb ID found in {os.path.basename(nfo_file)}") print(f"❌ No IMDb ID found in {os.path.basename(nfo_file)}")
return "unknown" return "unknown"
@@ -2568,7 +2585,7 @@ def register_routes(app, dependencies: dict):
with db.get_connection() as conn: with db.get_connection() as conn:
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute(""" cursor.execute("""
SELECT dateadded, source, added_by SELECT dateadded, source
FROM episodes FROM episodes
WHERE imdb_id = %s AND season = %s AND episode = %s WHERE imdb_id = %s AND season = %s AND episode = %s
LIMIT 1 LIMIT 1
@@ -2578,7 +2595,6 @@ def register_routes(app, dependencies: dict):
if result: if result:
episode["dateadded"] = result["dateadded"] episode["dateadded"] = result["dateadded"]
episode["source"] = result.get("source", "database") episode["source"] = result.get("source", "database")
episode["added_by"] = result.get("added_by", "unknown")
else: else:
print(f"⚠️ No database entry for {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d}") print(f"⚠️ No database entry for {episode['imdb_id']} S{episode['season']:02d}E{episode['episode']:02d}")
except Exception as e: except Exception as e:
@@ -2591,7 +2607,7 @@ def register_routes(app, dependencies: dict):
with db.get_connection() as conn: with db.get_connection() as conn:
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute(""" cursor.execute("""
SELECT dateadded, source, added_by SELECT dateadded, source
FROM movies FROM movies
WHERE imdb_id = %s WHERE imdb_id = %s
LIMIT 1 LIMIT 1
@@ -2601,7 +2617,6 @@ def register_routes(app, dependencies: dict):
if result: if result:
movie["dateadded"] = result["dateadded"] movie["dateadded"] = result["dateadded"]
movie["source"] = result.get("source", "database") movie["source"] = result.get("source", "database")
movie["added_by"] = result.get("added_by", "unknown")
else: else:
print(f"⚠️ No database entry for movie {movie['imdb_id']}") print(f"⚠️ No database entry for movie {movie['imdb_id']}")
except Exception as e: except Exception as e:
+91
View File
@@ -958,6 +958,97 @@ body {
border-radius: 0.375rem; border-radius: 0.375rem;
} }
/* NFO Repair Table Styling */
.nfo-repair-table-container {
background: white;
border-radius: 0.5rem;
box-shadow: var(--shadow);
overflow-x: auto;
overflow-y: auto;
max-height: 600px;
margin-bottom: 1rem;
border: 1px solid var(--border-color);
}
.nfo-repair-table {
width: 100%;
min-width: 800px;
border-collapse: collapse;
font-size: 0.875rem;
background: white;
}
.nfo-repair-table th {
background-color: var(--dark-color);
color: white;
padding: 0.75rem 0.5rem;
text-align: left;
font-weight: 600;
position: sticky;
top: 0;
z-index: 10;
}
.nfo-repair-table td {
padding: 0.75rem 0.5rem;
border-bottom: 1px solid var(--border-color);
vertical-align: top;
}
.nfo-repair-table tr:hover {
background-color: #f8f9fa;
}
.media-type-badge {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.media-type-badge.episode {
background-color: #e3f2fd;
color: #1976d2;
}
.media-type-badge.movie {
background-color: #f3e5f5;
color: #7b1fa2;
}
.status-ready {
color: #2e7d32;
font-weight: 600;
font-size: 0.75rem;
}
.status-missing {
color: #d32f2f;
font-weight: 600;
font-size: 0.75rem;
}
.text-center {
text-align: center;
}
.text-muted {
color: #6c757d;
}
.nfo-fix-actions {
margin-top: 1rem;
padding: 1rem;
background-color: #f8f9fa;
border-radius: 0.5rem;
text-align: center;
}
.nfo-fix-actions .btn {
margin-bottom: 0.5rem;
}
.admin-tools { .admin-tools {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NFOGuard - Database Management</title> <title>NFOGuard - Database Management</title>
<link rel="stylesheet" href="/static/css/styles.css?v=2.8.2-20241026-table-fix"> <link rel="stylesheet" href="/static/css/styles.css?v=2.8.2-20241026-nfo-table-redesign">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head> </head>
<body> <body>
@@ -533,6 +533,6 @@
<!-- Toast Notifications --> <!-- Toast Notifications -->
<div class="toast-container" id="toast-container"></div> <div class="toast-container" id="toast-container"></div>
<script src="/static/js/app.js?v=3.0.1-nfo-repair-movies"></script> <script src="/static/js/app.js?v=3.0.1-nfo-table-redesign"></script>
</body> </body>
</html> </html>
+37 -17
View File
@@ -1847,17 +1847,18 @@ async function checkMissingNFODates() {
html += ` html += `
<div class="missing-episodes"> <div class="missing-episodes">
<h5>Items with missing dateadded (showing first 50):</h5> <h5>Items with missing dateadded (showing first 50):</h5>
<div class="table-container"> <div class="nfo-repair-table-container">
<table class="results-table"> <table class="nfo-repair-table">
<thead> <thead>
<tr> <tr>
<th>Type</th> <th style="width: 8%">Type</th>
<th>IMDb ID</th> <th style="width: 12%">IMDb ID</th>
<th>Season</th> <th style="width: 8%">Season</th>
<th>Episode</th> <th style="width: 8%">Episode</th>
<th>Database Date</th> <th style="width: 15%">Database Date</th>
<th>Source</th> <th style="width: 10%">Source</th>
<th>NFO Path</th> <th style="width: 25%">Series/Movie</th>
<th style="width: 14%">Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -1865,20 +1866,39 @@ async function checkMissingNFODates() {
(response.items || response.episodes || []).forEach(item => { (response.items || response.episodes || []).forEach(item => {
const displayDate = item.dateadded ? new Date(item.dateadded).toLocaleString() : 'None'; const displayDate = item.dateadded ? new Date(item.dateadded).toLocaleString() : 'None';
const seriesName = item.series_name || item.title || 'Unknown';
const nfoFileName = item.nfo_path ? item.nfo_path.split('/').pop() : 'Unknown';
html += ` html += `
<tr> <tr>
<td>${item.media_type || 'episode'}</td> <td><span class="media-type-badge ${item.media_type || 'episode'}">${item.media_type || 'episode'}</span></td>
<td>${item.imdb_id}</td> <td><code>${item.imdb_id}</code></td>
<td>${item.season || 'N/A'}</td> <td class="text-center">${item.season || 'N/A'}</td>
<td>${item.episode || 'N/A'}</td> <td class="text-center">${item.episode || 'N/A'}</td>
<td>${displayDate}</td> <td><small>${displayDate}</small></td>
<td>${item.source || 'unknown'}</td> <td><small>${item.source || 'unknown'}</small></td>
<td><small>${item.nfo_path}</small></td> <td><strong>${seriesName}</strong><br><small class="text-muted">${nfoFileName}</small></td>
<td>
${item.dateadded ?
'<span class="status-ready">✅ Ready to fix</span>' :
'<span class="status-missing">❌ No DB date</span>'
}
</td>
</tr> </tr>
`; `;
}); });
html += '</tbody></table></div></div>'; html += `
</tbody>
</table>
</div>
<div class="nfo-fix-actions">
<button onclick="fixAllMissingNFODates()" class="btn btn-primary">
🔧 Fix All Missing dateadded Elements
</button>
<p class="text-muted">This will automatically add dateadded elements to NFO files using database values.</p>
</div>
</div>`;
} else { } else {
html += '<div class="success">✅ All NFO files have dateadded elements!</div>'; html += '<div class="success">✅ All NFO files have dateadded elements!</div>';
} }