feat: add historical backfill with --init CLI and episode numbering

Adds a --init mode that seeds the database with past shows from a given
anchor episode/date forward, batch-fetching likes from SoundCloud and
partitioning them into weekly buckets. Episode numbers are tracked in
the shows table and auto-incremented by the poller for new shows.

Includes full API documentation (docs/api.md) and updated README.

Made-with: Cursor
This commit is contained in:
cottongin
2026-03-12 02:09:15 -04:00
parent c88826ac4d
commit cb3ae403cf
14 changed files with 922 additions and 21 deletions

View File

@@ -20,7 +20,8 @@ CREATE TABLE IF NOT EXISTS shows (
id INTEGER PRIMARY KEY AUTOINCREMENT,
week_start TEXT NOT NULL,
week_end TEXT NOT NULL,
created_at TEXT NOT NULL
created_at TEXT NOT NULL,
episode_number INTEGER
);
CREATE TABLE IF NOT EXISTS show_tracks (
@@ -49,6 +50,11 @@ class Database:
def initialize(self) -> None:
conn = self._connect()
conn.executescript(SCHEMA)
try:
conn.execute("ALTER TABLE shows ADD COLUMN episode_number INTEGER")
conn.commit()
except sqlite3.OperationalError:
pass
conn.close()
def upsert_track(self, track: Track) -> None:
@@ -102,26 +108,36 @@ class Database:
)
def get_or_create_show(
self, week_start: datetime, week_end: datetime
self,
week_start: datetime,
week_end: datetime,
episode_number: int | None = None,
) -> Show:
conn = self._connect()
row = conn.execute(
"SELECT id, week_start, week_end, created_at FROM shows "
"SELECT id, week_start, week_end, created_at, episode_number FROM shows "
"WHERE week_start = ? AND week_end = ?",
(week_start.isoformat(), week_end.isoformat()),
).fetchone()
if row is not None:
if episode_number is not None and row["episode_number"] != episode_number:
conn.execute(
"UPDATE shows SET episode_number = ? WHERE id = ?",
(episode_number, row["id"]),
)
conn.commit()
conn.close()
return Show(
id=row["id"],
week_start=datetime.fromisoformat(row["week_start"]),
week_end=datetime.fromisoformat(row["week_end"]),
created_at=datetime.fromisoformat(row["created_at"]),
episode_number=episode_number if episode_number is not None else row["episode_number"],
)
now = datetime.now(timezone.utc).isoformat()
cursor = conn.execute(
"INSERT INTO shows (week_start, week_end, created_at) VALUES (?, ?, ?)",
(week_start.isoformat(), week_end.isoformat(), now),
"INSERT INTO shows (week_start, week_end, created_at, episode_number) VALUES (?, ?, ?, ?)",
(week_start.isoformat(), week_end.isoformat(), now, episode_number),
)
conn.commit()
show_id = cursor.lastrowid
@@ -131,6 +147,7 @@ class Database:
week_start=week_start,
week_end=week_end,
created_at=datetime.fromisoformat(now),
episode_number=episode_number,
)
def get_show_tracks(self, show_id: int) -> list[dict]:
@@ -203,9 +220,9 @@ class Database:
conn = self._connect()
rows = conn.execute(
"""
SELECT id, week_start, week_end, created_at
SELECT id, week_start, week_end, created_at, episode_number
FROM shows
ORDER BY created_at DESC
ORDER BY week_start DESC
LIMIT ? OFFSET ?
""",
(limit, offset),
@@ -217,10 +234,28 @@ class Database:
week_start=datetime.fromisoformat(row["week_start"]),
week_end=datetime.fromisoformat(row["week_end"]),
created_at=datetime.fromisoformat(row["created_at"]),
episode_number=row["episode_number"],
)
for row in rows
]
def get_latest_episode_number(self) -> int | None:
conn = self._connect()
row = conn.execute(
"SELECT MAX(episode_number) as max_ep FROM shows WHERE episode_number IS NOT NULL"
).fetchone()
conn.close()
return row["max_ep"] if row else None
def update_show_episode_number(self, show_id: int, episode_number: int) -> None:
conn = self._connect()
conn.execute(
"UPDATE shows SET episode_number = ? WHERE id = ?",
(episode_number, show_id),
)
conn.commit()
conn.close()
def has_track_in_show(self, show_id: int, track_id: int) -> bool:
conn = self._connect()
row = conn.execute(