magical-tales / app.py
agharsallah
adding Progress bar on chapter creation
c323310
"""
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)