"""scan_directory task: walks the configured import path, creates an ImportBatch, enumerates files into ImportTasks, and enqueues import_media_file for each. """ from pathlib import Path from sqlalchemy import create_engine, select from sqlalchemy.orm import sessionmaker from ..celery_app import celery from ..config import get_config from ..models import ImportBatch, ImportSettings, ImportTask def _iter_import_files(import_root: Path): """Every regular file except sidecar .json, dotfiles, and .partial temp files. The Importer dispatches by kind (media / archive / other) — scan no longer filters to media (FC-2d-iii).""" for entry in import_root.rglob("*"): if not entry.is_file(): continue if entry.name.startswith("."): continue if entry.suffix.lower() in (".json", ".partial"): continue yield entry def _sync_session_factory(): cfg = get_config() engine = create_engine(cfg.database_url_sync, future=True, pool_pre_ping=True) return sessionmaker(engine, expire_on_commit=False) @celery.task(name="backend.app.tasks.scan.scan_directory", bind=True) def scan_directory(self, triggered_by: str = "manual", mode: str = "quick") -> int: """Walks the import root and creates ImportTasks. `mode` is 'quick' or 'deep'; deep also enqueues a library-wide phash backfill and (via Importer.deep) re-derives on already-imported files. Returns the batch id.""" SessionLocal = _sync_session_factory() with SessionLocal() as session: settings = session.execute( select(ImportSettings).where(ImportSettings.id == 1) ).scalar_one() import_root = Path(settings.import_scan_path) batch = ImportBatch( triggered_by=triggered_by, source_path=str(import_root), scan_mode=mode, status="running", ) session.add(batch) session.flush() batch_id = batch.id # Walk and enumerate. files_seen = 0 for entry in _iter_import_files(import_root): try: size = entry.stat().st_size except OSError: size = None task = ImportTask( batch_id=batch_id, source_path=str(entry), task_type="media", status="pending", size_bytes=size, ) session.add(task) files_seen += 1 batch.total_files = files_seen session.commit() # Now enqueue import_media_file for each pending task. for task in session.execute( select(ImportTask).where(ImportTask.batch_id == batch_id) ).scalars(): task.status = "queued" session.add(task) from .import_file import import_media_file import_media_file.delay(task.id) session.commit() if mode == "deep": from .maintenance import backfill_phash backfill_phash.delay() return batch_id