README / app.py
Budi Mulyo
add app.py
eae6d79
Raw History Blame Contribute Delete
7.83 kB
"""
SIXR Prompt‑to‑Prototype Studio – Gradio starter app
====================================================
A lightweight, one‑file scaffold you can push to a **Hugging Face Gradio Space**.
It collects a creative prompt (plus optional reference image & visual style),
then returns placeholder outputs for:
• Loglines (3 variants)
• Mood board (1 placeholder image + 9 themed text cells)
• Shot list (6 items)
• Animatic description (≈4 s)
• Pitch summary
• JSON download bundle
Swap the stub generators with your real AI calls (OpenAI, Replicate, etc.)
when you're ready. Works locally via `python app.py` or `gradio app.py`.
"""
from __future__ import annotations
import datetime as _dt
import json
import uuid
from pathlib import Path
import os
import gradio as gr
from PIL import Image, ImageDraw, ImageFont
# -----------------------------------------------------------------------------
# Config
# -----------------------------------------------------------------------------
STYLES = [
"Default",
"Cyberpunk",
"Film Noir",
"Watercolor",
"Neo‑Deco",
]
PLACEHOLDER_COLOR = (24, 24, 32) # dark bluish‑gray bg
TEXT_COLOR = (220, 220, 230)
TMP_DIR = Path("/tmp")
# It's good practice to load secrets from environment variables.
# On Hugging Face Spaces, you can set these in the repository settings.
ACCESS_TOKEN = os.environ.get("HF_TOKEN") # Or whatever you named your secret
# -----------------------------------------------------------------------------
# Helper functions
# -----------------------------------------------------------------------------
def _make_placeholder_image(text: str = "Moodboard", size: tuple[int, int] = (512, 512)) -> Image.Image: # noqa: E501
"""Generate a simple placeholder PIL image with centered text."""
img = Image.new("RGB", size, color=PLACEHOLDER_COLOR)
draw = ImageDraw.Draw(img)
try:
font = ImageFont.truetype("arial.ttf", 32)
except Exception: # font might be missing in container – fall back
font = ImageFont.load_default()
w, h = draw.textsize(text, font=font)
draw.text(((size[0] - w) / 2, (size[1] - h) / 2), text, fill=TEXT_COLOR, font=font)
return img
def _timestamp() -> str:
"""Return an ISO‑8601 UTC timestamp (second precision)."""
return _dt.datetime.utcnow().isoformat(timespec="seconds") + "Z"
def read_readme():
with open("README.md", "r") as f:
return f.read()
# -----------------------------------------------------------------------------
# Core generator (stub – replace with your own AI calls)
# -----------------------------------------------------------------------------
def generate_prototype(prompt: str, reference_image: Image.Image | None, style: str):
"""Return all UI outputs plus a path to the JSON bundle file."""
prompt = (prompt or "").strip()
if not prompt:
raise gr.Error("Please enter a creative prompt first! ☝️")
# --- Loglines ------------------------------------------------------------
loglines = [
f"{prompt.capitalize()} – through the lens of {style.lower()} (take {i})."
for i in range(1, 4)
]
# --- Moodboard -----------------------------------------------------------
moodboard_img = _make_placeholder_image(f"{style} moodboard")
moodboard_words = [f"{style} motif #{i}" for i in range(1, 10)]
# --- Shot list -----------------------------------------------------------
framings = [
"Close‑up",
"Medium shot",
"Wide shot",
"Low‑angle",
"Over‑the‑shoulder",
"Drone reveal",
]
movements = [
"push‑in",
"pull‑out",
"pan‑left",
"tilt‑up",
"handheld",
"steadycam glide",
]
shot_list = [f"{framings[i]} with a {movements[i]}" for i in range(len(framings))]
# --- Animatic ------------------------------------------------------------
animatic = (
"4‑second sequence: stars dissolve into silhouette; camera drifts forward, "
"title flares in neon before cutting to black."
)
# --- Pitch summary -------------------------------------------------------
pitch = (
f"An immersive concept blending {style.lower()} aesthetics with audience agency "
f"around the theme: ‘{prompt}'."
)
# --- Bundle everything into a JSON dict ---------------------------------
bundle = {
"prompt": prompt,
"style": style,
"generated_at": _timestamp(),
"loglines": loglines,
"moodboard": {
"words": moodboard_words,
"image_placeholder": "(placeholder – no image bytes)",
},
"shot_list": shot_list,
"animatic": animatic,
"pitch_summary": pitch,
}
json_path = TMP_DIR / f"prototype_{uuid.uuid4().hex}.json"
json_path.write_text(json.dumps(bundle, indent=2), encoding="utf-8")
# Return tuple aligned with UI outputs order
return (
loglines,
moodboard_img,
"\n".join(moodboard_words),
"\n".join(shot_list),
animatic,
pitch,
str(json_path),
)
# -----------------------------------------------------------------------------
# Gradio interface definition
# -----------------------------------------------------------------------------
def build_ui() -> gr.Blocks:
with gr.Blocks(title="SIXR Prompt‑to‑Prototype Studio") as demo:
gr.Markdown(read_readme())
if ACCESS_TOKEN:
gr.Markdown("Your access token is configured!")
else:
gr.Markdown("Access token not found. Set it in your Space secrets.")
# --------------------------- Inputs ----------------------------------
with gr.Row():
prompt_in = gr.Textbox(
label="Creative prompt",
placeholder="Describe your immersive idea…",
lines=3,
)
image_in = gr.Image(
label="Reference image (optional)",
type="pil",
image_mode="RGB",
)
style_in = gr.Dropdown(STYLES, value=STYLES[0], label="Visual style")
generate_btn = gr.Button("Generate prototype", variant="primary")
# --------------------------- Outputs ---------------------------------
with gr.Tabs():
with gr.Tab("Loglines"):
loglines_out = gr.JSON(label="Three logline variants")
with gr.Tab("Moodboard"):
mood_img_out = gr.Image(label="Moodboard image")
mood_txt_out = gr.Textbox(label="Moodboard keywords", lines=10)
with gr.Tab("Shot list"):
shots_out = gr.Textbox(label="Shot list", lines=8)
with gr.Tab("Animatic"):
anim_out = gr.Textbox(label="Animatic description")
with gr.Tab("Pitch summary"):
pitch_out = gr.Textbox(label="Pitch summary")
json_file_out = gr.File(label="Download JSON bundle", interactive=False)
# Callback wiring
generate_btn.click(
fn=generate_prototype,
inputs=[prompt_in, image_in, style_in],
outputs=[
loglines_out,
mood_img_out,
mood_txt_out,
shots_out,
anim_out,
pitch_out,
json_file_out,
],
)
return demo
# -----------------------------------------------------------------------------
# Main entrypoint
# -----------------------------------------------------------------------------
if __name__ == "__main__":
ui = build_ui()
# In Spaces, host/port are auto‑set; defaults work fine.
ui.launch()