Spaces:
Runtime error
Runtime error
File size: 6,060 Bytes
056b10f 314f511 056b10f 6736809 314f511 6736809 056b10f 6736809 056b10f 6736809 056b10f 6736809 056b10f 6736809 056b10f 6736809 056b10f 6736809 056b10f 6736809 056b10f 6736809 056b10f 314f511 056b10f |
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 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
from __future__ import annotations
import os
from typing import List, Tuple
import gradio as gr
from cinegen import CharacterDesigner, StoryGenerator, VideoDirector
from cinegen.models import Storyboard
STYLE_CHOICES = [
"Cinematic Realism",
"Neo-Noir Animation",
"Analog Horror",
"Retro-Futuristic",
"Dreamlike Documentary",
]
VIDEO_MODEL_CHOICES = [
("Wan 2.1 (fal-ai)", "Wan-AI/Wan2.1-T2V-14B"),
("LTX Video 0.9.7", "Lightricks/LTX-Video-0.9.7-distilled"),
("Hunyuan Video 1.5", "tencent/HunyuanVideo-1.5"),
("CogVideoX 5B", "THUDM/CogVideoX-5b"),
]
SCENE_COLUMNS = ["Scene", "Title", "Action", "Visuals", "Characters", "Duration (s)"]
CHARACTER_COLUMNS = ["ID", "Name", "Role", "Traits"]
def _ensure_storyboard(board: Storyboard | None) -> Storyboard:
if not board:
raise gr.Error("Create a storyboard first.")
return board
def _validate_inputs(idea: str | None, image_path: str | None):
if not idea and not image_path:
raise gr.Error("Provide either a story idea or upload a reference image.")
def handle_storyboard(
idea: str,
inspiration_image: str | None,
style: str,
scene_count: int,
google_api_key: str,
) -> Tuple[str, List[List[str]], List[List[str]], Storyboard]:
_validate_inputs(idea, inspiration_image)
generator = StoryGenerator(api_key=google_api_key or None)
storyboard = generator.generate(
idea=idea,
style=style,
scene_count=scene_count,
inspiration_path=inspiration_image,
)
summary_md = f"### {storyboard.title}\n{storyboard.synopsis}"
scene_rows = storyboard.scenes_table()
character_rows = storyboard.characters_table()
return (
summary_md,
[[row[col] for col in SCENE_COLUMNS] for row in scene_rows],
[[row[col] for col in CHARACTER_COLUMNS] for row in character_rows],
storyboard,
)
def handle_character_design(
storyboard: Storyboard | None,
google_api_key: str,
):
board = _ensure_storyboard(storyboard)
designer = CharacterDesigner(api_key=google_api_key or None)
gallery, updated_board = designer.design(board)
if not gallery:
raise gr.Error("Failed to design characters.")
return gallery, updated_board
def handle_video_render(
storyboard: Storyboard | None,
hf_token: str,
model_choice: str,
):
board = _ensure_storyboard(storyboard)
prioritized_models = [model_choice] + [
model for _, model in VIDEO_MODEL_CHOICES if model != model_choice
]
director = VideoDirector(token=hf_token or None, models=prioritized_models)
final_cut, logs = director.render(board)
log_md = "\n".join(f"- {line}" for line in logs)
return final_cut, log_md
css = """
#cinegen-app {
max-width: 1080px;
margin: 0 auto;
}
"""
with gr.Blocks(css=css, fill_height=True, theme=gr.themes.Soft(), elem_id="cinegen-app") as demo:
gr.Markdown(
"## 🎬 CineGen AI Director\n"
"Drop an idea or inspiration image and let CineGen produce a storyboard, character boards, "
"and a compiled short film using Hugging Face video models."
)
story_state = gr.State()
with gr.Row():
idea_box = gr.Textbox(
label="Movie Idea",
placeholder="E.g. A time loop love story set in a neon bazaar.",
lines=3,
)
inspiration = gr.Image(label="Reference Image (optional)", type="filepath")
with gr.Row():
style_dropdown = gr.Dropdown(
label="Visual Style",
choices=STYLE_CHOICES,
value=STYLE_CHOICES[0],
)
scene_slider = gr.Slider(
label="Scene Count",
minimum=3,
maximum=8,
value=4,
step=1,
)
video_model_dropdown = gr.Dropdown(
label="Preferred Video Model",
choices=[choice for choice, _ in VIDEO_MODEL_CHOICES],
value=VIDEO_MODEL_CHOICES[0][0],
)
with gr.Accordion("API Keys", open=False):
google_key_input = gr.Textbox(
label="Google API Key (Gemini)",
type="password",
placeholder="Required for live Gemini calls. Leave blank to use offline stubs.",
value=os.environ.get("GOOGLE_API_KEY", ""),
)
hf_token_input = gr.Textbox(
label="Hugging Face Token",
type="password",
placeholder="Needed for Wan/LTX/Hunyuan video generation.",
value=os.environ.get("HF_TOKEN", ""),
)
storyboard_btn = gr.Button("Create Storyboard", variant="primary")
summary_md = gr.Markdown("Storyboard output will appear here.")
scenes_df = gr.Dataframe(headers=SCENE_COLUMNS, wrap=True)
characters_df = gr.Dataframe(headers=CHARACTER_COLUMNS, wrap=True)
storyboard_btn.click(
fn=handle_storyboard,
inputs=[idea_box, inspiration, style_dropdown, scene_slider, google_key_input],
outputs=[summary_md, scenes_df, characters_df, story_state],
)
with gr.Row():
design_btn = gr.Button("Design Characters", variant="secondary")
render_btn = gr.Button("Render Short Film", variant="primary")
gallery = gr.Gallery(label="Character References", columns=4, height=320)
render_logs = gr.Markdown(label="Render Log")
final_video = gr.Video(label="CineGen Short Film", interactive=False)
design_btn.click(
fn=handle_character_design,
inputs=[story_state, google_key_input],
outputs=[gallery, story_state],
)
def _model_value(label: str) -> str:
lookup = dict(VIDEO_MODEL_CHOICES)
return lookup.get(label, VIDEO_MODEL_CHOICES[0][1])
def render_wrapper(board, token, label):
return handle_video_render(board, token, _model_value(label))
render_btn.click(
fn=render_wrapper,
inputs=[story_state, hf_token_input, video_model_dropdown],
outputs=[final_video, render_logs],
)
if __name__ == "__main__":
demo.launch()
|