140 lines
4.5 KiB
Python
140 lines
4.5 KiB
Python
#!/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()
|