radio/web/api/queue.py
profit 3d635b742c 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>
2026-03-13 19:01:33 -07:00

121 lines
3.5 KiB
Python

"""Queue management API — GET/POST/DELETE /api/queue."""
import os
import re
import shutil
from datetime import datetime
from pathlib import Path
from flask import Blueprint, jsonify, request
from ..config import QUEUE_DIR, PODCAST_DIR
from ..db import get_db
bp = Blueprint("queue", __name__)
@bp.route("/queue")
def list_queue():
db = get_db()
rows = db.execute("""
SELECT q.id, q.position, q.enqueued, q.played,
e.id as episode_id, e.feed_name, e.title, e.pub_date, e.file_path
FROM queue q
JOIN episodes e ON e.id = q.episode_id
WHERE q.played = 0
ORDER BY q.position
""").fetchall()
return jsonify([dict(r) for r in rows])
@bp.route("/queue", methods=["POST"])
def enqueue():
data = request.get_json()
if not data:
return jsonify({"error": "JSON body required"}), 400
episode_id = data.get("episode_id")
file_path = data.get("file_path")
db = get_db()
if episode_id:
row = db.execute(
"SELECT * FROM episodes WHERE id=? AND downloaded=1", (episode_id,)
).fetchone()
if not row:
return jsonify({"error": "Episode not found or not downloaded"}), 404
file_path = row["file_path"]
elif file_path:
if not os.path.exists(file_path):
return jsonify({"error": "File not found"}), 404
else:
return jsonify({"error": "Provide episode_id or file_path"}), 400
# Create queue entry if episode_id is available
if episode_id:
existing = db.execute(
"SELECT 1 FROM queue WHERE episode_id=? AND played=0", (episode_id,)
).fetchone()
if existing:
return jsonify({"error": "Episode already in queue"}), 409
next_pos = db.execute(
"SELECT COALESCE(MAX(position), 0) + 1 as p FROM queue"
).fetchone()["p"]
db.execute(
"INSERT INTO queue (episode_id, position) VALUES (?, ?)",
(episode_id, next_pos),
)
db.execute(
"UPDATE episodes SET queued=1, played=0 WHERE id=?", (episode_id,)
)
db.commit()
# Create symlink in queue directory
QUEUE_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
src = Path(file_path)
eid = episode_id or 0
link_name = f"{timestamp}_{eid:06d}_{src.name}"
link_path = QUEUE_DIR / link_name
try:
link_path.symlink_to(src.resolve())
except (OSError, FileExistsError):
try:
link_path.hardlink_to(src.resolve())
except (OSError, FileExistsError):
shutil.copy2(str(src), str(link_path))
return jsonify({"status": "enqueued", "link": link_name}), 201
@bp.route("/queue/<int:queue_id>", methods=["DELETE"])
def dequeue(queue_id):
db = get_db()
row = db.execute(
"SELECT q.*, e.id as eid FROM queue q JOIN episodes e ON e.id=q.episode_id WHERE q.id=?",
(queue_id,),
).fetchone()
if not row:
return jsonify({"error": "Queue item not found"}), 404
# Remove symlink from queue directory
eid = row["eid"]
if QUEUE_DIR.exists():
pattern = re.compile(rf"_0*{eid}_")
for item in QUEUE_DIR.iterdir():
if pattern.search(item.name):
item.unlink(missing_ok=True)
break
db.execute("DELETE FROM queue WHERE id=?", (queue_id,))
db.execute("UPDATE episodes SET queued=0 WHERE id=?", (eid,))
db.commit()
return jsonify({"status": "removed"})