clean: remove Hermes cron artifacts, simplify for EasyPanel deploy
Some checks failed
Update Hugging Face Papers RSS Feed / update-feed (push) Has been cancelled
Update Hugging Face Papers Weekly RSS Feed / update-feed-weekly (push) Has been cancelled
Update Hugging Face Papers Monthly RSS Feed / update-feed-monthly (push) Has been cancelled

This commit is contained in:
2026-06-27 14:51:24 +07:00
parent 3ad39cb87d
commit b86665b7ff
4 changed files with 106 additions and 111 deletions

119
server.py
View File

@@ -1,26 +1,80 @@
#!/usr/bin/env python3
"""
Minimal HTTP server to serve generated RSS feed files.
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 ./feeds
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 http.server
import os
import sys
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 and auto-refresh."""
"""Custom handler with CORS headers."""
def __init__(self, *args, directory=None, **kwargs):
super().__init__(*args, directory=directory, **kwargs)
def end_headers(self):
# CORS — allow RSS readers to fetch from any origin
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")
@@ -32,49 +86,52 @@ class RSSFeedHandler(http.server.SimpleHTTPRequestHandler):
self.send_response(204)
self.end_headers()
def log_message(self, format, *args):
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
sys.stderr.write(f"[{timestamp}] {args[0]} {args[1]} {args[2]}\n")
# ── Main ──
def main():
parser = argparse.ArgumentParser(description="RSS Feed HTTP Server")
parser.add_argument("--dir", default=os.path.join(os.getcwd(), "feeds"),
help="Directory containing RSS feeds")
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="Port to serve on")
help="HTTP server port (default: 8080)")
parser.add_argument("--host", default="0.0.0.0",
help="Host to bind to")
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)
if not os.path.exists(feed_dir):
print(f"[server.py] Creating feed directory: {feed_dir}")
os.makedirs(feed_dir, exist_ok=True)
os.makedirs(feed_dir, exist_ok=True)
# Ensure at least one feed exists
feeds = sorted(f for f in os.listdir(feed_dir) if f.endswith(".xml"))
if feeds:
print(f"[server.py] Found {len(feeds)} feed(s): {', '.join(feeds)}")
else:
print(f"[server.py] No feeds yet in {feed_dir}. Run 'run.py' first or wait for cron.")
# 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)
print(f"[server.py] Serving RSS feeds from {feed_dir}")
print(f"[server.py] Listening on http://{args.host}:{args.port}")
print(f"[server.py] Feed URLs:")
print(f" http://localhost:{args.port}/feed.xml (daily)")
print(f" http://localhost:{args.port}/feed_weekly.xml (weekly)")
print(f" http://localhost:{args.port}/feed_monthly.xml (monthly)")
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:
print("\n[server.py] Shutting down...")
logger.info("Shutting down...")
server.server_close()