dev #62
+67
-18
@@ -1538,46 +1538,76 @@ async def execute_database_query(dependencies: dict, query: str, limit: int = 10
|
|||||||
|
|
||||||
|
|
||||||
async def get_episodes_missing_nfo_dateadded(dependencies: dict):
|
async def get_episodes_missing_nfo_dateadded(dependencies: dict):
|
||||||
"""Find episodes that have dateadded in database but missing from NFO files"""
|
"""Find episodes and movies that have dateadded in database but missing from NFO files"""
|
||||||
db = dependencies["db"]
|
db = dependencies["db"]
|
||||||
config = dependencies["config"]
|
config = dependencies["config"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get all episodes with dateadded in database
|
# Get all episodes with video files (even if dateadded is NULL, we can check if they should have dates)
|
||||||
with db.get_connection() as conn:
|
with db.get_connection() as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
SELECT e.imdb_id, e.season, e.episode, e.dateadded, e.source, e.has_video_file,
|
SELECT e.imdb_id, e.season, e.episode, e.dateadded, e.source, e.has_video_file,
|
||||||
s.path as series_path
|
s.path as series_path, 'episode' as media_type
|
||||||
FROM episodes e
|
FROM episodes e
|
||||||
JOIN series s ON e.imdb_id = s.imdb_id
|
JOIN series s ON e.imdb_id = s.imdb_id
|
||||||
WHERE e.dateadded IS NOT NULL
|
WHERE e.has_video_file = TRUE
|
||||||
AND e.has_video_file = TRUE
|
|
||||||
ORDER BY e.imdb_id, e.season, e.episode
|
ORDER BY e.imdb_id, e.season, e.episode
|
||||||
LIMIT 500
|
LIMIT 2000
|
||||||
""")
|
""")
|
||||||
episodes = [dict(row) for row in cursor.fetchall()]
|
episodes = [dict(row) for row in cursor.fetchall()]
|
||||||
|
|
||||||
# Check each episode's NFO file
|
# Also get movies with video files
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT m.imdb_id, NULL as season, NULL as episode, m.dateadded, m.source, m.has_video_file,
|
||||||
|
m.path as series_path, 'movie' as media_type
|
||||||
|
FROM movies m
|
||||||
|
WHERE m.has_video_file = TRUE
|
||||||
|
ORDER BY m.imdb_id
|
||||||
|
LIMIT 2000
|
||||||
|
""")
|
||||||
|
movies = [dict(row) for row in cursor.fetchall()]
|
||||||
|
|
||||||
|
# Combine episodes and movies
|
||||||
|
all_items = episodes + movies
|
||||||
|
|
||||||
|
# Check each item's NFO file
|
||||||
missing_dateadded = []
|
missing_dateadded = []
|
||||||
nfo_manager = dependencies["nfo_manager"]
|
nfo_manager = dependencies["nfo_manager"]
|
||||||
|
|
||||||
for ep in episodes:
|
for item in all_items:
|
||||||
try:
|
try:
|
||||||
# Build expected NFO path
|
media_type = item['media_type']
|
||||||
series_path = Path(ep['series_path'])
|
item_path = Path(item['series_path'])
|
||||||
season_dir = series_path / config.tv_season_dir_format.format(season=ep['season'])
|
|
||||||
|
if media_type == 'episode':
|
||||||
|
# Handle TV episodes
|
||||||
|
season_dir = item_path / config.tv_season_dir_format.format(season=item['season'])
|
||||||
|
|
||||||
if not season_dir.exists():
|
if not season_dir.exists():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Find NFO file for this episode
|
# Find NFO file for this episode
|
||||||
nfo_files = list(season_dir.glob(f"*S{ep['season']:02d}E{ep['episode']:02d}*.nfo"))
|
nfo_files = list(season_dir.glob(f"*S{item['season']:02d}E{item['episode']:02d}*.nfo"))
|
||||||
if not nfo_files:
|
if not nfo_files:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
nfo_path = nfo_files[0] # Use first match
|
nfo_path = nfo_files[0] # Use first match
|
||||||
|
|
||||||
|
elif media_type == 'movie':
|
||||||
|
# Handle movies
|
||||||
|
if not item_path.exists():
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Find NFO file for this movie
|
||||||
|
nfo_files = list(item_path.glob("*.nfo"))
|
||||||
|
if not nfo_files:
|
||||||
|
continue
|
||||||
|
|
||||||
|
nfo_path = nfo_files[0] # Use first match
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
# Parse NFO and check for dateadded
|
# Parse NFO and check for dateadded
|
||||||
try:
|
try:
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
@@ -1585,17 +1615,31 @@ async def get_episodes_missing_nfo_dateadded(dependencies: dict):
|
|||||||
root = tree.getroot()
|
root = tree.getroot()
|
||||||
dateadded_elem = root.find('dateadded')
|
dateadded_elem = root.find('dateadded')
|
||||||
|
|
||||||
if dateadded_elem is None or not dateadded_elem.text:
|
# Check if dateadded is missing or empty
|
||||||
|
has_dateadded = dateadded_elem is not None and dateadded_elem.text and dateadded_elem.text.strip()
|
||||||
|
|
||||||
|
# If NFO is missing dateadded, check if item should have a date
|
||||||
|
if not has_dateadded:
|
||||||
|
# Item should have a date if it has one in the database, or if it has aired date
|
||||||
|
should_have_date = (
|
||||||
|
item['dateadded'] is not None or
|
||||||
|
(media_type == 'episode' and item.get('aired')) or
|
||||||
|
media_type == 'movie' # Movies should generally have dateadded
|
||||||
|
)
|
||||||
|
|
||||||
|
if should_have_date:
|
||||||
missing_dateadded.append({
|
missing_dateadded.append({
|
||||||
**ep,
|
**item,
|
||||||
'nfo_path': str(nfo_path),
|
'nfo_path': str(nfo_path),
|
||||||
'nfo_exists': True,
|
'nfo_exists': True,
|
||||||
'dateadded_in_nfo': False
|
'dateadded_in_nfo': False,
|
||||||
|
'should_have_date': True
|
||||||
})
|
})
|
||||||
|
|
||||||
except ET.ParseError:
|
except ET.ParseError:
|
||||||
# NFO file is corrupted
|
# NFO file is corrupted
|
||||||
missing_dateadded.append({
|
missing_dateadded.append({
|
||||||
**ep,
|
**item,
|
||||||
'nfo_path': str(nfo_path),
|
'nfo_path': str(nfo_path),
|
||||||
'nfo_exists': True,
|
'nfo_exists': True,
|
||||||
'dateadded_in_nfo': False,
|
'dateadded_in_nfo': False,
|
||||||
@@ -1603,14 +1647,19 @@ async def get_episodes_missing_nfo_dateadded(dependencies: dict):
|
|||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error checking NFO for {ep['imdb_id']} S{ep['season']:02d}E{ep['episode']:02d}: {e}")
|
if item['media_type'] == 'episode':
|
||||||
|
print(f"Error checking NFO for {item['imdb_id']} S{item['season']:02d}E{item['episode']:02d}: {e}")
|
||||||
|
else:
|
||||||
|
print(f"Error checking NFO for movie {item['imdb_id']}: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"total_episodes_checked": len(episodes),
|
"total_episodes_checked": len(episodes),
|
||||||
|
"total_movies_checked": len(movies),
|
||||||
|
"total_items_checked": len(all_items),
|
||||||
"missing_dateadded_count": len(missing_dateadded),
|
"missing_dateadded_count": len(missing_dateadded),
|
||||||
"episodes": missing_dateadded[:50] # Limit display to first 50
|
"items": missing_dateadded[:50] # Limit display to first 50
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -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.0-debug-airdate"></script>
|
<script src="/static/js/app.js?v=3.0.1-nfo-repair-movies"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1836,20 +1836,23 @@ async function checkMissingNFODates() {
|
|||||||
let html = `
|
let html = `
|
||||||
<div class="scan-summary">
|
<div class="scan-summary">
|
||||||
<h4>NFO Scan Results</h4>
|
<h4>NFO Scan Results</h4>
|
||||||
<p><strong>Total episodes checked:</strong> ${response.total_episodes_checked}</p>
|
<p><strong>Total episodes checked:</strong> ${response.total_episodes_checked || 0}</p>
|
||||||
<p><strong>Episodes missing dateadded in NFO:</strong> ${response.missing_dateadded_count}</p>
|
<p><strong>Total movies checked:</strong> ${response.total_movies_checked || 0}</p>
|
||||||
|
<p><strong>Total items checked:</strong> ${response.total_items_checked || 0}</p>
|
||||||
|
<p><strong>Items missing dateadded in NFO:</strong> ${response.missing_dateadded_count}</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (response.missing_dateadded_count > 0) {
|
if (response.missing_dateadded_count > 0) {
|
||||||
html += `
|
html += `
|
||||||
<div class="missing-episodes">
|
<div class="missing-episodes">
|
||||||
<h5>Episodes with missing dateadded (showing first 50):</h5>
|
<h5>Items with missing dateadded (showing first 50):</h5>
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<table class="results-table">
|
<table class="results-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Series</th>
|
<th>Type</th>
|
||||||
|
<th>IMDb ID</th>
|
||||||
<th>Season</th>
|
<th>Season</th>
|
||||||
<th>Episode</th>
|
<th>Episode</th>
|
||||||
<th>Database Date</th>
|
<th>Database Date</th>
|
||||||
@@ -1860,15 +1863,17 @@ async function checkMissingNFODates() {
|
|||||||
<tbody>
|
<tbody>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
response.episodes.forEach(ep => {
|
(response.items || response.episodes || []).forEach(item => {
|
||||||
|
const displayDate = item.dateadded ? new Date(item.dateadded).toLocaleString() : 'None';
|
||||||
html += `
|
html += `
|
||||||
<tr>
|
<tr>
|
||||||
<td>${ep.imdb_id}</td>
|
<td>${item.media_type || 'episode'}</td>
|
||||||
<td>${ep.season}</td>
|
<td>${item.imdb_id}</td>
|
||||||
<td>${ep.episode}</td>
|
<td>${item.season || 'N/A'}</td>
|
||||||
<td>${new Date(ep.dateadded).toLocaleString()}</td>
|
<td>${item.episode || 'N/A'}</td>
|
||||||
<td>${ep.source}</td>
|
<td>${displayDate}</td>
|
||||||
<td><small>${ep.nfo_path}</small></td>
|
<td>${item.source || 'unknown'}</td>
|
||||||
|
<td><small>${item.nfo_path}</small></td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user