Update app.py
Browse files
app.py
CHANGED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
+
import streamlit as st
|
| 3 |
+
from core.gemini_handler import GeminiHandler
|
| 4 |
+
from core.visual_engine import VisualEngine
|
| 5 |
+
from core.prompt_engineering import create_story_breakdown_prompt, create_image_prompt_from_scene
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
# --- Configuration & Initialization ---
|
| 9 |
+
st.set_page_config(page_title="CineGen AI", layout="wide")
|
| 10 |
+
|
| 11 |
+
# Load API Key from Streamlit secrets
|
| 12 |
+
try:
|
| 13 |
+
GEMINI_API_KEY = st.secrets["GEMINI_API_KEY"]
|
| 14 |
+
except KeyError:
|
| 15 |
+
st.error("GEMINI_API_KEY not found in secrets. Please add it to your Streamlit Cloud secrets.")
|
| 16 |
+
st.stop()
|
| 17 |
+
|
| 18 |
+
gemini = GeminiHandler(api_key=GEMINI_API_KEY)
|
| 19 |
+
visualizer = VisualEngine(output_dir="temp_generated_media") # Use a temp dir for HF Spaces
|
| 20 |
+
|
| 21 |
+
# --- UI Elements ---
|
| 22 |
+
st.title("π¬ CineGen AI: Your Pocket AI Film Studio")
|
| 23 |
+
st.markdown("Let's turn your ideas into cinematic visuals!")
|
| 24 |
+
|
| 25 |
+
with st.sidebar:
|
| 26 |
+
st.header("π¨ Creative Controls")
|
| 27 |
+
user_idea = st.text_area("Enter your core idea/story prompt:", "A lone astronaut discovers a glowing alien artifact on Mars, a sense of wonder and slight dread.", height=100)
|
| 28 |
+
genre = st.selectbox("Select Genre:", ["Sci-Fi", "Fantasy", "Thriller", "Drama", "Comedy"], index=0)
|
| 29 |
+
mood = st.selectbox("Select Mood:", ["Suspenseful", "Mysterious", "Epic", "Uplifting", "Dark"], index=0)
|
| 30 |
+
generate_button = st.button("β¨ Generate Cinematic Concept", type="primary")
|
| 31 |
+
|
| 32 |
+
# --- Session State for Storing Results ---
|
| 33 |
+
if 'story_scenes' not in st.session_state:
|
| 34 |
+
st.session_state.story_scenes = None
|
| 35 |
+
if 'image_prompts' not in st.session_state:
|
| 36 |
+
st.session_state.image_prompts = []
|
| 37 |
+
if 'generated_images_paths' not in st.session_state:
|
| 38 |
+
st.session_state.generated_images_paths = []
|
| 39 |
+
if 'video_path' not in st.session_state:
|
| 40 |
+
st.session_state.video_path = None
|
| 41 |
+
|
| 42 |
+
# --- Main Logic ---
|
| 43 |
+
if generate_button and user_idea:
|
| 44 |
+
st.session_state.story_scenes = None # Reset previous results
|
| 45 |
+
st.session_state.image_prompts = []
|
| 46 |
+
st.session_state.generated_images_paths = []
|
| 47 |
+
st.session_state.video_path = None
|
| 48 |
+
|
| 49 |
+
with st.spinner("Phase 1: Gemini is drafting the script and scene breakdown... π"):
|
| 50 |
+
story_prompt = create_story_breakdown_prompt(user_idea, genre, mood)
|
| 51 |
+
st.session_state.story_scenes = gemini.generate_story_breakdown(story_prompt)
|
| 52 |
+
|
| 53 |
+
if st.session_state.story_scenes:
|
| 54 |
+
st.toast("Script and scene breakdown complete!", icon="β
")
|
| 55 |
+
|
| 56 |
+
with st.spinner("Phase 2: Gemini is crafting visual prompts for keyframes... πΌοΈ"):
|
| 57 |
+
for i, scene in enumerate(st.session_state.story_scenes):
|
| 58 |
+
scene_desc_for_image = f"Scene {scene.get('scene_number', i+1)}: {scene.get('key_action', 'N/A')}. Setting: {scene.get('setting_description', 'N/A')}"
|
| 59 |
+
img_gen_prompt_text = create_image_prompt_from_scene(scene_desc_for_image, scene.get('visual_style_suggestion', 'cinematic'))
|
| 60 |
+
|
| 61 |
+
# Get the actual image description from Gemini
|
| 62 |
+
# For this demo, we'll use the scene's key_action and setting as the "description" for the placeholder
|
| 63 |
+
# In a real app, Gemini would generate a detailed image prompt here.
|
| 64 |
+
# image_description_from_gemini = gemini.generate_image_prompt(img_gen_prompt_text)
|
| 65 |
+
# For simplicity, we use parts of the scene data directly for the placeholder image text
|
| 66 |
+
image_description_for_placeholder = f"Scene {scene.get('scene_number', i+1)}: {scene.get('key_action', 'N/A')}\nStyle: {scene.get('visual_style_suggestion', 'cinematic')}"
|
| 67 |
+
|
| 68 |
+
if image_description_for_placeholder: # was image_description_from_gemini
|
| 69 |
+
st.session_state.image_prompts.append(image_description_for_placeholder)
|
| 70 |
+
# Simulate image generation by creating a placeholder
|
| 71 |
+
img_path = visualizer.create_placeholder_image(
|
| 72 |
+
image_description_for_placeholder, # use the generated desc
|
| 73 |
+
f"scene_{scene.get('scene_number', i+1)}_placeholder.png"
|
| 74 |
+
)
|
| 75 |
+
st.session_state.generated_images_paths.append(img_path)
|
| 76 |
+
else:
|
| 77 |
+
st.warning(f"Could not generate image prompt/description for Scene {scene.get('scene_number', i+1)}.")
|
| 78 |
+
st.toast("Visual prompts and placeholder images generated!", icon="πΌοΈ")
|
| 79 |
+
|
| 80 |
+
if st.session_state.generated_images_paths:
|
| 81 |
+
with st.spinner("Phase 3: Assembling the cinematic video... ποΈ"):
|
| 82 |
+
st.session_state.video_path = visualizer.create_video_from_images(
|
| 83 |
+
st.session_state.generated_images_paths,
|
| 84 |
+
output_filename="cinegen_output.mp4",
|
| 85 |
+
duration_per_image=3 # Show each image for 3 seconds
|
| 86 |
+
)
|
| 87 |
+
st.toast("Video assembled!", icon="π¬")
|
| 88 |
+
st.balloons()
|
| 89 |
+
else:
|
| 90 |
+
st.error("No images were generated, cannot create video.")
|
| 91 |
+
else:
|
| 92 |
+
st.error("Failed to generate story breakdown from Gemini. Please check logs or try a different prompt.")
|
| 93 |
+
|
| 94 |
+
# --- Display Results ---
|
| 95 |
+
if st.session_state.story_scenes:
|
| 96 |
+
st.header("π Generated Story Breakdown")
|
| 97 |
+
for i, scene in enumerate(st.session_state.story_scenes):
|
| 98 |
+
with st.expander(f"Scene {scene.get('scene_number', i+1)}: {scene.get('key_action', 'N/A')[:50]}...", expanded=i==0):
|
| 99 |
+
st.markdown(f"**Setting:** {scene.get('setting_description', 'N/A')}")
|
| 100 |
+
st.markdown(f"**Characters:** {', '.join(scene.get('characters_involved', []))}")
|
| 101 |
+
st.markdown(f"**Key Action:** {scene.get('key_action', 'N/A')}")
|
| 102 |
+
st.markdown(f"**Visual Style Suggestion:** {scene.get('visual_style_suggestion', 'N/A')}")
|
| 103 |
+
st.markdown(f"**Camera Angle Suggestion:** {scene.get('camera_angle_suggestion', 'N/A')}")
|
| 104 |
+
|
| 105 |
+
if i < len(st.session_state.image_prompts):
|
| 106 |
+
st.markdown(f"**Image Concept (Placeholder Text):**")
|
| 107 |
+
st.code(st.session_state.image_prompts[i])
|
| 108 |
+
if i < len(st.session_state.generated_images_paths):
|
| 109 |
+
st.image(st.session_state.generated_images_paths[i], caption=f"Visual Concept for Scene {scene.get('scene_number', i+1)}")
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
if st.session_state.video_path and os.path.exists(st.session_state.video_path):
|
| 113 |
+
st.header("π¬ Generated Cinematic Concept Video")
|
| 114 |
+
video_file = open(st.session_state.video_path, 'rb')
|
| 115 |
+
video_bytes = video_file.read()
|
| 116 |
+
st.video(video_bytes)
|
| 117 |
+
st.markdown(f"Download your video: [{os.path.basename(st.session_state.video_path)}]({st.session_state.video_path})") # This download might not work directly on Spaces due to pathing, but video will play.
|
| 118 |
+
# For download on Spaces, you might need to serve it differently or provide a direct link if Spaces keeps it.
|
| 119 |
+
# A simpler way for Spaces is to just let the user right-click the video player and save.
|
| 120 |
+
else:
|
| 121 |
+
if generate_button and not st.session_state.video_path and st.session_state.generated_images_paths:
|
| 122 |
+
st.warning("Video generation might have failed or no images were available.")
|
| 123 |
+
elif not generate_button :
|
| 124 |
+
st.info("Enter your idea and click 'Generate Cinematic Concept' to begin!")
|