Add preset seed thumbnails; remove footnote blockquote
Browse files
app.py
CHANGED
|
@@ -50,7 +50,7 @@ from omegaconf import OmegaConf
|
|
| 50 |
from huggingface_hub import snapshot_download
|
| 51 |
|
| 52 |
from fastapi import UploadFile, File, WebSocket, WebSocketDisconnect
|
| 53 |
-
from fastapi.responses import HTMLResponse, JSONResponse
|
| 54 |
from gradio import Server
|
| 55 |
from gradio.context import LocalContext
|
| 56 |
|
|
@@ -62,6 +62,16 @@ if str(APP_DIR) not in sys.path:
|
|
| 62 |
MODEL_ID = "acvlab/ABot-World-0-5B-LF"
|
| 63 |
CKPT_DIR = APP_DIR / "checkpoints" / "ABot-World-0-5B-LF"
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
# ββ Stream / rollout configuration βββββββββββββββββββββββββββββββββββββββββββ
|
| 66 |
# 704x1280 is the native training resolution used by the upstream web client.
|
| 67 |
STREAM_HEIGHT = 704
|
|
@@ -583,6 +593,50 @@ async def upload_seed(file: UploadFile = File(...)):
|
|
| 583 |
return {"seed_path": tmp.name}
|
| 584 |
|
| 585 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 586 |
@app.get("/", response_class=HTMLResponse)
|
| 587 |
async def homepage():
|
| 588 |
html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")
|
|
|
|
| 50 |
from huggingface_hub import snapshot_download
|
| 51 |
|
| 52 |
from fastapi import UploadFile, File, WebSocket, WebSocketDisconnect
|
| 53 |
+
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
|
| 54 |
from gradio import Server
|
| 55 |
from gradio.context import LocalContext
|
| 56 |
|
|
|
|
| 62 |
MODEL_ID = "acvlab/ABot-World-0-5B-LF"
|
| 63 |
CKPT_DIR = APP_DIR / "checkpoints" / "ABot-World-0-5B-LF"
|
| 64 |
|
| 65 |
+
# Preset starting-world images bundled with the Space (sourced from the ABot-World
|
| 66 |
+
# repo). Shown in the UI as clickable thumbnails that seed the i2v rollout directly.
|
| 67 |
+
EXAMPLES_DIR = APP_DIR / "examples"
|
| 68 |
+
EXAMPLE_SEEDS = [
|
| 69 |
+
{"name": "desert_valley.png", "label": "Desert valley"},
|
| 70 |
+
{"name": "forest_stream.png", "label": "Forest stream"},
|
| 71 |
+
{"name": "mountain_meadow.png", "label": "Mountain meadow"},
|
| 72 |
+
{"name": "example.png", "label": "Sample scene"},
|
| 73 |
+
]
|
| 74 |
+
|
| 75 |
# ββ Stream / rollout configuration βββββββββββββββββββββββββββββββββββββββββββ
|
| 76 |
# 704x1280 is the native training resolution used by the upstream web client.
|
| 77 |
STREAM_HEIGHT = 704
|
|
|
|
| 593 |
return {"seed_path": tmp.name}
|
| 594 |
|
| 595 |
|
| 596 |
+
def _safe_example_path(name: str) -> Optional[Path]:
|
| 597 |
+
"""Resolve `name` to a bundled example image, guarding against traversal."""
|
| 598 |
+
if not any(name == e["name"] for e in EXAMPLE_SEEDS):
|
| 599 |
+
return None
|
| 600 |
+
path = (EXAMPLES_DIR / name).resolve()
|
| 601 |
+
if EXAMPLES_DIR.resolve() not in path.parents or not path.is_file():
|
| 602 |
+
return None
|
| 603 |
+
return path
|
| 604 |
+
|
| 605 |
+
|
| 606 |
+
@app.get("/example_seeds")
|
| 607 |
+
async def example_seeds():
|
| 608 |
+
"""List the preset starting-world images available as clickable thumbnails."""
|
| 609 |
+
return {"examples": [e for e in EXAMPLE_SEEDS if (EXAMPLES_DIR / e["name"]).is_file()]}
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
@app.get("/example_thumb")
|
| 613 |
+
async def example_thumb(name: str = ""):
|
| 614 |
+
"""Serve a preset starting-world image (for thumbnail display in the UI)."""
|
| 615 |
+
path = _safe_example_path(name)
|
| 616 |
+
if path is None:
|
| 617 |
+
return JSONResponse({"error": "unknown example"}, status_code=404)
|
| 618 |
+
return FileResponse(str(path), media_type="image/png")
|
| 619 |
+
|
| 620 |
+
|
| 621 |
+
@app.get("/example_seed")
|
| 622 |
+
async def example_seed(name: str = ""):
|
| 623 |
+
"""Seed the i2v rollout from a bundled preset image (no upload required).
|
| 624 |
+
|
| 625 |
+
Copies the chosen example into a server-side temp file and returns its path,
|
| 626 |
+
mirroring /upload_seed so the browser can pass it to /start_game as seed_path.
|
| 627 |
+
"""
|
| 628 |
+
path = _safe_example_path(name)
|
| 629 |
+
if path is None:
|
| 630 |
+
return JSONResponse({"error": "unknown example"}, status_code=404)
|
| 631 |
+
try:
|
| 632 |
+
img = Image.open(path).convert("RGB")
|
| 633 |
+
except Exception:
|
| 634 |
+
return JSONResponse({"error": "could not read example image"}, status_code=500)
|
| 635 |
+
tmp = tempfile.NamedTemporaryFile(prefix="abot_seed_", suffix=".png", delete=False)
|
| 636 |
+
img.save(tmp.name, format="PNG")
|
| 637 |
+
return {"seed_path": tmp.name}
|
| 638 |
+
|
| 639 |
+
|
| 640 |
@app.get("/", response_class=HTMLResponse)
|
| 641 |
async def homepage():
|
| 642 |
html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")
|