Spaces:
Running
Running
fix: proxy videos through Space same-origin to avoid cross-origin <video> blocks
Browse files- app.py +71 -1
- requirements.txt +2 -0
app.py
CHANGED
|
@@ -79,6 +79,17 @@ print(f"[mbench-ann] loaded {len(TASKS)} tasks × {len(MODELS)} models = {len(PO
|
|
| 79 |
|
| 80 |
|
| 81 |
def _video_url(model: str, task_id: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
return hf_hub_url(
|
| 83 |
DATASET_REPO,
|
| 84 |
filename=f"MBench-V/{model}/videos/{task_id}.mp4",
|
|
@@ -462,4 +473,63 @@ with gr.Blocks(title="MBench-V 标注", theme=gr.themes.Soft(),
|
|
| 462 |
|
| 463 |
|
| 464 |
if __name__ == "__main__":
|
| 465 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
|
| 81 |
def _video_url(model: str, task_id: str) -> str:
|
| 82 |
+
"""Same-origin proxy URL served by this Space (see /video route below).
|
| 83 |
+
|
| 84 |
+
We proxy the mp4 through the Space to avoid cross-origin issues that cause
|
| 85 |
+
some browsers to refuse playing HF's xet CDN as a <video> source even
|
| 86 |
+
though the CDN itself is reachable.
|
| 87 |
+
"""
|
| 88 |
+
return f"/video/{model}/{task_id}.mp4"
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _hf_video_url(model: str, task_id: str) -> str:
|
| 92 |
+
"""Real HF dataset URL — used only inside the proxy route."""
|
| 93 |
return hf_hub_url(
|
| 94 |
DATASET_REPO,
|
| 95 |
filename=f"MBench-V/{model}/videos/{task_id}.mp4",
|
|
|
|
| 473 |
|
| 474 |
|
| 475 |
if __name__ == "__main__":
|
| 476 |
+
# -----------------------------------------------------------------------
|
| 477 |
+
# Same-origin video proxy route
|
| 478 |
+
# -----------------------------------------------------------------------
|
| 479 |
+
# Many browsers refuse to play cross-origin third-party-cookie-tainted
|
| 480 |
+
# <video> sources (hf.space iframe → xet CDN). We dodge this by streaming
|
| 481 |
+
# the mp4 through this Space's own FastAPI app so the browser sees the
|
| 482 |
+
# video on the same origin as the page.
|
| 483 |
+
import httpx
|
| 484 |
+
import uvicorn
|
| 485 |
+
from fastapi import FastAPI, HTTPException, Request
|
| 486 |
+
from fastapi.responses import StreamingResponse
|
| 487 |
+
|
| 488 |
+
fastapi_app = FastAPI()
|
| 489 |
+
_video_client = httpx.AsyncClient(timeout=30.0, follow_redirects=True)
|
| 490 |
+
|
| 491 |
+
@fastapi_app.api_route(
|
| 492 |
+
"/video/{model}/{task_id}.mp4",
|
| 493 |
+
methods=["GET", "HEAD"],
|
| 494 |
+
include_in_schema=False,
|
| 495 |
+
)
|
| 496 |
+
async def _proxy_video(model: str, task_id: str, request: Request):
|
| 497 |
+
if model not in MODELS or task_id not in TASK_BY_ID:
|
| 498 |
+
raise HTTPException(404, "unknown (model, task_id)")
|
| 499 |
+
upstream = _hf_video_url(model, task_id)
|
| 500 |
+
req_headers = {}
|
| 501 |
+
if (rng := request.headers.get("range")):
|
| 502 |
+
req_headers["range"] = rng
|
| 503 |
+
try:
|
| 504 |
+
upstream_resp = await _video_client.send(
|
| 505 |
+
_video_client.build_request("GET", upstream, headers=req_headers),
|
| 506 |
+
stream=True,
|
| 507 |
+
)
|
| 508 |
+
except Exception as e:
|
| 509 |
+
raise HTTPException(502, f"upstream fetch failed: {e}")
|
| 510 |
+
passthrough_headers = {}
|
| 511 |
+
for h in ("content-type", "content-length", "accept-ranges",
|
| 512 |
+
"content-range", "etag", "last-modified"):
|
| 513 |
+
if h in upstream_resp.headers:
|
| 514 |
+
passthrough_headers[h] = upstream_resp.headers[h]
|
| 515 |
+
passthrough_headers.setdefault("content-type", "video/mp4")
|
| 516 |
+
passthrough_headers["cache-control"] = "public, max-age=300"
|
| 517 |
+
|
| 518 |
+
async def _body():
|
| 519 |
+
try:
|
| 520 |
+
async for chunk in upstream_resp.aiter_bytes(chunk_size=65536):
|
| 521 |
+
yield chunk
|
| 522 |
+
finally:
|
| 523 |
+
await upstream_resp.aclose()
|
| 524 |
+
|
| 525 |
+
return StreamingResponse(
|
| 526 |
+
_body(),
|
| 527 |
+
status_code=upstream_resp.status_code,
|
| 528 |
+
headers=passthrough_headers,
|
| 529 |
+
)
|
| 530 |
+
|
| 531 |
+
demo.queue(default_concurrency_limit=16)
|
| 532 |
+
fastapi_app = gr.mount_gradio_app(fastapi_app, demo, path="/")
|
| 533 |
+
print("[mbench-ann] mounted Gradio at '/' with /video proxy route")
|
| 534 |
+
uvicorn.run(fastapi_app, host="0.0.0.0", port=7860)
|
| 535 |
+
|
requirements.txt
CHANGED
|
@@ -1,2 +1,4 @@
|
|
| 1 |
gradio==5.9.1
|
| 2 |
huggingface_hub>=0.26,<0.30
|
|
|
|
|
|
|
|
|
| 1 |
gradio==5.9.1
|
| 2 |
huggingface_hub>=0.26,<0.30
|
| 3 |
+
httpx>=0.27
|
| 4 |
+
uvicorn>=0.30
|