import gradio as gr import tempfile, os, subprocess, json BLENDER = "blender" # symlinked in Dockerfile def build_prop(spec_file): # Load and sanity-check JSON try: spec_path = spec_file.name with open(spec_path, "r") as f: spec = json.load(f) except Exception as e: return None, f"Invalid JSON: {e}" # Minimal schema checks ptype = spec.get("type") if not ptype: return None, "Spec must include a 'type' field (e.g., 'book')." if "name" not in spec: spec["name"] = f"{ptype}_auto" # Route to generator (extend here with more types later) generator = "generate_book.py" if ptype == "book" else "generate_book.py" out_dir = tempfile.mkdtemp() cmd = [BLENDER, "-b", "-P", generator, "--", "--spec", spec_path, "--out", out_dir] try: run = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) logs = run.stdout except subprocess.CalledProcessError as e: return None, f"Blender failed:\n{e.stdout}" # Return first .glb produced glbs = [os.path.join(out_dir, f) for f in os.listdir(out_dir) if f.lower().endswith(".glb")] if not glbs: return None, f"No .glb exported.\nLogs:\n{logs}" return glbs[0], "Success." demo = gr.Interface( fn=build_prop, inputs=gr.File(label="Upload PropSpec JSON"), outputs=[gr.File(label="Generated .glb"), gr.Textbox(label="Log")], title="Code‑Generated Props (Blender Headless)", description="Upload a JSON PropSpec (supports 'book' for now) and download a .glb.", allow_flagging="never" # prevent write-permission errors on HF Spaces ) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))