Spaces:
Running
Running
File size: 6,622 Bytes
33a9d4f 7b7afde 33a9d4f 7b7afde 33a9d4f 7b7afde 33a9d4f | 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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | """
hf_launch.py β starts any of the three apps on Hugging Face behind the admin.gamify.pt gate,
WITHOUT changing the app's own code.
Video Processor β APP_MODULE=app (app.py builds a FastAPI "app")
Thumbnail Editor β APP_MODULE=server (server.py builds a FastAPI "app")
Upload Center β APP_MODULE=app (app.py builds Gradio Blocks and calls .launch())
How:
β’ If the module calls Blocks.launch(), that call is caught (Gradio's own server is NOT started)
and the Blocks are served here instead, with the same theme / css the app passed to launch().
β’ If the module already builds a FastAPI/Starlette app, that app is used as it is.
β’ The GamifyGate (gamify_gate.py) is put in front β unless the app already added it itself.
β’ GAMIFY_GATE=off opens the app to everyone; if gamify_gate.py can't be loaded, everything is
blocked (the app is never opened by accident).
Dockerfile: CMD ["python", "hf_launch.py"]
"""
import importlib
import inspect
import logging
import os
import gradio as gr
logging.basicConfig(level=logging.INFO)
PORT = int(os.environ.get("PORT", 7860))
MODULE = os.environ.get("APP_MODULE", "app")
def _params(fn):
try:
return inspect.signature(fn).parameters
except (TypeError, ValueError):
return {}
# ---- 0) clean pasted Secrets: stray spaces / line breaks / surrounding quotes break API keys ----
def _clean_env():
fixed = []
for name, value in list(os.environ.items()):
clean = value.strip()
if len(clean) >= 2 and clean[0] == clean[-1] and clean[0] in "\"'":
clean = clean[1:-1].strip()
if clean != value:
os.environ[name] = clean
fixed.append(name)
if fixed:
logging.warning(f"hf_launch: removed extra spaces/quotes from: {', '.join(sorted(fixed))}")
_clean_env()
# ---- 1) load the app; catch Blocks.launch() so it doesn't start its own (ungated) server ----
_captured = {}
_original_launch = gr.Blocks.launch
def _capture_launch(self, *args, **kwargs):
_captured["blocks"], _captured["kwargs"] = self, kwargs
logging.info("hf_launch: the app called launch() β it will be served behind the gate instead")
return None
def _import_app_module(name):
"""Imports the app file: <name>.py next to this script β or, if it was uploaded inside a
folder (e.g. upload_center/app.py), finds it there. Explains clearly when it's missing."""
import sys
from pathlib import Path
here = Path(__file__).resolve().parent
name = name.replace("\\", "/").removesuffix(".py")
if "/" in name: # APP_MODULE=upload_center/app
folder, name = name.rsplit("/", 1)
sys.path.insert(0, str(here / folder))
sys.path.insert(0, str(here))
if not (here / f"{name}.py").exists():
found = sorted(p for p in here.rglob(f"{name}.py")
if ".git" not in p.parts and p.resolve() != Path(__file__).resolve())
if found:
logging.warning(f"hf_launch: {name}.py is not next to hf_launch.py β using {found[0].relative_to(here)}")
sys.path.insert(0, str(found[0].parent))
else:
files = sorted(str(p.relative_to(here)) for p in here.rglob("*.py") if ".git" not in p.parts)
raise SystemExit(
f"hf_launch: could not find {name}.py in this Space.\n"
f" Python files found: {', '.join(files) or '(none)'}\n"
f" β Put the app file at the top level of the Space (next to hf_launch.py),\n"
f" or set the variable APP_MODULE to its name (e.g. APP_MODULE=server).")
return importlib.import_module(name)
gr.Blocks.launch = _capture_launch
try:
module = _import_app_module(MODULE)
finally:
gr.Blocks.launch = _original_launch
def _is_asgi_app(obj):
"""A FastAPI / Starlette application (has add_middleware and is callable)."""
return obj is not None and not isinstance(obj, gr.Blocks) and callable(obj) and hasattr(obj, "add_middleware")
# ---- 2) the web app to serve ----
asgi = next((getattr(module, n) for n in ("app", "server", "application") if _is_asgi_app(getattr(module, n, None))),
None)
if asgi is None:
from fastapi import FastAPI
blocks = _captured.get("blocks") or next(
(getattr(module, n) for n in ("app", "demo", "interface") if isinstance(getattr(module, n, None), gr.Blocks)),
None)
if blocks is None:
raise RuntimeError(f"hf_launch: no Gradio Blocks or FastAPI app found in {MODULE}.py")
launch_kwargs = _captured.get("kwargs", {})
mount_params = _params(gr.mount_gradio_app)
extra = {k: v for k, v in launch_kwargs.items() if k in mount_params and k not in ("app", "blocks", "path")}
if "css" in launch_kwargs and "css" not in mount_params and hasattr(blocks, "css"):
blocks.css = launch_kwargs["css"] # older Gradio: css lives on the Blocks
asgi = gr.mount_gradio_app(FastAPI(), blocks, path="/", **extra)
# ---- 3) the access gate ----
def _has_gate(app_):
for m in getattr(app_, "user_middleware", []) or []:
cls = getattr(m, "cls", None)
if getattr(cls, "__name__", "") == "GamifyGate":
return True
return False
class _BlockEverything:
"""Gate wanted but gamify_gate.py missing: nobody gets in."""
def __init__(self, app_):
self.app = app_
async def __call__(self, scope, receive, send):
if scope.get("type") != "http" or scope.get("path") == "/healthz":
return await self.app(scope, receive, send)
await send({"type": "http.response.start", "status": 503,
"headers": [(b"content-type", b"text/plain; charset=utf-8")]})
await send({"type": "http.response.body",
"body": b"Access blocked: gamify_gate.py is missing from this Space."})
if os.environ.get("GAMIFY_GATE", "on").strip().lower() == "off":
logging.warning("hf_launch: GAMIFY_GATE=off β the app is OPEN to everyone")
elif _has_gate(asgi):
logging.info("hf_launch: the app already has the access gate")
else:
try:
from gamify_gate import GamifyGate
asgi.add_middleware(GamifyGate)
logging.info("hf_launch: access gate ON β only through admin.gamify.pt")
except Exception as e:
asgi.add_middleware(_BlockEverything)
logging.error(f"hf_launch: gamify_gate could not be loaded ({e}) β blocking all access")
app = asgi
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=PORT) |