""" Scanner service for initial library scan and one-time bootstrap of the default source root on first boot. """ import os import logging from sqlalchemy import select from app.database import AsyncSessionLocal from app.models import SourceRoot from app.tasks.scan import scan_all_source_roots, watch_folders from app.config import settings logger = logging.getLogger(__name__) # The single host → container mount path. The compose file mounts whatever # the user set as PHOTO_DIRS at this path. DEFAULT_LIBRARY_PATH = "/photos" DEFAULT_LIBRARY_NAME = "Library" async def bootstrap_default_source_root() -> None: """If no source roots exist in the DB, create one pointing at the default library mount. Lets a fresh install pick up photos with zero configuration: the user only needs to set PHOTO_DIRS in .env. """ if not os.path.isdir(DEFAULT_LIBRARY_PATH): logger.warning( f"Default library path {DEFAULT_LIBRARY_PATH} is not mounted; " "set PHOTO_DIRS in .env and recreate the container." ) return async with AsyncSessionLocal() as session: result = await session.execute(select(SourceRoot)) if result.scalars().first() is not None: return # Already have at least one source root, leave it alone. source_root = SourceRoot( name=DEFAULT_LIBRARY_NAME, path=DEFAULT_LIBRARY_PATH, ) session.add(source_root) await session.commit() logger.info( f"Bootstrapped default source root: {DEFAULT_LIBRARY_NAME} → " f"{DEFAULT_LIBRARY_PATH}" ) async def start_initial_scan(): """Start the initial library scan""" try: # Queue scan of all source roots scan_all_source_roots.delay() # Start folder watcher if configured if settings.scanner.watch: watch_folders.delay() logger.info("Initial scan queued successfully") except Exception as e: logger.error(f"Failed to start initial scan: {e}")