File size: 3,001 Bytes
021c59c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""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).
@app.get("/", response_class=HTMLResponse)
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")))