Skip to content

Commit 9a3b4af

Browse files
Sync dev changes (#14)
2 parents e1dfb74 + d793f3e commit 9a3b4af

File tree

4 files changed

+53
-22
lines changed

4 files changed

+53
-22
lines changed

.env.example

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@ TOKEN_SALT=replace-with-a-long-random-string
77
TOKEN_TTL_SECONDS=0
88
ANNOUNCEMENT_HTML=
99
HOST_NAME=<your_addon_url>
10-
RECOMMENDATION_SOURCE_ITEMS_LIMIT=10
11-
# fetches recent watched/loved 10 movies and series to recommend based on those
10+
RECOMMENDATION_SOURCE_ITEMS_LIMIT=10 # fetches recent watched/loved 10 movies and series to recommend based on those
11+
12+
# UPDATER
13+
CATALOG_UPDATE_MODE=cron
14+
CATALOG_UPDATE_CRON_SCHEDULES=[{"hour": 12, "minute": 0, "id": "catalog_refresh_noon"},{"hour": 0, "minute": 0, "id": "catalog_refresh_midnight"}]
15+
CATALOG_REFRESH_INTERVAL_SECONDS=6*60*60

app/core/app.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,9 @@ async def lifespan(app: FastAPI):
3737
global catalog_updater
3838

3939
# Startup
40-
if settings.AUTO_UPDATE_CATALOGS and settings.CATALOG_REFRESH_INTERVAL_SECONDS > 3600: # minimum 1 hour
41-
catalog_updater = BackgroundCatalogUpdater(interval_seconds=settings.CATALOG_REFRESH_INTERVAL_SECONDS)
40+
if settings.AUTO_UPDATE_CATALOGS:
41+
catalog_updater = BackgroundCatalogUpdater()
4242
catalog_updater.start()
43-
logger.info(
44-
"Background catalog updates enabled (interval=%ss)",
45-
settings.CATALOG_REFRESH_INTERVAL_SECONDS,
46-
)
4743
yield
4844

4945
# Shutdown

app/core/config.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ class Settings(BaseSettings):
2323
TOKEN_TTL_SECONDS: int = 0 # 0 = never expire
2424
ANNOUNCEMENT_HTML: str = ""
2525
AUTO_UPDATE_CATALOGS: bool = True
26-
CATALOG_REFRESH_INTERVAL_SECONDS: int = 6 * 60 * 60 # 6 hours
26+
CATALOG_UPDATE_MODE: Literal["cron", "interval"] = "cron" # "cron" for fixed times, "interval" for periodic
27+
CATALOG_UPDATE_CRON_SCHEDULES: list[dict] = (
28+
{"hour": 12, "minute": 0, "id": "catalog_refresh_noon"},
29+
{"hour": 0, "minute": 0, "id": "catalog_refresh_midnight"},
30+
)
31+
CATALOG_REFRESH_INTERVAL_SECONDS: int = 6 * 60 * 60 # 6 hours (used when CATALOG_UPDATE_MODE="interval")
2732
APP_ENV: Literal["development", "production"] = "development"
2833
HOST_NAME: str = "https://1ccea4301587-watchly.baby-beamup.club"
2934

app/services/catalog_updater.py

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
from typing import Any
33

44
from apscheduler.schedulers.asyncio import AsyncIOScheduler
5+
from apscheduler.triggers.cron import CronTrigger
56
from apscheduler.triggers.interval import IntervalTrigger
67
from loguru import logger
78

9+
from app.core.config import settings
810
from app.services.catalog import DynamicCatalogService
911
from app.services.stremio_service import StremioService
1012
from app.services.token_store import token_store
@@ -37,25 +39,49 @@ async def refresh_catalogs_for_credentials(credentials: dict[str, Any], auth_key
3739

3840

3941
class BackgroundCatalogUpdater:
40-
"""Periodic job that refreshes catalogs for every stored credential token."""
42+
"""Periodic job that refreshes catalogs for every stored credential token.
4143
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
4552

4653
def start(self) -> None:
4754
if self.scheduler.running:
4855
return
4956

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+
5985
self.scheduler.start()
6086

6187
async def stop(self) -> None:

0 commit comments

Comments
 (0)