refactor: centralize initialization logic in dedicated service

- Create initialization_service.py with shared initialization functions
- Extract setup logic from lifespan and setup endpoint into reusable functions
- Setup endpoint now calls perform_initial_setup() directly
- Lifespan startup calls the same shared functions
- Eliminates code duplication between setup and lifespan
- Ensures consistent initialization behavior regardless of entry point
This commit is contained in:
2026-01-23 14:37:07 +01:00
parent 8e8487b7b7
commit 50e0b21669
3 changed files with 265 additions and 206 deletions

View File

@@ -186,35 +186,15 @@ async def lifespan(_application: FastAPI):
# Subscribe to progress events
progress_service.subscribe("progress_updated", progress_event_handler)
# Check if initial setup has been completed
try:
from src.server.database.connection import get_db_session
from src.server.database.system_settings_service import (
SystemSettingsService,
)
async with get_db_session() as db:
is_initial_scan_done = (
await SystemSettingsService.is_initial_scan_completed(db)
)
if is_initial_scan_done:
logger.info(
"Initial scan already completed, skipping data file sync"
)
else:
logger.info(
"Initial scan not completed, "
"performing first-time setup"
)
except Exception as e:
logger.warning(
"Failed to check system settings: %s, assuming first run", e
)
is_initial_scan_done = False
# Sync series from data files to database (only on first run)
# This must happen before SeriesApp initialization
# Perform initial setup (series sync and marking as completed)
# This is centralized in initialization_service and also called
# from the setup endpoint
from src.server.services.initialization_service import (
perform_initial_setup,
perform_media_scan_if_needed,
perform_nfo_scan_if_needed,
)
try:
logger.info(
"Checking anime_directory setting: '%s'",
@@ -222,103 +202,15 @@ async def lifespan(_application: FastAPI):
)
if settings.anime_directory:
# Only sync from data files on first run
if not is_initial_scan_done:
logger.info("Performing initial anime folder scan...")
sync_count = await sync_series_from_data_files(
settings.anime_directory
)
logger.info(
"Data file sync complete. Added %d series.", sync_count
)
# Mark initial scan as completed
try:
async with get_db_session() as db:
await (
SystemSettingsService
.mark_initial_scan_completed(db)
)
logger.info("Marked initial scan as completed")
except Exception as e:
logger.warning(
"Failed to mark initial scan as completed: %s", e
)
else:
logger.info(
"Skipping initial scan - "
"already completed on previous run"
)
# Perform initial setup if needed
await perform_initial_setup()
# Load series from database into SeriesApp's in-memory cache
# Get anime service for later use
from src.server.utils.dependencies import get_anime_service
anime_service = get_anime_service()
await anime_service._load_series_from_db()
logger.info("Series loaded from database into memory")
# Check if initial NFO scan has been completed
try:
async with get_db_session() as db:
is_nfo_scan_done = (
await SystemSettingsService
.is_initial_nfo_scan_completed(db)
)
except Exception as e:
logger.warning(
"Failed to check NFO scan status: %s, assuming not done",
e
)
is_nfo_scan_done = False
# Run NFO scan only on first run (if configured)
if settings.tmdb_api_key and (
settings.nfo_auto_create or settings.nfo_update_on_scan
):
if not is_nfo_scan_done:
logger.info("Performing initial NFO scan...")
try:
from src.core.services.series_manager_service import (
SeriesManagerService,
)
manager = SeriesManagerService.from_settings()
await manager.scan_and_process_nfo()
await manager.close()
logger.info("Initial NFO scan completed")
# Mark NFO scan as completed
try:
async with get_db_session() as db:
await (
SystemSettingsService
.mark_initial_nfo_scan_completed(db)
)
logger.info("Marked NFO scan as completed")
except Exception as e:
logger.warning(
"Failed to mark NFO scan as completed: %s",
e
)
except Exception as e:
logger.error(
"Failed to complete NFO scan: %s",
e,
exc_info=True
)
else:
logger.info(
"Skipping NFO scan - already completed on previous run"
)
else:
if not settings.tmdb_api_key:
logger.info(
"NFO scan skipped - TMDB API key not configured"
)
else:
logger.info(
"NFO scan skipped - auto_create and update_on_scan "
"both disabled"
)
await perform_nfo_scan_if_needed()
# Now initialize download service (will use data from database)
from src.server.utils.dependencies import get_download_service
@@ -341,52 +233,8 @@ async def lifespan(_application: FastAPI):
await background_loader.start()
logger.info("Background loader service started")
# Check if initial media scan has been completed
is_media_scan_done = False
try:
async with get_db_session() as db:
is_media_scan_done = (
await SystemSettingsService
.is_initial_media_scan_completed(db)
)
except Exception as e:
logger.warning(
"Failed to check media scan status: %s, assuming not done",
e
)
is_media_scan_done = False
# Run media scan only on first run
if not is_media_scan_done:
logger.info("Performing initial media scan...")
try:
# Check for incomplete series and queue background loading
await _check_incomplete_series_on_startup(background_loader)
logger.info("Initial media scan completed")
# Mark media scan as completed
try:
async with get_db_session() as db:
await (
SystemSettingsService
.mark_initial_media_scan_completed(db)
)
logger.info("Marked media scan as completed")
except Exception as e:
logger.warning(
"Failed to mark media scan as completed: %s",
e
)
except Exception as e:
logger.error(
"Failed to complete media scan: %s",
e,
exc_info=True
)
else:
logger.info(
"Skipping media scan - already completed on previous run"
)
await perform_media_scan_if_needed(background_loader)
else:
logger.info(
"Download service initialization skipped - "