forge-anim / app.py
jkorstad's picture
Update forge-anim from local forge-bricks (with vendored common)
9dd4328 verified
Raw
History Blame Contribute Delete
2.74 kB
"""
Forge-Anim Brick
"""
import gradio as gr
import spaces
# Robust import for local workspace + HF Space deployment (common is vendored on push)
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
for candidate in (HERE.parent, HERE):
if (candidate / "common" / "manifest.py").exists():
sys.path.insert(0, str(candidate))
break
try:
from common.manifest import create_manifest
except ImportError as e:
raise ImportError(
"Failed to import shared 'common' package (manifest.py).\n"
"For local development: run ./install.sh from the forge-bricks/ root.\n"
"For HF Spaces: re-run scripts/push_to_hf.py so it vendors common/."
) from e
import os
from pathlib import Path
from datetime import datetime
@spaces.GPU(duration=30)
def generate_anim(rig_data: str, motion_desc: str) -> dict:
out = Path(os.environ.get("FORGE_BRICKS_OUTPUT", "./outputs/forge_anim"))
out.mkdir(parents=True, exist_ok=True)
ts = int(datetime.now().timestamp())
# Use VLM or simple logic + structured output for animation data
# In production, could call a motion model Space or LLM for keyframe data
anim = {
"clips": ["idle", "walk", "attack"],
"motion_desc": motion_desc,
"rig_ref": rig_data[:100] if rig_data else "",
"keyframe_suggestions": {
"idle": "loop 0-30 frames, subtle breathing",
"walk": "cycle 0-24 frames, foot plant at 0/12",
"attack": "windup 0-10, impact 10-15, recover 15-30"
},
"note": "Structured animation metadata. Apply in Godot/Blender using the rig_data."
}
man = create_manifest(
name="anim",
type="animation",
source_brick="forge-anim",
prompt_or_desc=motion_desc,
files={"anim_data": str(out / f"anim_{ts}.json")},
metadata=anim,
commercial_ok=True
)
(out / f"anim_{ts}.json").write_text(str(anim))
man.save(out / f"manifest_{ts}.json")
return {"anim_data": anim, "manifest": man.to_dict()}
def build_ui():
with gr.Blocks() as d:
gr.Markdown("# Forge-Anim")
rig = gr.Textbox("rig json or description")
motion = gr.Textbox("heroic walk cycle")
btn = gr.Button("Generate Anim")
out = gr.JSON()
btn.click(generate_anim, [rig, motion], out)
return d
# Build at module level so that `demo` (and `gradio_app`) exist when the module is imported
# (required for Hugging Face Spaces and for agent/MCP discovery).
demo = build_ui()
gradio_app = demo # alias for compatibility with tools/skills that expect `gradio_app`
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7867, mcp_server=True)