#!/usr/bin/env python3 import gradio as gr import asyncio from director_bake_off import run_bake_off import traceback # Swedish-inspired color palette and styling SWEDISH_CSS = """ /* Swedish Minimalist Design */ :root { --primary-white: #FFFFFF; --soft-gray: #F5F5F5; --muted-blue: #4A90A4; --warm-beige: #E8DCC6; --charcoal: #2C2C2C; --light-blue: #E8F4F8; --accent-gold: #D4AF37; --success-green: #7FB069; --border-gray: #E0E0E0; } /* Global styling */ .gradio-container { font-family: 'Inter', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important; background: linear-gradient(135deg, var(--soft-gray) 0%, var(--primary-white) 100%) !important; min-height: 100vh; } /* Header styling */ .main-header { text-align: center; padding: 2rem 0; background: var(--primary-white); border-bottom: 1px solid var(--border-gray); margin-bottom: 2rem; } .main-title { font-size: 2.5rem; font-weight: 300; color: var(--charcoal); margin-bottom: 0.5rem; letter-spacing: -0.02em; } .main-subtitle { font-size: 1.1rem; color: var(--muted-blue); font-weight: 400; margin-bottom: 0; } /* Input section styling */ .input-section { background: var(--primary-white); border-radius: 12px; padding: 2rem; box-shadow: 0 2px 20px rgba(0,0,0,0.05); border: 1px solid var(--border-gray); margin-bottom: 2rem; } .input-label { font-size: 1rem; font-weight: 500; color: var(--charcoal); margin-bottom: 0.5rem; display: block; } /* Button styling */ .submit-btn { background: linear-gradient(135deg, var(--muted-blue) 0%, #5BA0B5 100%) !important; border: none !important; border-radius: 8px !important; padding: 12px 32px !important; font-weight: 500 !important; font-size: 1rem !important; color: white !important; transition: all 0.3s ease !important; box-shadow: 0 4px 15px rgba(74, 144, 164, 0.3) !important; } .submit-btn:hover { transform: translateY(-2px) !important; box-shadow: 0 6px 25px rgba(74, 144, 164, 0.4) !important; } /* Results section */ .results-container { background: var(--primary-white); border-radius: 12px; padding: 2rem; box-shadow: 0 2px 20px rgba(0,0,0,0.05); border: 1px solid var(--border-gray); margin-top: 2rem; } .director-card { background: var(--light-blue); border-radius: 10px; padding: 1.5rem; margin-bottom: 1.5rem; border-left: 4px solid var(--muted-blue); transition: all 0.3s ease; } .director-card:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(0,0,0,0.1); } .rank-badge { display: inline-block; background: var(--accent-gold); color: white; padding: 4px 12px; border-radius: 20px; font-size: 0.85rem; font-weight: 600; margin-bottom: 0.5rem; } .rank-1 { background: var(--accent-gold); } .rank-2 { background: #C0C0C0; } .rank-3 { background: #CD7F32; } .director-name { font-size: 1.3rem; font-weight: 600; color: var(--charcoal); margin-bottom: 1rem; } .prompt-text { font-size: 1rem; line-height: 1.6; color: var(--charcoal); background: var(--primary-white); padding: 1rem; border-radius: 8px; border: 1px solid var(--border-gray); margin-bottom: 1rem; } .additional-director { background: var(--warm-beige); border-radius: 10px; padding: 1.5rem; margin-bottom: 2rem; border-left: 4px solid var(--success-green); } .explanation-section { background: var(--soft-gray); border-radius: 10px; padding: 1.5rem; margin-top: 2rem; border-left: 4px solid var(--muted-blue); } .section-title { font-size: 1.2rem; font-weight: 600; color: var(--charcoal); margin-bottom: 1rem; } /* Loading animation */ .loading { text-align: center; padding: 2rem; color: var(--muted-blue); } /* Responsive design */ @media (max-width: 768px) { .main-title { font-size: 2rem; } .input-section, .results-container { padding: 1.5rem; margin: 1rem; } .director-card { padding: 1rem; } } /* Custom textbox styling */ .gr-textbox { border-radius: 8px !important; border: 1px solid var(--border-gray) !important; } .gr-textbox:focus { border-color: var(--muted-blue) !important; box-shadow: 0 0 0 3px rgba(74, 144, 164, 0.1) !important; } """ def format_results_html(result): """Format the results into beautiful HTML with Swedish design.""" if not result: return "
No results to display.
" try: # Header with additional director suggestion html = f"""
🎬 AI Suggested Director
{result.additional_director} - A perfect match for your vision!
""" # Director ideas with rankings html += '
🏆 Director Interpretations (Ranked)
' # Sort director ideas by ranking ranked_ideas = [] for i, idea in enumerate(result.director_ideas): rank = result.director_ranks.director_rankings[i] ranked_ideas.append((rank, idea.director_cut)) ranked_ideas.sort(key=lambda x: x[0]) # Sort by rank (1 is best) for rank, director_cut in ranked_ideas: rank_class = f"rank-{min(rank, 3)}" # Use rank-1, rank-2, rank-3 classes html += f"""
#{rank}
{director_cut.director}
{director_cut.assemble_prompt()}
View Detailed Breakdown
Subject: {director_cut.subject_description}
Action: {director_cut.action_description}
Setting: {director_cut.setting_description}
Style: {director_cut.cinematic_style}
Shot & Framing: {director_cut.shot_and_framing}
Camera Movement: {director_cut.camera_movement}
Lighting & Color: {director_cut.lighting_and_color}
""" # Explanation section with HTML formatting from DSPy html += f"""
🤔 Judge's Reasoning
{result.director_ranks.explanation}
""" return html except Exception as e: return f"""
Error formatting results: {str(e)}
""" def run_director_bakeoff(video_idea, directors): """Run the director bake-off and return formatted results.""" if not video_idea or not video_idea.strip(): return "
Please enter a video idea to get started!
" try: # Initial loading message with expectations yield """
🎬 Director Bake-Off in Progress...
Please be patient, this may take some minutes...
We're consulting with legendary directors to bring your vision to life!
This process typically takes 30-60 seconds.
What's happening:
• Finding the perfect additional director for your concept
• Generating unique interpretations from each director
• Ranking all concepts to find the best match
""" # Run the bake-off result = run_bake_off(video_idea, directors) # Format and return results formatted_html = format_results_html(result) yield formatted_html except Exception as e: error_html = f"""
Something went wrong!
{str(e)}
View technical details
{traceback.format_exc()}
                    
