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
81 lines
2.7 KiB
Python
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)
|