|
2 | 2 | from typing import Any |
3 | 3 |
|
4 | 4 | from apscheduler.schedulers.asyncio import AsyncIOScheduler |
| 5 | +from apscheduler.triggers.cron import CronTrigger |
5 | 6 | from apscheduler.triggers.interval import IntervalTrigger |
6 | 7 | from loguru import logger |
7 | 8 |
|
| 9 | +from app.core.config import settings |
8 | 10 | from app.services.catalog import DynamicCatalogService |
9 | 11 | from app.services.stremio_service import StremioService |
10 | 12 | from app.services.token_store import token_store |
@@ -37,25 +39,49 @@ async def refresh_catalogs_for_credentials(credentials: dict[str, Any], auth_key |
37 | 39 |
|
38 | 40 |
|
39 | 41 | class BackgroundCatalogUpdater: |
40 | | - """Periodic job that refreshes catalogs for every stored credential token.""" |
| 42 | + """Periodic job that refreshes catalogs for every stored credential token. |
41 | 43 |
|
42 | | - def __init__(self, interval_seconds: int) -> None: |
43 | | - self.interval_seconds = max(60, interval_seconds) |
44 | | - self.scheduler = AsyncIOScheduler() |
| 44 | + Supports two modes: |
| 45 | + - "cron": Runs twice daily at 12:00 PM UTC and 00:00 UTC (midnight) |
| 46 | + - "interval": Runs every CATALOG_REFRESH_INTERVAL_SECONDS |
| 47 | + """ |
| 48 | + |
| 49 | + def __init__(self) -> None: |
| 50 | + self.scheduler = AsyncIOScheduler(timezone="UTC") |
| 51 | + self.update_mode = settings.CATALOG_UPDATE_MODE |
45 | 52 |
|
46 | 53 | def start(self) -> None: |
47 | 54 | if self.scheduler.running: |
48 | 55 | return |
49 | 56 |
|
50 | | - logger.info(f"Starting background catalog updater. Interval: {self.interval_seconds}s") |
51 | | - self.scheduler.add_job( |
52 | | - self.refresh_all_tokens, |
53 | | - trigger=IntervalTrigger(seconds=self.interval_seconds), |
54 | | - id="catalog_refresh", |
55 | | - replace_existing=True, |
56 | | - max_instances=1, # Prevent new job from starting if previous one is still running |
57 | | - coalesce=True, # If multiple runs are missed, only run once |
58 | | - ) |
| 57 | + if self.update_mode == "cron": |
| 58 | + logger.info(f"Starting background catalog updater. Schedule: {settings.CATALOG_UPDATE_CRON_SCHEDULES}") |
| 59 | + job_defaults = { |
| 60 | + "func": self.refresh_all_tokens, |
| 61 | + "replace_existing": True, |
| 62 | + "max_instances": 1, |
| 63 | + "coalesce": True, |
| 64 | + } |
| 65 | + for schedule in settings.CATALOG_UPDATE_CRON_SCHEDULES: |
| 66 | + self.scheduler.add_job( |
| 67 | + trigger=CronTrigger(hour=schedule["hour"], minute=schedule["minute"], timezone="UTC"), |
| 68 | + id=schedule["id"], |
| 69 | + **job_defaults, |
| 70 | + ) |
| 71 | + else: # interval mode |
| 72 | + interval_seconds = max(3600, settings.CATALOG_REFRESH_INTERVAL_SECONDS) # minimum 1 hour |
| 73 | + interval_hours = interval_seconds // 3600 |
| 74 | + logger.info(f"Starting background catalog updater. Interval: {interval_seconds}s ({interval_hours} hours)") |
| 75 | + |
| 76 | + self.scheduler.add_job( |
| 77 | + self.refresh_all_tokens, |
| 78 | + trigger=IntervalTrigger(seconds=interval_seconds), |
| 79 | + id="catalog_refresh", |
| 80 | + replace_existing=True, |
| 81 | + max_instances=1, # Prevent new job from starting if previous one is still running |
| 82 | + coalesce=True, # If multiple runs are missed, only run once |
| 83 | + ) |
| 84 | + |
59 | 85 | self.scheduler.start() |
60 | 86 |
|
61 | 87 | async def stop(self) -> None: |
|
0 commit comments