feat(setup): separate NFO scan into dedicated phase

- Add /nfo-scan-phase endpoint to trigger NFO scan independently
- Move NFO scan out of initial setup into separate post-unresolved phase
- Add phase query param handling for /loading page (?phase=initial, ?phase=nfo)
- Update setup redirect middleware to handle phase-based redirects
- Update auth setup to pass phase=initial to loading page
This commit is contained in:
2026-06-07 17:37:32 +02:00
parent cf00c9f7c5
commit 07c311c1cd
7 changed files with 350 additions and 71 deletions

View File

@@ -386,8 +386,8 @@ async def perform_initial_setup(progress_service=None):
# Load series into memory from database
await _load_series_into_memory(progress_service)
# Run NFO scan as part of initialization
await perform_nfo_scan_if_needed(progress_service)
# NOTE: NFO scan is NO longer run here - it runs in a separate phase
# after unresolved folders are completed (via /loading?phase=nfo)
return True
@@ -534,6 +534,82 @@ async def perform_nfo_scan_if_needed(progress_service=None):
)
async def perform_nfo_scan_phase(progress_service=None):
"""Perform the NFO scan phase as part of the second loading page phase.
This is called when the loading page is accessed with ?phase=nfo query param.
It runs the NFO scan and emits progress updates via the progress service.
Args:
progress_service: Optional ProgressService for emitting updates
"""
logger.info("Starting NFO scan phase...")
if progress_service:
from src.server.services.progress_service import ProgressType
await progress_service.start_progress(
progress_id="nfo_scan",
progress_type=ProgressType.SCAN,
title="Scanning NFO Files",
total=100,
message="Starting NFO scan...",
metadata={"step_id": "nfo_scan", "phase": "nfo"}
)
# Check if NFO scan was already completed
is_nfo_scan_done = await _check_nfo_scan_status()
# Check if NFO features are configured
if not await _is_nfo_scan_configured():
message = (
"Skipped - TMDB API key not configured"
if not settings.tmdb_api_key
else "Skipped - NFO features disabled"
)
logger.info("NFO scan phase skipped: %s", message)
if progress_service:
await progress_service.complete_progress(
progress_id="nfo_scan",
message=message,
metadata={"step_id": "nfo_scan", "phase": "nfo", "nfo_scan_complete": True}
)
return
# Skip if already completed
if is_nfo_scan_done:
logger.info("Skipping NFO scan phase - already completed on previous run")
if progress_service:
await progress_service.complete_progress(
progress_id="nfo_scan",
message="Already completed",
metadata={"step_id": "nfo_scan", "phase": "nfo", "nfo_scan_complete": True}
)
return
# Execute the NFO scan
try:
await _execute_nfo_scan(progress_service)
await _mark_nfo_scan_completed()
# Send completion event
if progress_service:
await progress_service.complete_progress(
progress_id="nfo_scan",
message="NFO scan completed successfully",
metadata={"step_id": "nfo_scan", "phase": "nfo", "nfo_scan_complete": True}
)
logger.info("NFO scan phase completed successfully")
except Exception as e:
logger.error("Failed to complete NFO scan phase: %s", e, exc_info=True)
if progress_service:
await progress_service.fail_progress(
progress_id="nfo_scan",
error_message=f"NFO scan failed: {str(e)}",
metadata={"step_id": "nfo_scan", "phase": "nfo"}
)
async def _check_media_scan_status() -> bool:
"""Check if initial media scan has been completed.