""" yield error_html # Create the Gradio interface def create_interface(): with gr.Blocks(css=SWEDISH_CSS, title="Director Bake-Off Studio") as interface: # Header gr.HTML("""

Director Bake-Off Studio

Let legendary directors compete to bring your vision to life

""") # How it Works section - moved to top gr.HTML("""

How it works:

  1. Enter your video idea in the text area below
  2. List your favorite directors (or use our defaults)
  3. Our AI will suggest an additional director perfect for your vision
  4. Watch as each director creates their unique interpretation
  5. See them ranked by how well they match your concept!
""") # Results section - will only show content when there are actual results results_html = gr.HTML( value="", elem_classes=["results-display"], visible=False ) with gr.Row(): with gr.Column(scale=1): # Input section video_idea = gr.Textbox( label="🎥 Your Video Idea", placeholder="Describe your video concept... (e.g., 'A futuristic cityscape with flying cars and neon lights')", lines=4, elem_classes=["input-field"] ) directors = gr.Textbox( label="🎬 Directors (comma-separated)", placeholder="e.g., Quentin Tarantino, Alfred Hitchcock", value="Quentin Tarantino, Alfred Hitchcock", lines=2, elem_classes=["input-field"] ) submit_btn = gr.Button( "🚀 Start the Bake-Off!", elem_classes=["submit-btn"], variant="primary" ) # Set up the interaction - removed show_progress=True to eliminate progress bar def handle_submit(video_idea, directors): # Make results visible and update content for result in run_director_bakeoff(video_idea, directors): yield gr.update(value=result, visible=True) submit_btn.click( fn=handle_submit, inputs=[video_idea, directors], outputs=[results_html] ) # Footer gr.HTML("""

Powered by DSPy and the creative genius of legendary directors 🎬

""") return interface if __name__ == "__main__": # Create and launch the interface interface = create_interface() interface.launch( server_name="0.0.0.0", server_port=7860, share=False, show_error=True, debug=True )