Initial commit: self-hosted personal radio station

Flask + React web UI with audio player, podcast queue, feed management,
episode browser, music library, schedule viewer, and log tail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 19:01:33 -07:00
co-authored by Claude Opus 4.6
commit 3d635b742c
53 changed files with 7023 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
"""GET /api/episodes — paginated episode browser."""
from flask import Blueprint, jsonify, request
from ..db import get_db
bp = Blueprint("episodes", __name__)
@bp.route("/episodes")
def list_episodes():
db = get_db()
page = request.args.get("page", 1, type=int)
per_page = request.args.get("per_page", 20, type=int)
per_page = min(per_page, 100)
feed = request.args.get("feed")
status = request.args.get("status")
conditions = []
params = []
if feed:
conditions.append("feed_name = ?")
params.append(feed)
if status == "downloaded":
conditions.append("downloaded = 1")
elif status == "queued":
conditions.append("queued = 1")
elif status == "played":
conditions.append("played = 1")
where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
total = db.execute(
f"SELECT COUNT(*) as c FROM episodes {where}", params
).fetchone()["c"]
offset = (page - 1) * per_page
rows = db.execute(
f"""SELECT * FROM episodes {where}
ORDER BY discovered DESC
LIMIT ? OFFSET ?""",
params + [per_page, offset],
).fetchall()
return jsonify({
"episodes": [dict(r) for r in rows],
"total": total,
"page": page,
"per_page": per_page,
"pages": (total + per_page - 1) // per_page,
})
@bp.route("/episodes/feeds")
def list_episode_feeds():
"""Return distinct feed names for filter dropdowns."""
db = get_db()
rows = db.execute(
"SELECT DISTINCT feed_name FROM episodes ORDER BY feed_name"
).fetchall()
return jsonify([r["feed_name"] for r in rows])