import json import logging import os import random import threading import time import streamlit as st from cache_utils import get_cached_content from crew import SizzleReelCrew from video_gen_new import SizzleReelVideoGenerator # Update the app ideas with predefined names APP_IDEAS_WITH_NAMES = [ { "idea": "A social networking app for gardening enthusiasts to share tips and track plant growth", "name": "BloomBuddy" }, { "idea": "A fitness app that uses AI to create personalized workout and nutrition plans", "name": "FitFlow AI" }, { "idea": "A language learning platform that connects users with native speakers for real-time conversation practice", "name": "LingoPal" }, { "idea": "A mental health companion app providing daily moods tracking and personalized meditation guidance", "name": "MindMate" }, { "idea": "An eco-friendly shopping app that helps users make sustainable purchasing decisions", "name": "EcoCart" }, { "idea": "A collaborative recipe sharing platform with AI-powered meal planning and grocery list generation", "name": "ChefSync" }, { "idea": "A travel companion app that offers personalized itineraries and local experience recommendations", "name": "WanderWise" }, { "idea": "A skill-sharing platform where users can learn and teach various skills through interactive workshops", "name": "SkillShare+" } ] # Update the generate app name function def generate_app_name(idea): """Get the predefined app name for the idea.""" for app in APP_IDEAS_WITH_NAMES: if app["idea"] == idea: return app["name"] return "AppName" # fallback # Update the progress function def update_progress(progress_bar, status): """Simple progress updates using status container""" task_details = [ { "name": "Hero Research Specialist", "emoji": "🕵️", "progress": 25, "description": "Analyzing user personas and market opportunities", }, { "name": "Content Planner", "emoji": "📊", "progress": 50, "description": "Planning key messaging and content structure", }, { "name": "Narrative Architect", "emoji": "📝", "progress": 75, "description": "Crafting the core narrative structure", }, { "name": "Sizzle Reel Director", "emoji": "🎬", "progress": 100, "description": "Finalizing the promotional script", } ] # Show task progression for task in task_details: status.write(f"{task['emoji']} {task['name']}: {task['description']}...") progress_bar.progress(task['progress']) # time.sleep(2) def countdown_loader(duration=75): """Display a countdown loader""" placeholder = st.empty() progress_bar = st.progress(0) for remaining in range(duration, 0, -1): progress = (duration - remaining) / duration placeholder.text(f"⏳ Generating video... Estimated time remaining: {remaining} seconds") progress_bar.progress(progress) time.sleep(1.2) # Uncomment this to ensure real-time updates placeholder.empty() # Clear the placeholder after countdown return placeholder, progress_bar def create_task_callback(task_config, status_container, progress_bar, progress_value, task_status): """Create a callback for task completion""" def callback(output): # Clear previous task messages for ts in task_statuses: ts.empty() # Show current task task_status.write(f"🔄 In Progress: {task_config['emoji']} {task_config['name']}: {task_config['description']}") progress_bar.progress(progress_value) return output return callback def create_task_callback(task_config, status_container, progress_bar, progress_value, task_status, task_statuses): """Create a callback for task completion""" def callback(output): # Clear previous task messages for ts in task_statuses: ts.empty() # Show current task with role information status_message = f"🔄 {task_config['emoji']} {task_config['role']}: {task_config['description']}" task_status.write(status_message) progress_bar.progress(progress_value) return output return callback def generate_random_app(): selected_app = random.choice(APP_IDEAS_WITH_NAMES) st.session_state.app_name = selected_app["name"] st.session_state.customer_idea = selected_app["idea"] # Add this to automatically trigger the generate button st.session_state.generate_clicked = True def main(): # Initialize all session state variables if 'video_path' not in st.session_state: st.session_state.video_path = None if 'app_name' not in st.session_state: st.session_state.app_name = '' if 'customer_idea' not in st.session_state: st.session_state.customer_idea = '' if 'raw_script_result' not in st.session_state: st.session_state.raw_script_result = None if 'active_tab' not in st.session_state: st.session_state.active_tab = "video" if 'cached_content' not in st.session_state: st.session_state.cached_content = None # Page config with clean layout st.set_page_config( page_title="Sizzle Reel Generator", page_icon="🎬", layout="wide", initial_sidebar_state="expanded" ) # Title and Introduction with custom styling st.markdown("

