"""Download worker process entry point. Runs in a DEDICATED container (`command: python -m app.worker`, `WORKER_ENABLED=1`), separate from the API so long yt-dlp downloads + ffmpeg postprocessing never block web requests. It claims `queued` download jobs from the DB with FOR UPDATE SKIP LOCKED and runs them, writing live progress back to the job row (the frontend polls it — the worker is a separate process and can't reach the API's in-process WebSocket registry). M1: thin, idle entry point (keeps the container healthy). The claim loop + yt-dlp integration land in M2. """ import logging import signal import time from app.config import settings log = logging.getLogger("siftlode.worker") _stop = False def _handle_signal(signum, frame): global _stop _stop = True def main() -> None: logging.basicConfig(level=logging.INFO) signal.signal(signal.SIGTERM, _handle_signal) signal.signal(signal.SIGINT, _handle_signal) if not settings.worker_enabled: log.info("WORKER_ENABLED is off; worker exiting (nothing to do).") return log.info( "Download worker started (concurrency=%d, root=%s). Claim loop lands in M2.", settings.download_worker_concurrency, settings.download_root, ) while not _stop: # M2: claim queued jobs (FOR UPDATE SKIP LOCKED) and download them. time.sleep(settings.download_poll_seconds) log.info("Download worker stopped.") if __name__ == "__main__": main()