| """Small Talk — Hugging Face Space entrypoint. |
| |
| A Gradio app that wears no Gradio: we use `gradio.Server` (a FastAPI server with |
| Gradio's backend) purely as the host, then serve our own three.js meeting UI and |
| the /api control plane on it. Custom routes take priority over Gradio's, so the |
| visitor only ever sees the custom frontend — which also earns the hackathon's |
| "Off-Brand" badge. |
| |
| LiveKit (the WebRTC SFU) is LiveKit Cloud; credentials come from Space secrets |
| (LIVEKIT_URL / LIVEKIT_API_KEY / LIVEKIT_API_SECRET). The browser connects to |
| LiveKit Cloud directly; this app just mints tokens and runs the Reachy |
| publishers that stream the pre-rendered voice clips into the room. |
| """ |
| import os |
| import pathlib |
|
|
| import gradio as gr |
| from fastapi.responses import FileResponse |
|
|
| from backend.server import attach |
|
|
| DIST = pathlib.Path(__file__).parent / "frontend" / "dist" |
|
|
| app = gr.Server() |
| attach(app) |
|
|
| |
| |
| from fastapi.staticfiles import StaticFiles |
|
|
| class CachedStaticFiles(StaticFiles): |
| """Hashed/immutable build artifacts: cache hard, forever.""" |
|
|
| def file_response(self, *args, **kwargs): |
| resp = super().file_response(*args, **kwargs) |
| resp.headers["Cache-Control"] = "public, max-age=31536000, immutable" |
| return resp |
|
|
|
|
| for sub in ("assets", "robot-3d", "moves", "props"): |
| d = DIST / sub |
| if d.is_dir(): |
| cls = CachedStaticFiles if sub == "assets" else StaticFiles |
| app.mount(f"/{sub}", cls(directory=str(d)), name=sub) |
|
|
| class RadioStaticFiles(StaticFiles): |
| """Songs/art are immutable (cache hard); playlist.json must always revalidate |
| so updated lyrics/timings are never served stale.""" |
|
|
| def file_response(self, *args, **kwargs): |
| resp = super().file_response(*args, **kwargs) |
| full_path = str(args[0]) if args else "" |
| resp.headers["Cache-Control"] = ( |
| "no-cache, must-revalidate" if full_path.endswith(".json") |
| else "public, max-age=604800") |
| return resp |
|
|
|
|
| |
| RADIO = pathlib.Path(__file__).parent / "radio" |
| if RADIO.is_dir(): |
| app.mount("/radio", RadioStaticFiles(directory=str(RADIO)), name="radio") |
|
|
|
|
| |
| |
| _NO_CACHE = {"Cache-Control": "no-cache, must-revalidate"} |
|
|
|
|
| @app.get("/") |
| async def index(): |
| return FileResponse(DIST / "index.html", headers=_NO_CACHE) |
|
|
|
|
| @app.get("/favicon.ico") |
| async def favicon(): |
| return FileResponse(DIST / "index.html", headers=_NO_CACHE) |
|
|
|
|
| if __name__ == "__main__": |
| |
| port = int(os.environ.get("GRADIO_SERVER_PORT", os.environ.get("PORT", 7860))) |
| app.launch(server_name="0.0.0.0", server_port=port) |
|
|