Shalmoni commited on
Commit
0ac3eb4
·
verified ·
1 Parent(s): ee1ebb9

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +196 -0
app.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, json, uuid
2
+ from datetime import datetime
3
+ import gradio as gr
4
+
5
+ # -------- storage helpers --------
6
+ ROOT = "outputs"
7
+ os.makedirs(ROOT, exist_ok=True)
8
+
9
+ def now_iso(): return datetime.utcnow().replace(microsecond=0).isoformat()+"Z"
10
+ def new_id(): return uuid.uuid4().hex[:8]
11
+
12
+ def project_dir(pid):
13
+ path = os.path.join(ROOT, pid)
14
+ os.makedirs(path, exist_ok=True)
15
+ os.makedirs(os.path.join(path, "keyframes"), exist_ok=True)
16
+ os.makedirs(os.path.join(path, "clips"), exist_ok=True)
17
+ return path
18
+
19
+ def save_project(proj):
20
+ pid = proj["meta"]["id"]
21
+ path = os.path.join(project_dir(pid), "project.json")
22
+ with open(path, "w") as f: json.dump(proj, f, indent=2)
23
+ return path
24
+
25
+ def load_project_file(file_obj):
26
+ with open(file_obj.name, "r") as f:
27
+ proj = json.load(f)
28
+ # ensure directories exist
29
+ project_dir(proj["meta"]["id"])
30
+ return proj
31
+
32
+ # -------- model-free stubs for Step 1 --------
33
+ def stub_storyboard_from_prompt(prompt, target_shots, default_fps, default_len):
34
+ """Return a minimal storyboard (no LLM yet)."""
35
+ shots = []
36
+ n = target_shots or 3
37
+ for i in range(1, n+1):
38
+ shots.append({
39
+ "id": i,
40
+ "title": f"Shot {i}",
41
+ "description": f"Placeholder description for shot {i} derived from: {prompt[:80]}",
42
+ "duration": default_len,
43
+ "fps": default_fps,
44
+ "video_length": default_len,
45
+ "steps": 30,
46
+ "seed": None,
47
+ "negative": "",
48
+ "keyframe_path": None
49
+ })
50
+ return shots
51
+
52
+ # -------- Gradio app --------
53
+ with gr.Blocks() as demo:
54
+ gr.Markdown("# 🎬 Storyboard → Keyframes → Videos → Export (Scaffold)")
55
+ 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.")
56
+
57
+ # Global state
58
+ project = gr.State(None) # dict with meta/shots/clips
59
+ current_tab = gr.State("Storyboard")
60
+
61
+ with gr.Row():
62
+ with gr.Column(scale=2):
63
+ proj_name = gr.Textbox(label="Project name", placeholder="e.g., Desert Chase")
64
+ with gr.Column(scale=1):
65
+ new_btn = gr.Button("New Project", variant="primary")
66
+ with gr.Column(scale=1):
67
+ save_btn = gr.Button("Save Project")
68
+ with gr.Column(scale=1):
69
+ load_file = gr.File(label="Load Project (project.json)", file_count="single", type="filepath")
70
+ load_btn = gr.Button("Load")
71
+
72
+ # ----- Tabs -----
73
+ with gr.Tabs() as tabs:
74
+ with gr.Tab("Storyboard"):
75
+ gr.Markdown("### 1) Storyboard")
76
+ sb_prompt = gr.Textbox(label="High-level prompt", lines=3, placeholder="Describe the story you want to create…")
77
+ with gr.Row():
78
+ sb_target_shots = gr.Slider(1, 12, value=3, step=1, label="Target # of shots")
79
+ sb_default_fps = gr.Slider(8, 60, value=24, step=1, label="Default FPS")
80
+ sb_default_len = gr.Slider(1, 12, value=4, step=1, label="Default seconds per shot")
81
+ propose_btn = gr.Button("Propose Storyboard (stub)")
82
+ shots_json = gr.JSON(label="Storyboard (editable later)")
83
+ confirm_btn = gr.Button("Confirm Storyboard ✓", variant="primary")
84
+ sb_status = gr.Markdown("")
85
+
86
+ with gr.Tab("Keyframes"):
87
+ gr.Markdown("### 2) Keyframes")
88
+ gr.Markdown("In Step 2–3 we’ll add img2img generation and approvals here.")
89
+ kf_table = gr.JSON(label="Shots (read-only for now)")
90
+ to_videos_btn = gr.Button("Continue to Videos →", interactive=False)
91
+
92
+ with gr.Tab("Videos"):
93
+ gr.Markdown("### 3) Videos (Transitions)")
94
+ gr.Markdown("In Step 3–4 we’ll add your Modal start/end-frame video generation here.")
95
+ vd_table = gr.JSON(label="Planned clip edges (read-only for now)")
96
+ to_export_btn = gr.Button("Continue to Export →", interactive=False)
97
+
98
+ with gr.Tab("Export"):
99
+ gr.Markdown("### 4) Export")
100
+ gr.Markdown("Final concatenation & asset packaging will land here in Step 4.")
101
+ export_info = gr.Markdown("Nothing to export yet.")
102
+
103
+ # ----- Handlers -----
104
+ def on_new(name):
105
+ name = name.strip() or f"Project-{new_id()}"
106
+ pid = new_id()
107
+ p = {
108
+ "meta": {
109
+ "id": pid,
110
+ "name": name,
111
+ "created": now_iso(),
112
+ "updated": now_iso()
113
+ },
114
+ "shots": [],
115
+ "clips": []
116
+ }
117
+ save_project(p)
118
+ return p, gr.update(value=f"**New project created** `{name}` (id: `{pid}`)")
119
+
120
+ new_btn.click(
121
+ on_new,
122
+ inputs=[proj_name],
123
+ outputs=[project, sb_status]
124
+ )
125
+
126
+ def on_propose(p, prompt, target_shots, fps, vlen):
127
+ if p is None:
128
+ raise gr.Error("Create a project first (New Project).")
129
+ shots = stub_storyboard_from_prompt(prompt or "", int(target_shots), int(fps), int(vlen))
130
+ p = dict(p)
131
+ p["shots"] = shots
132
+ p["meta"]["updated"] = now_iso()
133
+ save_project(p)
134
+ return p, shots, gr.update(value="Storyboard proposed (stub). Edit will be available in Step 2.")
135
+
136
+ propose_btn.click(
137
+ on_propose,
138
+ inputs=[project, sb_prompt, sb_target_shots, sb_default_fps, sb_default_len],
139
+ outputs=[project, shots_json, sb_status]
140
+ )
141
+
142
+ def on_confirm(p):
143
+ if p is None or not p.get("shots"):
144
+ raise gr.Error("No storyboard yet.")
145
+ # Build planned clip edges (i->i+1)
146
+ edges = []
147
+ for i in range(len(p["shots"]) - 1):
148
+ edges.append({"from": p["shots"][i]["id"], "to": p["shots"][i+1]["id"], "prompt": f"Transition from shot {i+1} to {i+2}"})
149
+ p = dict(p)
150
+ p["clips"] = edges
151
+ p["meta"]["updated"] = now_iso()
152
+ save_project(p)
153
+ return (
154
+ p,
155
+ gr.update(value=p["shots"]),
156
+ gr.update(value=p["clips"]),
157
+ gr.update(value="Storyboard confirmed. Proceed to Keyframes."),
158
+ gr.update(interactive=True)
159
+ )
160
+
161
+ confirm_btn.click(
162
+ on_confirm,
163
+ inputs=[project],
164
+ outputs=[project, kf_table, vd_table, sb_status, to_videos_btn]
165
+ )
166
+
167
+ def on_save(p):
168
+ if p is None:
169
+ raise gr.Error("No project in memory.")
170
+ path = save_project(p)
171
+ return gr.update(value=f"Saved to `{path}`")
172
+
173
+ save_btn.click(
174
+ on_save,
175
+ inputs=[project],
176
+ outputs=[sb_status]
177
+ )
178
+
179
+ def on_load(file_obj):
180
+ p = load_project_file(file_obj)
181
+ return (
182
+ p,
183
+ gr.update(value=f"Loaded project `{p['meta']['name']}` (id: `{p['meta']['id']}`)"),
184
+ gr.update(value=p["shots"]),
185
+ gr.update(value=p["clips"]),
186
+ gr.update(interactive=bool(p.get("shots")))
187
+ )
188
+
189
+ load_btn.click(
190
+ on_load,
191
+ inputs=[load_file],
192
+ outputs=[project, sb_status, kf_table, vd_table, to_videos_btn]
193
+ )
194
+
195
+ if __name__ == "__main__":
196
+ demo.launch()