"""Feed management API — CRUD on feeds.yaml.""" import os import tempfile import yaml from flask import Blueprint, jsonify, request from ..config import FEEDS_CONFIG from ..db import get_db bp = Blueprint("feeds", __name__) def _read_feeds(): if not FEEDS_CONFIG.exists(): return [] with open(FEEDS_CONFIG) as f: data = yaml.safe_load(f) or {} return data.get("feeds", []) def _write_feeds(feeds): data = {"feeds": feeds} # Atomic write: temp file then rename fd, tmp_path = tempfile.mkstemp( dir=str(FEEDS_CONFIG.parent), suffix=".yaml.tmp" ) try: with os.fdopen(fd, "w") as f: yaml.dump(data, f, default_flow_style=False, sort_keys=False) os.replace(tmp_path, str(FEEDS_CONFIG)) except Exception: os.unlink(tmp_path) raise @bp.route("/feeds") def list_feeds(): feeds = _read_feeds() db = get_db() result = [] for feed in feeds: name = feed.get("name", "") state = db.execute( "SELECT last_poll FROM feed_state WHERE feed_name=?", (name,) ).fetchone() ep_count = db.execute( "SELECT COUNT(*) as c FROM episodes WHERE feed_name=?", (name,) ).fetchone()["c"] result.append({ **feed, "last_poll": state["last_poll"] if state else None, "episode_count": ep_count, }) return jsonify(result) @bp.route("/feeds", methods=["POST"]) def add_feed(): data = request.get_json() if not data or not data.get("name") or not data.get("url"): return jsonify({"error": "name and url are required"}), 400 feeds = _read_feeds() # Check for duplicate name for f in feeds: if f.get("name") == data["name"]: return jsonify({"error": "Feed with this name already exists"}), 409 new_feed = { "name": data["name"], "url": data["url"], "enabled": data.get("enabled", True), "priority": data.get("priority", 10), "max_episodes": data.get("max_episodes", 20), } feeds.append(new_feed) _write_feeds(feeds) return jsonify(new_feed), 201 @bp.route("/feeds/", methods=["PUT"]) def update_feed(name): data = request.get_json() if not data: return jsonify({"error": "JSON body required"}), 400 feeds = _read_feeds() found = False for feed in feeds: if feed.get("name") == name: if "enabled" in data: feed["enabled"] = data["enabled"] if "url" in data: feed["url"] = data["url"] if "priority" in data: feed["priority"] = data["priority"] if "max_episodes" in data: feed["max_episodes"] = data["max_episodes"] found = True break if not found: return jsonify({"error": "Feed not found"}), 404 _write_feeds(feeds) return jsonify({"status": "updated"}) @bp.route("/feeds/", methods=["DELETE"]) def delete_feed(name): feeds = _read_feeds() new_feeds = [f for f in feeds if f.get("name") != name] if len(new_feeds) == len(feeds): return jsonify({"error": "Feed not found"}), 404 _write_feeds(new_feeds) return jsonify({"status": "deleted"})