#!/usr/bin/env python3 import gradio as gr import asyncio from director_bake_off import run_bake_off import traceback # Clean, professional light mode styling LIGHT_MODE_CSS = """ /* Force light mode and override system preferences */ * { color-scheme: light !important; } /* Override any dark mode media queries */ @media (prefers-color-scheme: dark) { :root { color-scheme: light !important; } body, .gradio-container { background: #ffffff !important; color: #1f2937 !important; } } /* Clean, professional styling */ .gradio-container { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; background: #ffffff !important; color: #1f2937 !important; } /* Header styling */ .main-header { text-align: center; padding: 2rem 0; background: #ffffff; border-bottom: 1px solid #e5e7eb; margin-bottom: 2rem; } .main-title { font-size: 2.25rem; font-weight: 700; color: #1f2937; margin-bottom: 0.5rem; } .main-subtitle { font-size: 1.125rem; color: #6b7280; font-weight: 400; } /* Force light backgrounds for ALL input containers and elements */ .gr-textbox, .gr-textarea, input, textarea, .gr-textbox *, .gr-textarea *, .gr-textbox > div, .gr-textarea > div, .gradio-textbox, .gradio-textarea { background: #ffffff !important; background-color: #ffffff !important; color: #1f2937 !important; border: 1px solid #d1d5db !important; border-radius: 6px !important; } /* Force the container backgrounds to be light */ .gr-textbox, .gr-textarea { background: #ffffff !important; background-color: #ffffff !important; } /* Force all child elements to have light backgrounds */ .gr-textbox > *, .gr-textarea > *, .gr-textbox > div > *, .gr-textarea > div > * { background: #ffffff !important; background-color: #ffffff !important; color: #1f2937 !important; } .gr-textbox:focus, .gr-textarea:focus, input:focus, textarea:focus { border-color: #3b82f6 !important; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; outline: none !important; background: #ffffff !important; background-color: #ffffff !important; } /* Button styling */ .submit-btn { background: #3b82f6 !important; color: #ffffff !important; border: none !important; border-radius: 6px !important; padding: 0.75rem 1.5rem !important; font-weight: 600 !important; transition: background-color 0.2s !important; } .submit-btn:hover { background: #2563eb !important; } /* Results styling */ .results-container { background: #ffffff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 1.5rem; margin-top: 1.5rem; } .director-card { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 1.5rem; margin-bottom: 1rem; } .rank-badge { display: inline-block; background: #3b82f6; color: #ffffff; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.875rem; font-weight: 600; margin-bottom: 0.5rem; } .rank-1 { background: #f59e0b; } .rank-2 { background: #6b7280; } .rank-3 { background: #d97706; } .director-name { font-size: 1.25rem; font-weight: 600; color: #1f2937; margin-bottom: 1rem; } .prompt-text { background: #ffffff; color: #1f2937; padding: 1rem; border: 1px solid #e5e7eb; border-radius: 6px; line-height: 1.6; margin-bottom: 1rem; } .additional-director { background: #ecfdf5; border: 1px solid #d1fae5; border-radius: 8px; padding: 1.5rem; margin-bottom: 1.5rem; } .explanation-section { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 1.5rem; margin-top: 1.5rem; } .section-title { font-size: 1.125rem; font-weight: 600; color: #1f2937; margin-bottom: 1rem; } .loading { text-align: center; padding: 2rem; color: #6b7280; } /* Ensure all Gradio components use light mode */ .gr-block, .gr-form, .gr-box { background: #ffffff !important; color: #1f2937 !important; } /* Override any remaining dark mode styles */ .dark, [data-theme="dark"] { background: #ffffff !important; color: #1f2937 !important; } /* Force all text elements to be visible */ h1, h2, h3, h4, h5, h6, p, div, span, li, ol, ul { color: #1f2937 !important; } /* Ensure HTML content is visible */ .gr-html * { color: #1f2937 !important; } /* Force visibility for all text content */ * { color: #1f2937 !important; } /* Override Gradio's default text colors */ .gradio-container * { color: #1f2937 !important; } /* AGGRESSIVE LABEL VISIBILITY - Override all Gradio label styles */ .gr-label, .gr-label *, label, .label, .gradio-textbox label, .gradio-textarea label, [data-testid*="textbox"] label, [data-testid*="textarea"] label, .gr-textbox .gr-label, .gr-textarea .gr-label, .gr-textbox span[data-testid*="label"], .gr-textarea span[data-testid*="label"], .gr-textbox .label, .gr-textarea .label, .gr-form .gr-label, .gr-block .gr-label, .gradio-container .gr-label, .gradio-container label, .gr-textbox > span, .gr-textarea > span, .gr-textbox > div > span, .gr-textarea > div > span, .gr-textbox .svelte-*, .gr-textarea .svelte-*, span[class*="label"], div[class*="label"], .gr-textbox span:first-child, .gr-textarea span:first-child { color: #1f2937 !important; font-weight: 600 !important; font-size: 1rem !important; margin-bottom: 0.5rem !important; display: block !important; opacity: 1 !important; visibility: visible !important; background: transparent !important; } /* Target Gradio's internal label structure more aggressively */ .gr-textbox > div:first-child, .gr-textarea > div:first-child, .gr-textbox > div:first-child > *, .gr-textarea > div:first-child > *, .gr-textbox .svelte-1gfkn6j, .gr-textarea .svelte-1gfkn6j, .gr-textbox .svelte-*, .gr-textarea .svelte-* { color: #1f2937 !important; font-weight: 600 !important; opacity: 1 !important; visibility: visible !important; } /* Force all possible label selectors */ .gradio-container [class*="label"], .gradio-container [data-testid*="label"], .gradio-container .gr-textbox *:first-child, .gradio-container .gr-textarea *:first-child { color: #1f2937 !important; font-weight: 600 !important; opacity: 1 !important; visibility: visible !important; } /* Nuclear option - force ALL text in textbox containers to be visible */ .gr-textbox *, .gr-textarea * { color: #1f2937 !important; opacity: 1 !important; visibility: visible !important; } /* NUCLEAR BACKGROUND OVERRIDE - Force all containers to be white */ .gr-textbox, .gr-textarea, .gr-textbox *, .gr-textarea *, .gr-textbox > div, .gr-textarea > div, .gr-textbox > div > div, .gr-textarea > div > div, .gr-textbox > div > div > div, .gr-textarea > div > div > div, [class*="textbox"], [class*="textarea"], [data-testid*="textbox"], [data-testid*="textarea"] { background: #ffffff !important; background-color: #ffffff !important; } /* Override any blue/dark backgrounds specifically */ .gr-textbox [style*="background"], .gr-textarea [style*="background"], .gr-textbox [style*="background-color"], .gr-textarea [style*="background-color"] { background: #ffffff !important; background-color: #ffffff !important; } /* Force white background on any element with blue/dark styling */ [style*="background: rgb("], [style*="background-color: rgb("], [style*="background:#"], [style*="background-color:#"] { background: #ffffff !important; background-color: #ffffff !important; } """ def format_results_html(result): """Format the results into clean, professional HTML.""" 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(): # Force light mode theme light_theme = gr.themes.Default( primary_hue="blue", secondary_hue="gray", neutral_hue="gray" ) with gr.Blocks(css=LIGHT_MODE_CSS, theme=light_theme, 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 )