Spaces:
Running on Zero
Running on Zero
File size: 3,512 Bytes
d285f12 cd02463 d285f12 ac02880 cd02463 ac02880 d285f12 7c47628 d285f12 cd02463 d285f12 cd02463 d285f12 cd02463 d285f12 cd02463 d285f12 7c47628 d285f12 7c47628 d285f12 7c47628 d285f12 cd02463 39b2cfc cd02463 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | 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
@spaces.GPU
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/Rednote 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 _fmt_size(n) -> str:
try:
n = float(n)
except (TypeError, ValueError):
return ""
return f" · {n / 1024 / 1024:.1f} MB" if n > 0 else ""
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']}){_fmt_size(d.get('size'))}" 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\n"
"Dán link `xhslink.com`, `xiaohongshu.com` hoặc `rednote.com` "
"(giữ nguyên cả `xsec_token` nếu có)."
)
inp = gr.Textbox(label="URL", placeholder="https://www.rednote.com/discovery/item/<id>?xsec_token=...")
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() |