Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6fae41aef5 | |||
| 249b55964e | |||
| 7b7ac6f179 | |||
| 8da23013ff |
@@ -1068,6 +1068,14 @@ def register_routes(app, dependencies: dict):
|
|||||||
"""Get available date options for an episode"""
|
"""Get available date options for an episode"""
|
||||||
return await get_episode_date_options(dependencies, imdb_id, season, episode)
|
return await get_episode_date_options(dependencies, imdb_id, season, episode)
|
||||||
|
|
||||||
|
@app.get("/api/debug/movie/{imdb_id}/raw")
|
||||||
|
async def _debug_movie_raw(imdb_id: str):
|
||||||
|
"""Debug endpoint to see raw movie database data"""
|
||||||
|
db = dependencies["db"]
|
||||||
|
movie = db.get_movie_dates(imdb_id)
|
||||||
|
if not movie:
|
||||||
|
raise HTTPException(status_code=404, detail="Movie not found")
|
||||||
|
return {"raw_data": dict(movie), "imdb_id": imdb_id}
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# Static Web Interface
|
# Static Web Interface
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
|
|||||||
+38
-12
@@ -326,6 +326,11 @@ async def update_movie_date(dependencies: dict, imdb_id: str, dateadded: Optiona
|
|||||||
"""Update dateadded for a specific movie"""
|
"""Update dateadded for a specific movie"""
|
||||||
db = dependencies["db"]
|
db = dependencies["db"]
|
||||||
|
|
||||||
|
# Debug logging to track the issue
|
||||||
|
print(f"🔍 UPDATE_MOVIE_DATE: imdb_id={imdb_id}, dateadded={dateadded}, source={source}")
|
||||||
|
print(f" - dateadded type: {type(dateadded)}")
|
||||||
|
print(f" - dateadded repr: {repr(dateadded)}")
|
||||||
|
|
||||||
# Validate movie exists
|
# Validate movie exists
|
||||||
movie = db.get_movie_dates(imdb_id)
|
movie = db.get_movie_dates(imdb_id)
|
||||||
if not movie:
|
if not movie:
|
||||||
@@ -449,6 +454,15 @@ async def get_movie_date_options(dependencies: dict, imdb_id: str):
|
|||||||
if not movie:
|
if not movie:
|
||||||
raise HTTPException(status_code=404, detail="Movie not found")
|
raise HTTPException(status_code=404, detail="Movie not found")
|
||||||
|
|
||||||
|
# Debug logging to understand the data issue
|
||||||
|
print(f"🔍 DEBUG: Movie data for {imdb_id}:")
|
||||||
|
print(f" - released: {repr(movie.get('released'))}")
|
||||||
|
print(f" - dateadded: {repr(movie.get('dateadded'))}")
|
||||||
|
print(f" - source: {repr(movie.get('source'))}")
|
||||||
|
print(f" - path: {repr(movie.get('path'))}")
|
||||||
|
print(f" - has_video_file: {repr(movie.get('has_video_file'))}")
|
||||||
|
print(f" - last_updated: {repr(movie.get('last_updated'))}")
|
||||||
|
|
||||||
options = []
|
options = []
|
||||||
|
|
||||||
# Option 1: Current dateadded (if exists and is different from released)
|
# Option 1: Current dateadded (if exists and is different from released)
|
||||||
@@ -479,18 +493,26 @@ async def get_movie_date_options(dependencies: dict, imdb_id: str):
|
|||||||
})
|
})
|
||||||
|
|
||||||
# Option 2: Released date as digital release (if different from current)
|
# Option 2: Released date as digital release (if different from current)
|
||||||
if movie.get('released'):
|
if movie.get('released') and movie['released'].strip():
|
||||||
release_date = f"{movie['released']}T00:00:00"
|
try:
|
||||||
# Only add if it's different from current dateadded
|
release_date = f"{movie['released']}T00:00:00"
|
||||||
current_dateadded = movie.get('dateadded', '')
|
# Validate the date format
|
||||||
if not current_dateadded or not current_dateadded.startswith(movie['released']):
|
from datetime import datetime
|
||||||
options.append({
|
datetime.fromisoformat(release_date.replace('Z', '+00:00'))
|
||||||
"type": "digital_release",
|
|
||||||
"label": "Use Actual Release Date",
|
# Only add if it's different from current dateadded
|
||||||
"date": release_date,
|
current_dateadded = movie.get('dateadded', '')
|
||||||
"source": "digital_release",
|
if not current_dateadded or not current_dateadded.startswith(movie['released']):
|
||||||
"description": f"Use the movie's actual release date: {movie['released']} (instead of download date)"
|
options.append({
|
||||||
})
|
"type": "digital_release",
|
||||||
|
"label": "Use Actual Release Date",
|
||||||
|
"date": release_date,
|
||||||
|
"source": "digital_release",
|
||||||
|
"description": f"Use the movie's actual release date: {movie['released']} (instead of download date)"
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Invalid released date format for {imdb_id}: {movie.get('released')} - {e}")
|
||||||
|
# Don't add this option if the date is invalid
|
||||||
|
|
||||||
# Option 3: Manual entry
|
# Option 3: Manual entry
|
||||||
options.append({
|
options.append({
|
||||||
@@ -509,6 +531,10 @@ async def get_movie_date_options(dependencies: dict, imdb_id: str):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
print(f"🔍 DEBUG: Generated {len(options)} options for {imdb_id}:")
|
||||||
|
for i, option in enumerate(options):
|
||||||
|
print(f" Option {i}: {option}")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"imdb_id": imdb_id,
|
"imdb_id": imdb_id,
|
||||||
"current_data": movie,
|
"current_data": movie,
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Debug script to check specific movie data in NFOGuard database
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Add the project root to the path
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
|
from core.database import NFOGuardDatabase
|
||||||
|
|
||||||
|
def debug_movie(imdb_id: str):
|
||||||
|
"""Debug a specific movie's data"""
|
||||||
|
print(f"🔍 DEBUG MOVIE: {imdb_id}")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# Initialize database
|
||||||
|
db = NFOGuardDatabase()
|
||||||
|
|
||||||
|
# Get movie data
|
||||||
|
movie = db.get_movie_dates(imdb_id)
|
||||||
|
if not movie:
|
||||||
|
print(f"❌ Movie {imdb_id} not found in database")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("📊 RAW MOVIE DATA:")
|
||||||
|
for key, value in movie.items():
|
||||||
|
print(f" {key}: {repr(value)}")
|
||||||
|
|
||||||
|
print("\n🎬 FORMATTED MOVIE DATA:")
|
||||||
|
print(f" Title/Path: {movie.get('path', 'Unknown')}")
|
||||||
|
print(f" Released: {movie.get('released', 'None')}")
|
||||||
|
print(f" Date Added: {movie.get('dateadded', 'None')}")
|
||||||
|
print(f" Source: {movie.get('source', 'None')}")
|
||||||
|
print(f" Has Video: {movie.get('has_video_file', False)}")
|
||||||
|
print(f" Last Updated: {movie.get('last_updated', 'None')}")
|
||||||
|
|
||||||
|
# Check if released date is valid
|
||||||
|
released = movie.get('released')
|
||||||
|
if released and released.strip():
|
||||||
|
try:
|
||||||
|
from datetime import datetime
|
||||||
|
test_date = f"{released}T00:00:00"
|
||||||
|
parsed = datetime.fromisoformat(test_date.replace('Z', '+00:00'))
|
||||||
|
print(f"\n✅ Released date is valid: {parsed}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ Released date is INVALID: {e}")
|
||||||
|
else:
|
||||||
|
print(f"\n⚠️ Released date is empty or None")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print("Usage: python debug_movie.py <imdb_id>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
imdb_id = sys.argv[1]
|
||||||
|
if not imdb_id.startswith('tt'):
|
||||||
|
imdb_id = f'tt{imdb_id}'
|
||||||
|
|
||||||
|
debug_movie(imdb_id)
|
||||||
+2
-2
@@ -133,8 +133,8 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Title</th>
|
<th>Title</th>
|
||||||
<th>IMDb ID</th>
|
<th>IMDb ID</th>
|
||||||
<th>Released</th>
|
<th>Movie Released</th>
|
||||||
<th>Date Added</th>
|
<th>Date Added to Library</th>
|
||||||
<th>Source</th>
|
<th>Source</th>
|
||||||
<th>Date Type</th>
|
<th>Date Type</th>
|
||||||
<th>Video File</th>
|
<th>Video File</th>
|
||||||
|
|||||||
+112
-7
@@ -245,6 +245,9 @@ function updateMoviesTable(data) {
|
|||||||
<button class="btn btn-sm btn-primary" onclick="editMovie('${movie.imdb_id}', '${dateadded}', '${movie.source || ''}')">
|
<button class="btn btn-sm btn-primary" onclick="editMovie('${movie.imdb_id}', '${dateadded}', '${movie.source || ''}')">
|
||||||
<i class="fas fa-edit"></i> Edit
|
<i class="fas fa-edit"></i> Edit
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-sm btn-secondary" onclick="debugMovie('${movie.imdb_id}')" title="Debug Data">
|
||||||
|
<i class="fas fa-bug"></i>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
@@ -591,7 +594,13 @@ function closeSmartFixModal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function applySmartFix(mediaType, options) {
|
async function applySmartFix(mediaType, options) {
|
||||||
const selectedIndex = document.querySelector('input[name="date-option"]:checked').value;
|
const selectedRadio = document.querySelector('input[name="date-option"]:checked');
|
||||||
|
if (!selectedRadio) {
|
||||||
|
showToast('Please select a date option', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedIndex = selectedRadio.value;
|
||||||
const selectedOption = options.options[selectedIndex];
|
const selectedOption = options.options[selectedIndex];
|
||||||
|
|
||||||
let dateadded = selectedOption.date;
|
let dateadded = selectedOption.date;
|
||||||
@@ -601,13 +610,49 @@ async function applySmartFix(mediaType, options) {
|
|||||||
if (selectedOption.type === 'manual') {
|
if (selectedOption.type === 'manual') {
|
||||||
const manualDateInput = document.getElementById(`manual-date-${selectedIndex}`);
|
const manualDateInput = document.getElementById(`manual-date-${selectedIndex}`);
|
||||||
if (manualDateInput && manualDateInput.value) {
|
if (manualDateInput && manualDateInput.value) {
|
||||||
dateadded = new Date(manualDateInput.value).toISOString();
|
try {
|
||||||
|
dateadded = new Date(manualDateInput.value).toISOString();
|
||||||
|
} catch (e) {
|
||||||
|
showToast('Invalid date format', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showToast('Please enter a date for manual option', 'warning');
|
showToast('Please enter a date for manual option', 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (dateadded) {
|
||||||
|
// Fix date format for non-manual options
|
||||||
|
try {
|
||||||
|
let dateValue = dateadded;
|
||||||
|
|
||||||
|
// Handle timezone offsets
|
||||||
|
if (dateValue.includes('+00:00')) {
|
||||||
|
dateValue = dateValue.replace('+00:00', 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = new Date(dateValue);
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
showToast('Invalid date from server', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dateadded = date.toISOString();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Date conversion error:', e, dateadded);
|
||||||
|
showToast('Date conversion error', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug logging
|
||||||
|
console.log('🔍 SMART FIX DEBUG:', {
|
||||||
|
mediaType,
|
||||||
|
imdb_id: options.imdb_id,
|
||||||
|
selectedOption,
|
||||||
|
dateadded,
|
||||||
|
source,
|
||||||
|
originalDate: selectedOption.date
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (mediaType === 'movie') {
|
if (mediaType === 'movie') {
|
||||||
await updateMovieDate(options.imdb_id, dateadded, source);
|
await updateMovieDate(options.imdb_id, dateadded, source);
|
||||||
@@ -617,6 +662,7 @@ async function applySmartFix(mediaType, options) {
|
|||||||
closeSmartFixModal();
|
closeSmartFixModal();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Smart fix failed:', error);
|
console.error('Smart fix failed:', error);
|
||||||
|
showToast('Smart fix failed: ' + error.message, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,11 +878,26 @@ function updateEditDateFromOption(optionIndex, option) {
|
|||||||
const sourceSelect = document.getElementById('edit-source');
|
const sourceSelect = document.getElementById('edit-source');
|
||||||
|
|
||||||
if (option.date) {
|
if (option.date) {
|
||||||
// Convert to datetime-local format
|
// Convert to datetime-local format with better date parsing
|
||||||
try {
|
try {
|
||||||
const date = new Date(option.date);
|
let dateValue = option.date;
|
||||||
dateInput.value = date.toISOString().slice(0, 16);
|
|
||||||
|
// Handle timezone offsets by converting to local time
|
||||||
|
if (dateValue.includes('+00:00') || dateValue.includes('Z')) {
|
||||||
|
dateValue = dateValue.replace('+00:00', 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = new Date(dateValue);
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
console.error('Invalid date:', dateValue);
|
||||||
|
dateInput.value = '';
|
||||||
|
} else {
|
||||||
|
// Convert to local datetime-local format
|
||||||
|
const localDateTime = new Date(date.getTime() - (date.getTimezoneOffset() * 60000));
|
||||||
|
dateInput.value = localDateTime.toISOString().slice(0, 16);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error('Date parsing error:', e, option.date);
|
||||||
dateInput.value = '';
|
dateInput.value = '';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -857,8 +918,19 @@ async function handleEnhancedEditSubmit(event) {
|
|||||||
const dateadded = document.getElementById('edit-dateadded').value;
|
const dateadded = document.getElementById('edit-dateadded').value;
|
||||||
const source = document.getElementById('edit-source').value;
|
const source = document.getElementById('edit-source').value;
|
||||||
|
|
||||||
// Convert datetime-local to ISO string
|
if (!dateadded) {
|
||||||
const isoDateadded = dateadded ? new Date(dateadded).toISOString() : null;
|
showToast('Please enter a date', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert datetime-local to ISO string with error handling
|
||||||
|
let isoDateadded = null;
|
||||||
|
try {
|
||||||
|
isoDateadded = new Date(dateadded).toISOString();
|
||||||
|
} catch (e) {
|
||||||
|
showToast('Invalid date format', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (mediaType === 'movie') {
|
if (mediaType === 'movie') {
|
||||||
@@ -870,6 +942,7 @@ async function handleEnhancedEditSubmit(event) {
|
|||||||
closeModal();
|
closeModal();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Enhanced edit failed:', error);
|
console.error('Enhanced edit failed:', error);
|
||||||
|
showToast('Update failed: ' + error.message, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1024,4 +1097,36 @@ function showToast(message, type = 'info') {
|
|||||||
toast.parentNode.removeChild(toast);
|
toast.parentNode.removeChild(toast);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug function
|
||||||
|
async function debugMovie(imdbId) {
|
||||||
|
try {
|
||||||
|
const data = await apiCall(`/api/debug/movie/${imdbId}/raw`);
|
||||||
|
|
||||||
|
const debugInfo = `
|
||||||
|
DEBUG INFO for ${imdbId}:
|
||||||
|
|
||||||
|
Raw Database Data:
|
||||||
|
- imdb_id: ${data.raw_data.imdb_id}
|
||||||
|
- path: ${data.raw_data.path}
|
||||||
|
- released: ${data.raw_data.released}
|
||||||
|
- dateadded: ${data.raw_data.dateadded}
|
||||||
|
- source: ${data.raw_data.source}
|
||||||
|
- has_video_file: ${data.raw_data.has_video_file}
|
||||||
|
- last_updated: ${data.raw_data.last_updated}
|
||||||
|
|
||||||
|
Analysis:
|
||||||
|
- Movie Released: ${data.raw_data.released || 'Not set'}
|
||||||
|
- Library Import Date: ${data.raw_data.dateadded || 'Not set'}
|
||||||
|
- Date Source: ${data.raw_data.source || 'Unknown'}
|
||||||
|
`;
|
||||||
|
|
||||||
|
alert(debugInfo);
|
||||||
|
console.log('🔍 Debug data for', imdbId, data);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Debug failed:', error);
|
||||||
|
showToast('Debug failed: ' + error.message, 'error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user