"""Background job scheduler for album polling.""" from __future__ import annotations import logging from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.interval import IntervalTrigger from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession from ..database.engine import get_engine from ..database.models import AlbumTracker from .watcher import check_tracker _LOGGER = logging.getLogger(__name__) _scheduler: AsyncIOScheduler | None = None def get_scheduler() -> AsyncIOScheduler: """Get the global scheduler instance.""" global _scheduler if _scheduler is None: _scheduler = AsyncIOScheduler() return _scheduler async def start_scheduler() -> None: """Start the scheduler and load all enabled trackers.""" scheduler = get_scheduler() engine = get_engine() async with AsyncSession(engine) as session: result = await session.exec( select(AlbumTracker).where(AlbumTracker.enabled == True) # noqa: E712 ) trackers = result.all() for tracker in trackers: _add_tracker_job(scheduler, tracker.id, tracker.scan_interval) scheduler.start() _LOGGER.info("Scheduler started with %d tracker jobs", len(trackers)) async def stop_scheduler() -> None: """Stop the scheduler.""" scheduler = get_scheduler() if scheduler.running: scheduler.shutdown(wait=False) _LOGGER.info("Scheduler stopped") def add_tracker_job(tracker_id: int, scan_interval: int) -> None: """Add or update a scheduler job for a tracker.""" scheduler = get_scheduler() _add_tracker_job(scheduler, tracker_id, scan_interval) def remove_tracker_job(tracker_id: int) -> None: """Remove a scheduler job for a tracker.""" scheduler = get_scheduler() job_id = f"tracker_{tracker_id}" if scheduler.get_job(job_id): scheduler.remove_job(job_id) _LOGGER.debug("Removed scheduler job for tracker %d", tracker_id) def _add_tracker_job( scheduler: AsyncIOScheduler, tracker_id: int, scan_interval: int ) -> None: """Add or replace a scheduler job.""" job_id = f"tracker_{tracker_id}" # Remove existing job if present if scheduler.get_job(job_id): scheduler.remove_job(job_id) scheduler.add_job( _run_tracker_check, trigger=IntervalTrigger(seconds=scan_interval), id=job_id, args=[tracker_id], replace_existing=True, max_instances=1, ) _LOGGER.debug( "Scheduled tracker %d every %d seconds", tracker_id, scan_interval ) async def _run_tracker_check(tracker_id: int) -> None: """Run a single tracker check (called by scheduler).""" try: result = await check_tracker(tracker_id) _LOGGER.debug("Tracker %d check result: %s", tracker_id, result) except Exception: _LOGGER.exception("Error checking tracker %d", tracker_id)