Files
NtR-soudcloud-fetcher/src/ntr_fetcher/poller.py
cottongin 82049ab47f feat: delay show rotation during live recording window
Decouple the like-window boundary (Wed 10pm ET) from when the system
rotates to a new show. NTR_SHOW_ROTATION_DELAY_HOURS=2 keeps the
previous week's show visible during the ~2 hour recording, then creates
the new show at midnight.

Made-with: Cursor
2026-04-01 21:29:42 -04:00

81 lines
2.7 KiB
Python

import asyncio
import logging
from datetime import datetime, timezone
from ntr_fetcher.db import Database
from ntr_fetcher.soundcloud import SoundCloudClient
from ntr_fetcher.week import get_current_show_week
logger = logging.getLogger(__name__)
class Poller:
def __init__(
self,
db: Database,
soundcloud: SoundCloudClient,
soundcloud_user: str,
show_day: int,
show_hour: int,
poll_interval: float,
rotation_delay_hours: float = 0,
):
self._db = db
self._sc = soundcloud
self._user = soundcloud_user
self._show_day = show_day
self._show_hour = show_hour
self._poll_interval = poll_interval
self._rotation_delay_hours = rotation_delay_hours
self._user_id: int | None = None
self.last_fetch: datetime | None = None
self.alive = True
async def _get_user_id(self) -> int:
if self._user_id is None:
self._user_id = await self._sc.resolve_user(self._user)
return self._user_id
async def poll_once(self, full: bool = False) -> None:
user_id = await self._get_user_id()
now = datetime.now(timezone.utc)
week_start, week_end = get_current_show_week(
now, self._show_day, self._show_hour, self._rotation_delay_hours,
)
show = self._db.get_or_create_show(week_start, week_end)
if show.episode_number is None:
latest = self._db.get_latest_episode_number()
if latest is not None:
new_ep = latest + 1
self._db.update_show_episode_number(show.id, new_ep)
logger.info("Auto-assigned episode #%d to show %d", new_ep, show.id)
tracks = await self._sc.fetch_likes(
user_id=user_id,
since=week_start,
until=week_end,
)
for track in tracks:
self._db.upsert_track(track)
track_ids = [t.id for t in tracks]
self._db.set_show_tracks(show.id, track_ids)
self.last_fetch = datetime.now(timezone.utc)
logger.info("Fetched %d tracks for show %d", len(tracks), show.id)
async def run_supervised(self, restart_delay: float = 30.0) -> None:
while True:
try:
self.alive = True
await self.poll_once()
await asyncio.sleep(self._poll_interval)
except asyncio.CancelledError:
self.alive = False
raise
except Exception:
self.alive = False
logger.exception("Poller failed, restarting in %.1fs", restart_delay)
await asyncio.sleep(restart_delay)