Prompt-2-Video / app.py
Shalmoni's picture
Create app.py
0ac3eb4 verified
raw
history blame
7.11 kB
import os, json, uuid
from datetime import datetime
import gradio as gr
# -------- storage helpers --------
ROOT = "outputs"
os.makedirs(ROOT, exist_ok=True)
def now_iso(): return datetime.utcnow().replace(microsecond=0).isoformat()+"Z"
def new_id(): return uuid.uuid4().hex[:8]
def project_dir(pid):
path = os.path.join(ROOT, pid)
os.makedirs(path, exist_ok=True)
os.makedirs(os.path.join(path, "keyframes"), exist_ok=True)
os.makedirs(os.path.join(path, "clips"), exist_ok=True)
return path
def save_project(proj):
pid = proj["meta"]["id"]
path = os.path.join(project_dir(pid), "project.json")
with open(path, "w") as f: json.dump(proj, f, indent=2)
return path
def load_project_file(file_obj):
with open(file_obj.name, "r") as f:
proj = json.load(f)
# ensure directories exist
project_dir(proj["meta"]["id"])
return proj
# -------- model-free stubs for Step 1 --------
def stub_storyboard_from_prompt(prompt, target_shots, default_fps, default_len):
"""Return a minimal storyboard (no LLM yet)."""
shots = []
n = target_shots or 3
for i in range(1, n+1):
shots.append({
"id": i,
"title": f"Shot {i}",
"description": f"Placeholder description for shot {i} derived from: {prompt[:80]}",
"duration": default_len,
"fps": default_fps,
"video_length": default_len,
"steps": 30,
"seed": None,
"negative": "",
"keyframe_path": None
})
return shots
# -------- Gradio app --------
with gr.Blocks() as demo:
gr.Markdown("# 🎬 Storyboard → Keyframes → Videos → Export (Scaffold)")
gr.Markdown("This is the skeleton UI. In the next steps we’ll plug in an open LLM, img2img keyframes, and your Modal video generator.")
# Global state
project = gr.State(None) # dict with meta/shots/clips
current_tab = gr.State("Storyboard")
with gr.Row():
with gr.Column(scale=2):
proj_name = gr.Textbox(label="Project name", placeholder="e.g., Desert Chase")
with gr.Column(scale=1):
new_btn = gr.Button("New Project", variant="primary")
with gr.Column(scale=1):
save_btn = gr.Button("Save Project")
with gr.Column(scale=1):
load_file = gr.File(label="Load Project (project.json)", file_count="single", type="filepath")
load_btn = gr.Button("Load")
# ----- Tabs -----
with gr.Tabs() as tabs:
with gr.Tab("Storyboard"):
gr.Markdown("### 1) Storyboard")
sb_prompt = gr.Textbox(label="High-level prompt", lines=3, placeholder="Describe the story you want to create…")
with gr.Row():
sb_target_shots = gr.Slider(1, 12, value=3, step=1, label="Target # of shots")
sb_default_fps = gr.Slider(8, 60, value=24, step=1, label="Default FPS")
sb_default_len = gr.Slider(1, 12, value=4, step=1, label="Default seconds per shot")
propose_btn = gr.Button("Propose Storyboard (stub)")
shots_json = gr.JSON(label="Storyboard (editable later)")
confirm_btn = gr.Button("Confirm Storyboard ✓", variant="primary")
sb_status = gr.Markdown("")
with gr.Tab("Keyframes"):
gr.Markdown("### 2) Keyframes")
gr.Markdown("In Step 2–3 we’ll add img2img generation and approvals here.")
kf_table = gr.JSON(label="Shots (read-only for now)")
to_videos_btn = gr.Button("Continue to Videos →", interactive=False)
with gr.Tab("Videos"):
gr.Markdown("### 3) Videos (Transitions)")
gr.Markdown("In Step 3–4 we’ll add your Modal start/end-frame video generation here.")
vd_table = gr.JSON(label="Planned clip edges (read-only for now)")
to_export_btn = gr.Button("Continue to Export →", interactive=False)
with gr.Tab("Export"):
gr.Markdown("### 4) Export")
gr.Markdown("Final concatenation & asset packaging will land here in Step 4.")
export_info = gr.Markdown("Nothing to export yet.")
# ----- Handlers -----
def on_new(name):
name = name.strip() or f"Project-{new_id()}"
pid = new_id()
p = {
"meta": {
"id": pid,
"name": name,
"created": now_iso(),
"updated": now_iso()
},
"shots": [],
"clips": []
}
save_project(p)
return p, gr.update(value=f"**New project created** `{name}` (id: `{pid}`)")
new_btn.click(
on_new,
inputs=[proj_name],
outputs=[project, sb_status]
)
def on_propose(p, prompt, target_shots, fps, vlen):
if p is None:
raise gr.Error("Create a project first (New Project).")
shots = stub_storyboard_from_prompt(prompt or "", int(target_shots), int(fps), int(vlen))
p = dict(p)
p["shots"] = shots
p["meta"]["updated"] = now_iso()
save_project(p)
return p, shots, gr.update(value="Storyboard proposed (stub). Edit will be available in Step 2.")
propose_btn.click(
on_propose,
inputs=[project, sb_prompt, sb_target_shots, sb_default_fps, sb_default_len],
outputs=[project, shots_json, sb_status]
)
def on_confirm(p):
if p is None or not p.get("shots"):
raise gr.Error("No storyboard yet.")
# Build planned clip edges (i->i+1)
edges = []
for i in range(len(p["shots"]) - 1):
edges.append({"from": p["shots"][i]["id"], "to": p["shots"][i+1]["id"], "prompt": f"Transition from shot {i+1} to {i+2}"})
p = dict(p)
p["clips"] = edges
p["meta"]["updated"] = now_iso()
save_project(p)
return (
p,
gr.update(value=p["shots"]),
gr.update(value=p["clips"]),
gr.update(value="Storyboard confirmed. Proceed to Keyframes."),
gr.update(interactive=True)
)
confirm_btn.click(
on_confirm,
inputs=[project],
outputs=[project, kf_table, vd_table, sb_status, to_videos_btn]
)
def on_save(p):
if p is None:
raise gr.Error("No project in memory.")
path = save_project(p)
return gr.update(value=f"Saved to `{path}`")
save_btn.click(
on_save,
inputs=[project],
outputs=[sb_status]
)
def on_load(file_obj):
p = load_project_file(file_obj)
return (
p,
gr.update(value=f"Loaded project `{p['meta']['name']}` (id: `{p['meta']['id']}`)"),
gr.update(value=p["shots"]),
gr.update(value=p["clips"]),
gr.update(interactive=bool(p.get("shots")))
)
load_btn.click(
on_load,
inputs=[load_file],
outputs=[project, sb_status, kf_table, vd_table, to_videos_btn]
)
if __name__ == "__main__":
demo.launch()