🎬 Sizzle Reel Generator

", unsafe_allow_html=True) st.markdown("

Create an engaging promotional video for your app in a single click!

", unsafe_allow_html=True) # Progress bar at the top progress_bar = st.progress(0) # Sidebar for inputs with st.sidebar: # Info about Sizzle Reels st.info(""" 💡 **What is a Sizzle Reel?** A sizzle reel is a short, captivating promotional video designed to showcase your app’s unique features, value, and vision. It's designed to grab attention, and leave a lasting impression on potential users or investors. """) # Random Generator Button - In the middle st.write("Need inspiration? Try a random app idea:") if st.button("🎲 Generate Random App", key="random_app_button", use_container_width=True): selected_app = random.choice(APP_IDEAS_WITH_NAMES) st.session_state.app_name = selected_app["name"] st.session_state.customer_idea = selected_app["idea"] # Check if selected app has cached content cached_content = get_cached_content(selected_app["name"]) if cached_content: st.session_state.cached_content = cached_content else: st.session_state.cached_content = None st.session_state.auto_generate = True # Set flag to trigger auto-generation st.rerun() # App Name Input app_name = st.text_input( "App Name", value=st.session_state.get('app_name', ''), placeholder="e.g., GreenFingers", key='app_name_input' ) # App Idea Input customer_idea = st.text_area( "App Idea", value=st.session_state.get('customer_idea', ''), placeholder="Describe your app's core purpose", key='customer_idea_input' ) # Generate Script Button # Check if auto_generate flag is set or button is pressed auto_generate = st.session_state.pop("auto_generate", False) if "auto_generate" in st.session_state else False generate_clicked = st.button("Generate Sizzle Reel", type="primary") # --- NEW LOGIC: If user clicks generate with empty fields, trigger random app and rerun --- if generate_clicked and (not app_name.strip() or not customer_idea.strip()): selected_app = random.choice(APP_IDEAS_WITH_NAMES) st.session_state.app_name = selected_app["name"] st.session_state.customer_idea = selected_app["idea"] cached_content = get_cached_content(selected_app["name"]) if cached_content: st.session_state.cached_content = cached_content else: st.session_state.cached_content = None st.session_state.auto_generate = True st.rerun() # --- END NEW LOGIC --- if (generate_clicked or auto_generate) and app_name.strip() and customer_idea.strip(): # Update session state with latest input values st.session_state.app_name = app_name st.session_state.customer_idea = customer_idea inputs = { "app_name": st.session_state.app_name, "customer_idea": st.session_state.customer_idea, } # Only check cache if it's from our predefined list is_predefined = any(app["name"] == st.session_state.app_name and app["idea"] == st.session_state.customer_idea for app in APP_IDEAS_WITH_NAMES) cached_content = get_cached_content(st.session_state.app_name) if is_predefined else None if cached_content: # Use cached content for predefined apps video_path, script = cached_content # Script Generation Status (faster for cached content) with st.status("Generating Script...", expanded=True) as script_status: hero_status = script_status.empty() content_status = script_status.empty() script_status_placeholder = script_status.empty() # Show progress with minimal delays hero_status.write("🕵️ Hero Research Specialist: Analyzing...") progress_bar.progress(33) time.sleep(1.2) # <-- changed from 0.5 to 1 content_status.write("📝 Narrative Architect: Creating content...") progress_bar.progress(66) time.sleep(1.2) # <-- changed from 0.5 to 1 script_status_placeholder.write("🎬 Script Writer: Finalizing...") progress_bar.progress(90) time.sleep(1.2) # <-- changed from 0.5 to 1 script_status.update(label="Script Generated! ✅", state="complete", expanded=False) # Video section (faster for cached content) video_placeholder = st.empty() video_placeholder.info("🎥 Starting video generation...") # Quick countdown for cached videos loader_placeholder, loader_progress = countdown_loader(duration=5) # Display cached video loader_placeholder.empty() loader_progress.empty() st.session_state.video_path = video_path st.session_state.raw_script_result = script progress_bar.progress(100) video_placeholder.success("✨ Video generated successfully!") st.balloons() # Display video and script tabs tab1, tab2 = st.tabs(["🎥 Media Preview", "📜 Script"]) with tab1: st.video(video_path) with open(video_path, 'rb') as f: st.download_button( label="Download Video 📥", data=f, file_name=f"{st.session_state.app_name}_sizzle_reel.mp4", mime="video/mp4" ) with tab2: st.json(script) else: # Original code for non-cached apps # Script Generation Status with st.status("Generating Script...", expanded=True) as script_status: try: crew = SizzleReelCrew(inputs).crew() # Create task status placeholders hero_status = script_status.empty() content_status = script_status.empty() script_status_placeholder = script_status.empty() # Show initial task hero_status.write("🕵️ Starting Hero Research...") # Add callbacks to crew tasks for i, task in enumerate(crew.tasks): def create_callback(task_index): def callback(output): if task_index == 0: # Hero Research Task hero_status.write("�️ Hero Research Specialist: Analyzing hero user journey...") progress_bar.progress(20) time.sleep(2) # Give time to read hero_status.write("🎯 Hero Research Specialist: Uncovering user persona and journey insights for the app...") progress_bar.progress(33) elif task_index == 1: # Content Strategy Task content_status.write("📊 Narrative Architect: Creating content strategy...") progress_bar.progress(45) time.sleep(2) # Give time to read content_status.write("✍️ Narrative Architect: Crafting a compelling narrative...") progress_bar.progress(66) elif task_index == 2: # Script Generation Task script_status_placeholder.write("✍️ Script Writer: Writing final script...") progress_bar.progress(80) time.sleep(2) # Give time to read script_status_placeholder.write("🎬 Script Writer: Transforming insights into an engaging sizzle reel script...") progress_bar.progress(90) return output return callback task.callback = create_callback(i) result = crew.kickoff(inputs=inputs) script_status_placeholder.write("✅ Script Writing Complete") script_status.update(label="Script Generated! ✅", state="complete", expanded=False) except Exception as e: logging.exception("Script generation error:") script_status.update(label="Script generation failed! ❌", state="error") st.error(f"An error occurred: {str(e)}") st.stop() # Video generation section video_placeholder = st.empty() video_placeholder.info("🎥 Starting video generation...") # Video generation section try: video_generator = SizzleReelVideoGenerator() formatted_script = {"sizzle_reel_script": result} app_name = st.session_state.app_name # Create a thread-safe way to store video path and status video_path_result = [None] video_generation_complete = threading.Event() generation_start_time = time.time() def video_generation_task(generator, script, name): try: video_path = generator.generate_sizzle_reel( script_json=script, app_name=name ) video_path_result[0] = video_path except Exception as e: logging.error(f"Video generation error in thread: {e}", exc_info=True) finally: video_generation_complete.set() # Start video generation in a separate thread video_thread = threading.Thread( target=video_generation_task, args=(video_generator, formatted_script, app_name) ) video_thread.start() # Run countdown and check for long-running generation loader_placeholder, loader_progress = countdown_loader(duration=75) # Check every few seconds if we need to show the warning while not video_generation_complete.is_set() and time.time() - generation_start_time <= 150: if time.time() - generation_start_time > 75: video_placeholder.warning("🕒 Video generation is taking longer than usual, but we're still working on it, hang tight!") time.sleep(5) # Check every 5 seconds # Wait for completion or timeout video_thread.join(timeout=150) # Check final video path video_path = video_path_result[0] if video_path: # Clear the loader placeholders loader_placeholder.empty() loader_progress.empty() st.session_state.video_path = video_path st.session_state.raw_script_result = result progress_bar.progress(100) video_placeholder.success("✨ Video generated successfully!") st.balloons() # Display video and script tabs tab1, tab2 = st.tabs(["🎥 Media Preview", "📜 Script"]) with tab1: st.video(video_path) with open(video_path, 'rb') as f: st.download_button( label="Download Video 📥", data=f, file_name=f"{app_name}_sizzle_reel.mp4", mime="video/mp4" ) with tab2: st.json(result) else: video_placeholder.warning("Video generation timed out or failed.") except Exception as video_error: logging.error(f"Video generation error: {video_error}", exc_info=True) video_placeholder.warning("Script generated successfully, but video generation failed.") # Footer st.markdown("---") st.markdown("🚀 Powered by CrewAI, Pexels, and Deepgram | Sizzle Reel Generator") if __name__ == "__main__": main()