Spaces:
Running on Zero
Running on Zero
Download app.py from MokerBao/rednote-api: direct link, hf CLI and curl.
- Browser
- Download file 3.14 kB
-
https://huggingface.co/spaces/MokerBao/rednote-api/resolve/main/app.py
- Command line
-
hf download hf://spaces/MokerBao/rednote-api/app.py
-
curl -L -o app.py https://huggingface.co/spaces/MokerBao/rednote-api/resolve/main/app.py
3.14 kB
| import gradio as gr | |
| from fastapi import Body, Query | |
| from fastapi.responses import JSONResponse | |
| from scraper import is_valid_rednote_url, scrape | |
| # Space ZeroGPU bắt buộc phải có ít nhất một hàm @spaces.GPU (app này không dùng GPU thật). | |
| # Nếu chạy ở nơi không có thư viện `spaces` (CPU / máy local) thì bỏ qua. | |
| try: | |
| import spaces | |
| def _gpu_placeholder(): | |
| return None | |
| except ImportError: | |
| pass | |
| # ---------------------------------------------------------------- core | |
| def process_url(url: str): | |
| """Trả về (payload, http_status) giống main.rs.""" | |
| url = (url or "").strip() | |
| if not is_valid_rednote_url(url): | |
| return {"status": False, "error": "Invalid Xiaohongshu URL"}, 400 | |
| try: | |
| data = scrape(url) | |
| except ValueError as e: | |
| return {"status": False, "error": str(e)}, 400 | |
| data["status"] = True | |
| return data, 200 | |
| # ---------------------------------------------------------------- REST API (giữ nguyên đường dẫn bản Rust) | |
| CORS = {"Access-Control-Allow-Origin": "*"} | |
| def api_get(url: str = Query(..., description="Rednote / xhslink URL")): | |
| payload, code = process_url(url) | |
| return JSONResponse(payload, status_code=code, headers=CORS) | |
| def api_post(body: dict = Body(..., examples=[{"url": "http://xhslink.com/o/21DKXV988zp"}])): | |
| payload, code = process_url(body.get("url", "")) | |
| return JSONResponse(payload, status_code=code, headers=CORS) | |
| # ---------------------------------------------------------------- Gradio UI | |
| def ui_fn(url: str): | |
| payload, _ = process_url(url) | |
| if not payload.get("status"): | |
| raise gr.Error(payload.get("error", "Unknown error")) | |
| links = "\n".join(f"- [{d['quality']}]({d['url']})" for d in payload["downloads"]) or "_Không có video_" | |
| info = ( | |
| f"**{payload['title']}** \n" | |
| f"👤 {payload['nickname']} · ❤️ {payload['engagement']['likes']} · " | |
| f"💬 {payload['engagement']['comments']} · ⭐ {payload['engagement']['collects']}\n\n" | |
| f"{payload['desc']}\n\n### Video\n{links}" | |
| ) | |
| return info, payload["images"], payload | |
| with gr.Blocks(title="Rednote Downloader") as demo: | |
| gr.Markdown("# Rednote (小红书) Downloader\nDán link `xhslink.com` hoặc `xiaohongshu.com`.") | |
| inp = gr.Textbox(label="URL", placeholder="http://xhslink.com/o/21DKXV988zp") | |
| btn = gr.Button("Lấy dữ liệu", variant="primary") | |
| info_md = gr.Markdown() | |
| gallery = gr.Gallery(label="Ảnh", columns=4) | |
| raw = gr.JSON(label="JSON") | |
| btn.click(ui_fn, inp, [info_md, gallery, raw], api_name="rednote") | |
| inp.submit(ui_fn, inp, [info_md, gallery, raw]) | |
| if __name__ == "__main__": | |
| # Khởi động kiểu Gradio chuẩn (tương thích ZeroGPU), rồi gắn thêm route REST vào app bên dưới | |
| fastapi_app, _, _ = demo.launch(server_name="0.0.0.0", server_port=7860, prevent_thread_lock=True, ssr_mode=False) | |
| fastapi_app.add_api_route("/api/rednote", api_get, methods=["GET"]) | |
| fastapi_app.add_api_route("/api/rednote", api_post, methods=["POST"]) | |
| demo.block_thread() |