""" Main application entry point file for the Magic Story Creator """ import gradio as gr import logging from dotenv import load_dotenv from pathlib import Path from ui.styles import main_css from ui.components import ( create_chapter_loading_placeholder, create_header, create_instructions_accordion, create_story_tab_header, create_step_heading, create_story_options, create_reader_details, create_story_details, create_story_action_buttons, create_loading_container, story_ready_header, create_story_output_container, create_chapter_loading_container, create_empty_chapters_placeholder, create_chapter_error_display, create_story_title_display, create_chapter_navigation, create_chapter_accordion, create_story_melody_section, create_3d_model_viewer, create_readme_display, ) # from ui.mcp_components import letter_counter_component from controllers.app_controller import ( generate_audio_with_status, generate_melody_from_story_with_status, generate_3d_model, ) from ui.events import setup_event_handlers # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) # Load environment variables from .env file if present (local development) load_dotenv() # Set static paths for assets gr.set_static_paths(paths=[Path.cwd().absolute() / "assets/images"]) def create_app(): """Create the main Gradio app for Magic Story Creator""" with gr.Blocks( css=main_css, theme=gr.themes.Ocean(), title="Magic Tales", ) as demo: # Header create_header() # Instructions accordion create_instructions_accordion() # Create the main tabs tabs = gr.Tabs() with tabs: # Story Generator Tab with gr.Tab("Story Generator"): create_story_tab_header() # Story options with gr.Column(elem_classes="gr-form"): gr.HTML(create_step_heading(1, "Choose your adventure!")) story_type, tone = create_story_options() # Reader details with gr.Column(elem_classes="gr-form"): gr.HTML(create_step_heading(2, "Tell us about the reader!")) kid_age, kid_language, kid_interests = create_reader_details() # Story details with gr.Column(elem_classes="gr-form"): gr.HTML(create_step_heading(3, "Story details")) subject, reading_time, pdf_upload, model_selector = ( create_story_details() ) # Action buttons generate_button, clear_button = create_story_action_buttons() # Loading animation with gr.Row(visible=False): create_loading_container() # Story output container with gr.Blocks(elem_classes="gr-form", visible=False): story_ready_header() story_title, story_text, process_chapters_button = ( create_story_output_container() ) # Chapters & Image Prompts Tab with gr.Tab("Chapters & Image Prompts"): # State to store chapter data chapters_state = gr.State(value=None) # Chapter processing loading animation chapter_loading_container = create_chapter_loading_container() # Function to show the chapter loading container when processing starts def show_chapter_progress_container(): return gr.update(visible=True) # Connect process_chapters_button to show the loading container process_chapters_button.click( show_chapter_progress_container, inputs=[], outputs=[chapter_loading_container], ) @gr.render(inputs=[chapters_state]) def render_chapters(chapters_data): if chapters_data is None: return create_empty_chapters_placeholder() # Check if chapters_data is a string indicating an error or dict with error key if isinstance(chapters_data, str) and chapters_data.startswith( "Error:" ): return create_chapter_error_display(chapters_data) elif isinstance(chapters_data, dict) and "error" in chapters_data: return create_chapter_error_display(chapters_data["error"]) # Check if we're still processing if isinstance(chapters_data, dict) and chapters_data.get( "processing", False ): # If chapters exist but we're still processing, show them with loading indicators chapters = chapters_data.get("chapters", []) if chapters: # Display story title with magical styling create_story_title_display( chapters_data.get("title", "Story Chapters") ) # Chapter navigation create_chapter_navigation() # Display each chapter with appropriate loading indicators for i, chapter in enumerate(chapters, 1): status = chapter.get( "status", "Creating your magical illustration..." ) ( read_aloud_btn, read_aloud_audio, chapter_content_state, audio_statuss, ) = create_chapter_accordion(index=i, chapter=chapter) # Add audio generation functionality read_aloud_btn.click( generate_audio_with_status, inputs=[chapter_content_state], outputs=[read_aloud_audio, audio_statuss], ) return gr.update() else: # If no chapters yet, show a processing message return create_chapter_loading_placeholder() # Display story title with magical styling create_story_title_display( chapters_data.get("title", "Story Chapters") ) # Chapter navigation create_chapter_navigation() # Display each chapter with buttons in colorful cards for i, chapter in enumerate(chapters_data.get("chapters", []), 1): ( read_aloud_btn, read_aloud_audio, chapter_content_state, audio_statuss, ) = create_chapter_accordion(index=i, chapter=chapter) # Add audio generation functionality read_aloud_btn.click( generate_audio_with_status, inputs=[chapter_content_state], outputs=[read_aloud_audio, audio_statuss], ) # Story melody section generate_melody_button, melody_status, melody_output = ( create_story_melody_section() ) # Add melody generation from story functionality generate_melody_button.click( generate_melody_from_story_with_status, inputs=[story_text], outputs=[melody_output, melody_status], ) # 3D Model Viewer Tab (demo) generate_model_button, model_viewer, model_status = ( create_3d_model_viewer(story_text) ) # Connect the button to the generation function generate_model_button.click( generate_3d_model, inputs=[story_text], outputs=[model_viewer, model_status], ) with gr.Tab("README"): create_readme_display( ["🌟 Magical Tales: Where Imagination Comes Alive! 📚✨"] ) # TODO DELETE THIS AS IT IRRELEVENT TO STORY GENERATION """ with gr.Tab("Character Counter"): letter_counter_component() """ # Set up event handlers setup_event_handlers( generate_button=generate_button, clear_button=clear_button, process_chapters_button=process_chapters_button, story_type=story_type, tone=tone, kid_age=kid_age, kid_language=kid_language, kid_interests=kid_interests, subject=subject, reading_time=reading_time, pdf_upload=pdf_upload, model_selector=model_selector, story_title=story_title, story_text=story_text, chapters_state=chapters_state, chapter_loading_container=chapter_loading_container, ) return demo # Create the main application demo = create_app() # Launch the Gradio app with enhanced settings if __name__ == "__main__": try: logger.info("Starting Magic Story Creator app") demo.launch(mcp_server=True) except Exception as e: logger.error(f"Failed to start app: {e}", exc_info=True)