mgbam commited on
Commit
cd569d8
Β·
verified Β·
1 Parent(s): 8583908

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -45
app.py CHANGED
@@ -1,7 +1,7 @@
1
  # app.py
2
  import streamlit as st
3
  from core.gemini_handler import GeminiHandler
4
- from core.visual_engine import VisualEngine # Your updated VisualEngine with DALL-E prep
5
  from core.prompt_engineering import (
6
  create_story_breakdown_prompt,
7
  create_image_prompt_from_scene_data,
@@ -39,20 +39,23 @@ if 'visual_engine' not in st.session_state:
39
  # --- TRY TO SET OPENAI API KEY FOR VISUAL ENGINE ---
40
  openai_key_from_secrets = None
41
  try:
 
42
  if "OPENAI_API_KEY" in st.secrets:
43
  openai_key_from_secrets = st.secrets["OPENAI_API_KEY"]
 
 
44
  except Exception as e:
45
- print(f"Could not access st.secrets for OPENAI_API_KEY: {e}") # Log, don't stop app
46
 
47
  if not openai_key_from_secrets and "OPENAI_API_KEY" in os.environ: # Fallback to env var
48
  openai_key_from_secrets = os.environ["OPENAI_API_KEY"]
49
 
50
  if openai_key_from_secrets:
51
  st.session_state.visual_engine.set_openai_api_key(openai_key_from_secrets)
52
- # st.sidebar.info("OpenAI API Key loaded. DALL-E generation enabled.") # Optional UI feedback
53
  else:
54
  st.session_state.visual_engine.set_openai_api_key(None)
55
- st.sidebar.warning("OpenAI API Key for DALL-E not found. Visuals will be placeholders.")
56
  # --- END OF OPENAI API KEY SETUP ---
57
 
58
  # Story and generated content state
@@ -78,22 +81,21 @@ def initialize_new_story():
78
 
79
  def generate_visual_for_scene_wrapper(scene_index, scene_data, is_regeneration=False, version_count=1):
80
  scene_num_for_log = scene_data.get('scene_number', scene_index + 1)
81
- # This st.info will appear briefly in the main app area below the "Generate" button.
82
- # Consider moving detailed progress to sidebar or using st.status.
83
- # st.info(f"Processing visual concept for Scene {scene_num_for_log} (v{version_count})...")
84
 
85
  textual_image_prompt = ""
86
- if is_regeneration and scene_index < len(st.session_state.scene_image_prompts):
87
  textual_image_prompt = st.session_state.scene_image_prompts[scene_index]
 
88
  else:
89
  textual_image_prompt = create_image_prompt_from_scene_data(
90
  scene_data,
91
  st.session_state.character_definitions,
92
  st.session_state.style_reference_description
93
  )
 
94
 
95
  if not textual_image_prompt:
96
- st.error(f"Failed to create/retrieve textual image prompt for Scene {scene_num_for_log}.")
97
  return False
98
 
99
  if scene_index >= len(st.session_state.scene_image_prompts):
@@ -103,21 +105,18 @@ def generate_visual_for_scene_wrapper(scene_index, scene_data, is_regeneration=F
103
 
104
  image_filename = f"scene_{scene_num_for_log}_visual_v{version_count}.png"
105
 
106
- # The actual generation (AI or placeholder) happens here
107
  generated_image_path = st.session_state.visual_engine.generate_image_visual(
108
- textual_image_prompt,
109
- image_filename
110
  )
111
 
112
  while len(st.session_state.generated_images_paths) <= scene_index:
113
  st.session_state.generated_images_paths.append(None)
114
 
115
  if generated_image_path and os.path.exists(generated_image_path):
116
- # st.success(f"Visual for Scene {scene_num_for_log} (v{version_count}) ready.") # Can be noisy
117
  st.session_state.generated_images_paths[scene_index] = generated_image_path
118
  return True
119
  else:
120
- st.warning(f"Visual generation failed or path invalid for Scene {scene_num_for_log} (v{version_count}).")
121
  st.session_state.generated_images_paths[scene_index] = None
122
  return False
123
 
@@ -125,10 +124,10 @@ def generate_visual_for_scene_wrapper(scene_index, scene_data, is_regeneration=F
125
  with st.sidebar:
126
  st.title("🎬 CineGen AI Pro")
127
  st.markdown("### Creative Controls")
128
- user_idea = st.text_area("Enter your core story idea:", "A detective in a cyberpunk city investigates a rogue AI that believes it's the next step in evolution.", height=100, key="user_idea_input")
129
- genre = st.selectbox("Genre:", ["Sci-Fi", "Fantasy", "Noir", "Thriller", "Drama", "Horror"], index=2, key="genre_select")
130
- mood = st.selectbox("Mood:", ["Suspenseful", "Mysterious", "Gritty", "Epic", "Dark", "Hopeful"], index=2, key="mood_select")
131
- num_scenes_val = st.slider("Number of Scenes:", 1, 5, 3, key="num_scenes_slider")
132
 
133
  if st.button("✨ Generate Full Story Concept", type="primary", key="generate_full_story_btn", use_container_width=True):
134
  initialize_new_story()
@@ -140,51 +139,55 @@ with st.sidebar:
140
  story_prompt_text = create_story_breakdown_prompt(user_idea, genre, mood, num_scenes_val)
141
  try:
142
  st.session_state.story_scenes = st.session_state.gemini_handler.generate_story_breakdown(story_prompt_text)
143
- status_main.update(label="Script breakdown complete! βœ…", state="running")
144
 
145
  num_actual_scenes = len(st.session_state.story_scenes)
146
  st.session_state.scene_image_prompts = [""] * num_actual_scenes
147
  st.session_state.generated_images_paths = [None] * num_actual_scenes
148
 
149
  except Exception as e:
150
- status_main.update(label=f"Failed to generate story breakdown: {e}", state="error")
151
- st.session_state.story_scenes = []
152
- st.stop() # Stop further execution if script fails
153
 
154
  if st.session_state.story_scenes:
155
- st.write("Phase 2: Generating initial visual concepts... πŸ–ΌοΈ")
156
  success_count = 0
157
  for i_loop, scene_data_loop_var in enumerate(st.session_state.story_scenes):
158
- st.write(f"Generating visual for Scene {scene_data_loop_var.get('scene_number', i_loop + 1)}...")
 
159
  if generate_visual_for_scene_wrapper(i_loop, scene_data_loop_var, version_count=1):
160
  success_count +=1
 
 
 
161
 
162
  if success_count == len(st.session_state.story_scenes):
163
  status_main.update(label="All concepts generated successfully! πŸŽ‰", state="complete", expanded=False)
164
  elif success_count > 0:
165
- status_main.update(label=f"{success_count}/{len(st.session_state.story_scenes)} visuals generated. Some failed.", state="warning", expanded=False)
166
  else:
167
  status_main.update(label="Visual concept generation failed for all scenes.", state="error", expanded=False)
168
 
169
-
170
  st.markdown("---")
171
  st.markdown("### Advanced Options")
172
  with st.expander("Character Consistency", expanded=False):
173
  char_name_input = st.text_input("Character Name (e.g., Eva)", key="char_name_adv_input")
174
- char_desc_input = st.text_area("Character Description (for visual consistency)", key="char_desc_adv_input", height=80)
175
  if st.button("Add/Update Character", key="add_char_adv_btn"):
176
  if char_name_input and char_desc_input:
177
- st.session_state.character_definitions[char_name_input.strip()] = char_desc_input.strip()
 
178
  st.success(f"Character '{char_name_input.strip()}' defined.")
179
  else:
180
  st.warning("Please provide both name and description.")
181
  if st.session_state.character_definitions:
182
  st.caption("Defined Characters:")
183
- for char, desc in st.session_state.character_definitions.items():
184
- st.markdown(f"**{char}:** _{desc}_")
185
 
186
  with st.expander("Style Transfer", expanded=False):
187
- style_ref_text = st.text_area("Describe Style (e.g., 'Van Gogh inspired, swirling brushstrokes')", key="style_text_adv_input", height=80)
188
  if st.button("Apply Textual Style", key="apply_style_adv_btn"):
189
  st.session_state.style_reference_description = style_ref_text.strip()
190
  st.success("Style reference applied. Re-generate visuals or full story to see changes.")
@@ -221,7 +224,7 @@ else:
221
  with col2:
222
  current_image_path = st.session_state.generated_images_paths[i] if i < len(st.session_state.generated_images_paths) else None
223
  if current_image_path and os.path.exists(current_image_path):
224
- st.image(current_image_path, caption=f"Visual Concept for Scene {scene_num_display}")
225
  else:
226
  if st.session_state.story_scenes:
227
  st.caption("Visual for this scene is pending or failed to generate.")
@@ -282,7 +285,8 @@ else:
282
  if '_v' in base: version = int(base.split('_v')[-1]) + 1
283
  except : pass
284
 
285
- if generate_visual_for_scene_wrapper(i, scene_data_display, is_regeneration=True, version_count=version): # Pass current scene_data, as only prompt changed
 
286
  status_visual_regen.update(label="Visual for Scene updated! πŸŽ‰", state="complete", expanded=False)
287
  else:
288
  status_visual_regen.update(label="Prompt refined, but visual failed.", state="warning", expanded=False)
@@ -294,20 +298,30 @@ else:
294
  st.markdown("---")
295
 
296
  if st.session_state.story_scenes and any(p for p in st.session_state.generated_images_paths if p is not None):
297
- if st.button("🎬 Assemble Animatic Video", key="assemble_video_btn", type="primary", use_container_width=True):
298
- with st.status("Assembling video...", expanded=False) as status_video:
299
- valid_image_paths_for_video = [p for p in st.session_state.generated_images_paths if p and os.path.exists(p)]
300
- if valid_image_paths_for_video:
 
 
 
 
 
 
 
 
 
301
  st.session_state.video_path = st.session_state.visual_engine.create_video_from_images(
302
- valid_image_paths_for_video,
303
- output_filename="cinegen_pro_animatic.mp4",
304
- duration_per_image=3 # Adjust as needed
 
305
  )
306
  if st.session_state.video_path and os.path.exists(st.session_state.video_path):
307
- status_video.update(label="Animatic video assembled! πŸŽ‰", state="complete")
308
  st.balloons()
309
  else:
310
- status_video.update(label="Video assembly failed. Check logs.", state="error")
311
  else:
312
  status_video.update(label="No valid images to assemble video.", state="error")
313
  elif st.session_state.story_scenes:
@@ -318,12 +332,12 @@ else:
318
  try:
319
  with open(st.session_state.video_path, 'rb') as video_file_obj:
320
  video_bytes_content = video_file_obj.read()
321
- st.video(video_bytes_content)
322
  with open(st.session_state.video_path, "rb") as fp_download_video:
323
  st.download_button(
324
- label="Download Animatic Video", data=fp_download_video,
325
  file_name=os.path.basename(st.session_state.video_path), mime="video/mp4",
326
- use_container_width=True
327
  )
328
  except Exception as e:
329
  st.error(f"Error displaying or preparing video for download: {e}")
 
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 (
6
  create_story_breakdown_prompt,
7
  create_image_prompt_from_scene_data,
 
39
  # --- TRY TO SET OPENAI API KEY FOR VISUAL ENGINE ---
40
  openai_key_from_secrets = None
41
  try:
42
+ # Check Streamlit Cloud secrets first
43
  if "OPENAI_API_KEY" in st.secrets:
44
  openai_key_from_secrets = st.secrets["OPENAI_API_KEY"]
45
+ except AttributeError: # st.secrets might not exist in all local dev environments
46
+ print("st.secrets not available (likely local dev without secrets.toml). Checking environment variables.")
47
  except Exception as e:
48
+ print(f"Could not access st.secrets for OPENAI_API_KEY: {e}")
49
 
50
  if not openai_key_from_secrets and "OPENAI_API_KEY" in os.environ: # Fallback to env var
51
  openai_key_from_secrets = os.environ["OPENAI_API_KEY"]
52
 
53
  if openai_key_from_secrets:
54
  st.session_state.visual_engine.set_openai_api_key(openai_key_from_secrets)
55
+ # st.sidebar.caption("OpenAI API Key loaded. DALL-E ready.") # Optional UI feedback
56
  else:
57
  st.session_state.visual_engine.set_openai_api_key(None)
58
+ st.sidebar.caption("OpenAI API Key for DALL-E not found. Visuals will be placeholders.")
59
  # --- END OF OPENAI API KEY SETUP ---
60
 
61
  # Story and generated content state
 
81
 
82
  def generate_visual_for_scene_wrapper(scene_index, scene_data, is_regeneration=False, version_count=1):
83
  scene_num_for_log = scene_data.get('scene_number', scene_index + 1)
 
 
 
84
 
85
  textual_image_prompt = ""
86
+ if is_regeneration and scene_index < len(st.session_state.scene_image_prompts) and st.session_state.scene_image_prompts[scene_index]:
87
  textual_image_prompt = st.session_state.scene_image_prompts[scene_index]
88
+ # st.caption(f"Using refined prompt for Scene {scene_num_for_log}.") # Can be noisy
89
  else:
90
  textual_image_prompt = create_image_prompt_from_scene_data(
91
  scene_data,
92
  st.session_state.character_definitions,
93
  st.session_state.style_reference_description
94
  )
95
+ # st.caption(f"Generated initial prompt for Scene {scene_num_for_log}.") # Can be noisy
96
 
97
  if not textual_image_prompt:
98
+ # st.error(f"Failed to create/retrieve textual image prompt for Scene {scene_num_for_log}.") # Handled by status
99
  return False
100
 
101
  if scene_index >= len(st.session_state.scene_image_prompts):
 
105
 
106
  image_filename = f"scene_{scene_num_for_log}_visual_v{version_count}.png"
107
 
 
108
  generated_image_path = st.session_state.visual_engine.generate_image_visual(
109
+ textual_image_prompt, image_filename
 
110
  )
111
 
112
  while len(st.session_state.generated_images_paths) <= scene_index:
113
  st.session_state.generated_images_paths.append(None)
114
 
115
  if generated_image_path and os.path.exists(generated_image_path):
 
116
  st.session_state.generated_images_paths[scene_index] = generated_image_path
117
  return True
118
  else:
119
+ # st.warning(f"Visual for Scene {scene_num_for_log} (v{version_count}) failed or path invalid.") # Handled by status
120
  st.session_state.generated_images_paths[scene_index] = None
121
  return False
122
 
 
124
  with st.sidebar:
125
  st.title("🎬 CineGen AI Pro")
126
  st.markdown("### Creative Controls")
127
+ user_idea = st.text_area("Enter your core story idea:", "A lone astronaut discovers a glowing alien artifact on Mars, a sense of wonder and slight dread.", height=100, key="user_idea_input")
128
+ genre = st.selectbox("Genre:", ["Sci-Fi", "Fantasy", "Noir", "Thriller", "Drama", "Horror", "Cyberpunk"], index=6, key="genre_select")
129
+ mood = st.selectbox("Mood:", ["Suspenseful", "Mysterious", "Gritty", "Epic", "Dark", "Hopeful", "Wonder"], index=6, key="mood_select")
130
+ num_scenes_val = st.slider("Number of Scenes:", 1, 5, 3, key="num_scenes_slider") # Max 5 for sensible generation times
131
 
132
  if st.button("✨ Generate Full Story Concept", type="primary", key="generate_full_story_btn", use_container_width=True):
133
  initialize_new_story()
 
139
  story_prompt_text = create_story_breakdown_prompt(user_idea, genre, mood, num_scenes_val)
140
  try:
141
  st.session_state.story_scenes = st.session_state.gemini_handler.generate_story_breakdown(story_prompt_text)
142
+ status_main.update(label="Script breakdown complete! βœ…", state="running", expanded=True)
143
 
144
  num_actual_scenes = len(st.session_state.story_scenes)
145
  st.session_state.scene_image_prompts = [""] * num_actual_scenes
146
  st.session_state.generated_images_paths = [None] * num_actual_scenes
147
 
148
  except Exception as e:
149
+ status_main.update(label=f"Failed to generate story breakdown: {e}", state="error", expanded=True)
150
+ st.session_state.story_scenes = [] # Ensure it's empty on failure
151
+ st.stop()
152
 
153
  if st.session_state.story_scenes:
154
+ st.write("Phase 2: Generating initial visual concepts... πŸ–ΌοΈ (This may take a few minutes per scene with DALL-E)")
155
  success_count = 0
156
  for i_loop, scene_data_loop_var in enumerate(st.session_state.story_scenes):
157
+ scene_num_log = scene_data_loop_var.get('scene_number', i_loop + 1)
158
+ st.write(f"Generating visual for Scene {scene_num_log}...")
159
  if generate_visual_for_scene_wrapper(i_loop, scene_data_loop_var, version_count=1):
160
  success_count +=1
161
+ st.write(f"Visual for Scene {scene_num_log} done.")
162
+ else:
163
+ st.write(f"Visual for Scene {scene_num_log} failed.")
164
 
165
  if success_count == len(st.session_state.story_scenes):
166
  status_main.update(label="All concepts generated successfully! πŸŽ‰", state="complete", expanded=False)
167
  elif success_count > 0:
168
+ status_main.update(label=f"{success_count}/{len(st.session_state.story_scenes)} visuals generated.", state="warning", expanded=False)
169
  else:
170
  status_main.update(label="Visual concept generation failed for all scenes.", state="error", expanded=False)
171
 
 
172
  st.markdown("---")
173
  st.markdown("### Advanced Options")
174
  with st.expander("Character Consistency", expanded=False):
175
  char_name_input = st.text_input("Character Name (e.g., Eva)", key="char_name_adv_input")
176
+ char_desc_input = st.text_area("Character Description (e.g., 'female, short red hair, green eyes, wearing a blue jumpsuit')", key="char_desc_adv_input", height=100)
177
  if st.button("Add/Update Character", key="add_char_adv_btn"):
178
  if char_name_input and char_desc_input:
179
+ # Store character names in lowercase for easier matching, but keep original for display if needed
180
+ st.session_state.character_definitions[char_name_input.strip().lower()] = char_desc_input.strip()
181
  st.success(f"Character '{char_name_input.strip()}' defined.")
182
  else:
183
  st.warning("Please provide both name and description.")
184
  if st.session_state.character_definitions:
185
  st.caption("Defined Characters:")
186
+ for char_key, desc_val in st.session_state.character_definitions.items():
187
+ st.markdown(f"**{char_key.title()}:** _{desc_val}_") # Display with title case
188
 
189
  with st.expander("Style Transfer", expanded=False):
190
+ style_ref_text = st.text_area("Describe Visual Style (e.g., 'impressionistic oil painting, vibrant colors')", key="style_text_adv_input", height=100)
191
  if st.button("Apply Textual Style", key="apply_style_adv_btn"):
192
  st.session_state.style_reference_description = style_ref_text.strip()
193
  st.success("Style reference applied. Re-generate visuals or full story to see changes.")
 
224
  with col2:
225
  current_image_path = st.session_state.generated_images_paths[i] if i < len(st.session_state.generated_images_paths) else None
226
  if current_image_path and os.path.exists(current_image_path):
227
+ st.image(current_image_path, caption=f"Visual Concept for Scene {scene_num_display}", use_column_width=True)
228
  else:
229
  if st.session_state.story_scenes:
230
  st.caption("Visual for this scene is pending or failed to generate.")
 
285
  if '_v' in base: version = int(base.split('_v')[-1]) + 1
286
  except : pass
287
 
288
+ # Pass current scene_data_display as scene content hasn't changed, only the prompt.
289
+ if generate_visual_for_scene_wrapper(i, scene_data_display, is_regeneration=True, version_count=version):
290
  status_visual_regen.update(label="Visual for Scene updated! πŸŽ‰", state="complete", expanded=False)
291
  else:
292
  status_visual_regen.update(label="Prompt refined, but visual failed.", state="warning", expanded=False)
 
298
  st.markdown("---")
299
 
300
  if st.session_state.story_scenes and any(p for p in st.session_state.generated_images_paths if p is not None):
301
+ if st.button("🎬 Assemble Enhanced Animatic", key="assemble_enhanced_video_btn", type="primary", use_container_width=True):
302
+ with st.status("Assembling enhanced animatic video...", expanded=False) as status_video:
303
+ image_data_for_video = []
304
+ for idx, scene_info in enumerate(st.session_state.story_scenes):
305
+ img_path = st.session_state.generated_images_paths[idx] if idx < len(st.session_state.generated_images_paths) else None
306
+ if img_path and os.path.exists(img_path):
307
+ image_data_for_video.append({
308
+ 'path': img_path,
309
+ 'scene_num': scene_info.get('scene_number', idx + 1),
310
+ 'key_action': scene_info.get('key_action', '')
311
+ })
312
+
313
+ if image_data_for_video:
314
  st.session_state.video_path = st.session_state.visual_engine.create_video_from_images(
315
+ image_data_for_video,
316
+ output_filename="cinegen_pro_animatic_enhanced.mp4",
317
+ duration_per_image=4,
318
+ fps=24
319
  )
320
  if st.session_state.video_path and os.path.exists(st.session_state.video_path):
321
+ status_video.update(label="Enhanced animatic assembled! πŸŽ‰", state="complete")
322
  st.balloons()
323
  else:
324
+ status_video.update(label="Enhanced video assembly failed. Check logs.", state="error")
325
  else:
326
  status_video.update(label="No valid images to assemble video.", state="error")
327
  elif st.session_state.story_scenes:
 
332
  try:
333
  with open(st.session_state.video_path, 'rb') as video_file_obj:
334
  video_bytes_content = video_file_obj.read()
335
+ st.video(video_bytes_content, format="video/mp4") # Specify format for better compatibility
336
  with open(st.session_state.video_path, "rb") as fp_download_video:
337
  st.download_button(
338
+ label="Download Enhanced Animatic", data=fp_download_video,
339
  file_name=os.path.basename(st.session_state.video_path), mime="video/mp4",
340
+ use_container_width=True, key="download_video_btn" # Added key
341
  )
342
  except Exception as e:
343
  st.error(f"Error displaying or preparing video for download: {e}")