Spaces:
Sleeping
Sleeping
| """HF Spaces entrypoint for NIGHTWAVE (shipped via the Docker SDK). | |
| ================================================================================ | |
| HOW THIS IS SERVED (Docker SDK -> `uvicorn app:app`) | |
| -------------------------------------------------------------------------------- | |
| The Space runs `uvicorn app:app --host 0.0.0.0 --port 7860` (see ../space/Dockerfile | |
| and the README front-matter: `sdk: docker`, `app_port: 7860`). That makes the | |
| combined FastAPI ASGI `app` the single, deterministic server -- no guessing about | |
| which object an HF gradio-launcher would pick up. | |
| The module-level `app` is built FastAPI-FIRST and is ALWAYS functional: | |
| * GET / -> the raw skeuomorphic radio document (top-level, so mic | |
| + autoplay permissions work without extra iframe nesting) | |
| * POST /api/broadcast, /api/call, /api/seek -> same-origin proxy to Modal | |
| (browser never sees Modal creds; graceful canned | |
| fallback so the radio never goes silent) | |
| A minimal Gradio Blocks (the iframe-wrapped radio) is then mounted at /gradio so | |
| the Space is a bona-fide Gradio app (Off-Brand: a custom frontend past the default | |
| Gradio look). The mount is OPPORTUNISTIC: if gradio import/mount fails for any | |
| reason, the radio + /api/* still serve -- a gradio hiccup can never take the | |
| product down. This is the deliberate <24h ship-safety choice. | |
| ================================================================================ | |
| """ | |
| import logging | |
| import os | |
| from fastapi.responses import HTMLResponse | |
| from server import create_api, radio_iframe_html, read_radio_html | |
| _log = logging.getLogger("nightwave") | |
| # 1) FastAPI app with the same-origin proxy routes. This ALWAYS works (no gradio). | |
| app = create_api() | |
| # Serve the radio at the root as the RAW document (top-level mic/autoplay). | |
| def root_radio() -> HTMLResponse: | |
| return HTMLResponse(read_radio_html()) | |
| # 2) Opportunistically mount a minimal Gradio Blocks at /gradio so the Space is a | |
| # genuine Gradio app. Never let a gradio problem break the product. | |
| demo = None | |
| try: | |
| import gradio as gr | |
| with gr.Blocks( | |
| title="NIGHTWAVE", | |
| theme=gr.themes.Base(primary_hue="indigo", neutral_hue="slate"), | |
| css="footer{display:none !important} .gradio-container{max-width:100% !important}", | |
| ) as demo: | |
| gr.HTML(radio_iframe_html()) | |
| app = gr.mount_gradio_app(app, demo, path="/gradio") | |
| _log.info("Gradio mounted at /gradio") | |
| except Exception as exc: # pragma: no cover - defensive: never break serving | |
| _log.warning("Gradio mount skipped (%r); serving radio + /api/* without it", exc) | |
| if __name__ == "__main__": | |
| # Runs locally too: with gradio absent (local Py3.9) the try/except above just | |
| # skips the mount and we still serve the radio + /api/* exactly as on HF. | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "7860"))) | |