#!/usr/bin/env python3 """ RSS Feed HTTP Server — generates feeds on startup + serves them via HTTP. For EasyPanel deployment: single entrypoint that handles everything. Usage: python server.py # port 8080, dir /data python server.py --dir /data --port 8080 python server.py --dir /data --port 8080 --refresh 24 """ import argparse import datetime import http.server import logging import os import subprocess import sys import threading import time logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) logger = logging.getLogger("hf-rss") # ── Feed generation ── def generate_feeds(output_dir: str): """Run run.py to generate RSS feeds.""" script_dir = os.path.dirname(os.path.abspath(__file__)) run_py = os.path.join(script_dir, "run.py") python = sys.executable logger.info("Generating RSS feeds...") try: result = subprocess.run( [python, run_py, "--output-dir", output_dir], capture_output=True, text=True, timeout=60, cwd=script_dir, ) if result.returncode == 0: logger.info(f"Feeds generated successfully in {output_dir}") for f in sorted(os.listdir(output_dir)): if f.endswith(".xml"): fpath = os.path.join(output_dir, f) size = os.path.getsize(fpath) logger.info(f" {f} ({size:,} bytes)") else: logger.error(f"Feed generation failed (exit {result.returncode})") logger.error(result.stderr[:500] if result.stderr else result.stdout[:500]) except Exception as e: logger.error(f"Feed generation error: {e}") def background_refresh(output_dir: str, interval_hours: int): """Regenerate feeds periodically.""" if interval_hours <= 0: return interval_seconds = interval_hours * 3600 logger.info(f"Background refresh every {interval_hours}h (next in {interval_hours}h)") while True: time.sleep(interval_seconds) generate_feeds(output_dir) # ── HTTP Server ── class RSSFeedHandler(http.server.SimpleHTTPRequestHandler): """Custom handler with CORS headers.""" def __init__(self, *args, directory=None, **kwargs): super().__init__(*args, directory=directory, **kwargs) def end_headers(self): self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Allow-Methods", "GET, OPTIONS") self.send_header("Cache-Control", "no-cache, no-store, must-revalidate") self.send_header("Pragma", "no-cache") self.send_header("Expires", "0") super().end_headers() def do_OPTIONS(self): self.send_response(204) self.end_headers() # ── Main ── def main(): parser = argparse.ArgumentParser(description="HF Daily Paper RSS HTTP Server") parser.add_argument("--dir", default="/data", help="Feed output directory (default: /data)") parser.add_argument("--port", type=int, default=8080, help="HTTP server port (default: 8080)") parser.add_argument("--host", default="0.0.0.0", help="Bind address (default: 0.0.0.0)") parser.add_argument("--refresh", type=int, default=24, help="Auto-refresh interval in hours (0=disabled, default: 24)") args = parser.parse_args() feed_dir = os.path.abspath(args.dir) os.makedirs(feed_dir, exist_ok=True) # Generate feeds on startup generate_feeds(feed_dir) # Start background refresh thread if args.refresh > 0: t = threading.Thread( target=background_refresh, args=(feed_dir, args.refresh), daemon=True, ) t.start() # Start HTTP server class Handler(RSSFeedHandler): def __init__(self, *args, **kwargs): super().__init__(*args, directory=feed_dir, **kwargs) server = http.server.HTTPServer((args.host, args.port), Handler) logger.info(f"Serving RSS feeds from {feed_dir}") logger.info(f"Listening on http://{args.host}:{args.port}") logger.info(f" GET /feed.xml (daily)") logger.info(f" GET /feed_weekly.xml (weekly, Mondays)") logger.info(f" GET /feed_monthly.xml (monthly, 1st)") try: server.serve_forever() except KeyboardInterrupt: logger.info("Shutting down...") server.server_close() if __name__ == "__main__": main()