fix: not shutting down during scan #55

Merged
sbcrumb merged 2 commits from dev into main 2025-10-20 11:40:56 -04:00
3 changed files with 94 additions and 2 deletions
+31 -1
View File
@@ -682,7 +682,8 @@ async def manual_scan(background_tasks: BackgroundTasks, path: Optional[str] = N
try:
# Determine force_scan based on scan mode
force_scan = (scan_mode == "full")
result = movie_processor.process_movie(item, webhook_mode=False, force_scan=force_scan)
shutdown_event = dependencies.get("shutdown_event")
result = movie_processor.process_movie(item, webhook_mode=False, force_scan=force_scan, shutdown_event=shutdown_event)
movie_total += 1
if result == "skipped":
movie_skipped += 1
@@ -691,6 +692,9 @@ async def manual_scan(background_tasks: BackgroundTasks, path: Optional[str] = N
elif result == "no_video_files":
print(f"INFO: Skipped empty directory: {item.name}")
movie_skipped += 1
elif result == "shutdown":
print("INFO: ⚠️ SHUTDOWN SIGNAL RECEIVED - Stopping movie scan gracefully")
return
except Exception as e:
print(f"ERROR: Failed processing movie {item}: {e}")
movie_total += 1
@@ -1207,6 +1211,28 @@ async def cleanup_orphaned_movies(dependencies: dict):
}
async def cleanup_orphaned_series(dependencies: dict):
"""Find and delete TV series that don't have corresponding directories"""
db = dependencies["db"]
try:
deleted_series = db.delete_orphaned_series()
return {
"success": True,
"message": f"Cleaned up {len(deleted_series)} orphaned TV series",
"deleted_count": len(deleted_series),
"deleted_series": deleted_series
}
except Exception as e:
return {
"success": False,
"error": str(e),
"message": "Failed to cleanup orphaned TV series"
}
# ---------------------------
# Route Registration
# ---------------------------
@@ -1277,6 +1303,10 @@ def register_routes(app, dependencies: dict):
async def _cleanup_orphaned_movies():
return await cleanup_orphaned_movies(dependencies)
@app.post("/database/cleanup/orphaned-series")
async def _cleanup_orphaned_series():
return await cleanup_orphaned_series(dependencies)
@app.post("/manual/scan")
async def _manual_scan(background_tasks: BackgroundTasks, path: Optional[str] = None, scan_type: str = "both", scan_mode: str = "smart"):
return await manual_scan(background_tasks, path, scan_type, scan_mode, dependencies)
+52
View File
@@ -501,6 +501,58 @@ class NFOGuardDatabase:
return deleted_movies
def delete_orphaned_series(self) -> List[Dict]:
"""
Find and delete TV series that don't have corresponding directories on disk
This requires checking filesystem for each series, so use carefully
Returns:
List of deleted series with their details
"""
from pathlib import Path
deleted_series = []
with self.get_connection() as conn:
cursor = conn.cursor()
# Get all series with their paths
cursor.execute("""
SELECT imdb_id, path, last_updated, metadata
FROM series
""")
series_list = cursor.fetchall()
for series in series_list:
imdb_id = series['imdb_id']
series_path = Path(series['path'])
if not series_path.exists():
# Series directory doesn't exist - delete the series and all its episodes
cursor.execute("""
DELETE FROM episodes
WHERE imdb_id = %s
""", (imdb_id,))
episodes_deleted = cursor.rowcount
cursor.execute("""
DELETE FROM series
WHERE imdb_id = %s
""", (imdb_id,))
deleted_series.append({
'imdb_id': imdb_id,
'reason': 'directory_not_found',
'path': str(series_path),
'last_updated': series['last_updated'],
'episodes_deleted': episodes_deleted
})
conn.commit()
return deleted_series
def close(self):
"""Close all database connections"""
if hasattr(self._local, 'connection'):
+11 -1
View File
@@ -151,7 +151,7 @@ class MovieProcessor:
_log("ERROR", f"Error checking movie completion for {imdb_id}: {e}")
return False, f"Error checking completion: {e}"
def process_movie(self, movie_path: Path, webhook_mode: bool = False, force_scan: bool = False) -> str:
def process_movie(self, movie_path: Path, webhook_mode: bool = False, force_scan: bool = False, shutdown_event=None) -> str:
"""Process a movie directory"""
imdb_id = self.nfo_manager.find_movie_imdb_id(movie_path)
if not imdb_id:
@@ -180,6 +180,11 @@ class MovieProcessor:
else:
_log("INFO", f"📥 WEBHOOK PROCESSING MOVIE: {movie_path.name} [{imdb_id}] - Webhook mode")
# Check for shutdown signal early in processing
if shutdown_event and shutdown_event.is_set():
_log("INFO", f"⚠️ SHUTDOWN SIGNAL RECEIVED - Stopping movie processing: {movie_path.name}")
return "shutdown"
# Update database
self.db.upsert_movie(imdb_id, str(movie_path))
@@ -309,6 +314,11 @@ class MovieProcessor:
# TIER 3: No cached data found - proceed with API lookups and verification
# Check for shutdown signal before expensive API operations
if shutdown_event and shutdown_event.is_set():
_log("INFO", f"⚠️ SHUTDOWN SIGNAL RECEIVED - Stopping movie processing before API calls: {movie_path.name}")
return "shutdown"
# TIER 3: No cached data found - determine if we should query APIs
if webhook_mode:
_log("INFO", f"Webhook processing - no cached data found, using full date decision logic")