EduVid-LLM / app.py
gokul00060's picture
New VERY Conservative Limits:
2353c7d
"""
LVM Video Engine - Gradio Web Interface
AI-driven educational video generation - Streamlined with Smart Logging
"""
import gradio as gr
import os
import tempfile
import shutil
from pathlib import Path
from src.core.video_engine import LVMVideoEngine
from src.utils.smart_logger import logger
import time
from datetime import datetime
# Load environment variables from .env file if it exists
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
env_file = Path(".env")
if env_file.exists():
with open(env_file) as f:
for line in f:
if line.strip() and not line.startswith('#'):
key, value = line.strip().split('=', 1)
os.environ[key] = value
class GradioVideoEngine:
def __init__(self):
self.engine = None
self.current_project_dir = None
self.log_messages = []
def log_message(self, message):
"""Add timestamped log message for UI display"""
timestamp = datetime.now().strftime("%H:%M:%S")
log_entry = f"[{timestamp}] {message}"
self.log_messages.append(log_entry)
return "\n".join(self.log_messages[-30:]) # Keep last 30 messages
def initialize_engine(self, api_key):
"""Initialize the video engine with API key"""
self.log_message("πŸ”§ Starting engine initialization...")
if not api_key:
error_msg = "❌ Please provide a valid Gemini API key"
self.log_message(error_msg)
return error_msg
os.environ["GEMINI_API_KEY"] = api_key
self.current_project_dir = tempfile.mkdtemp(prefix="lvm_project_")
try:
self.engine = LVMVideoEngine(self.current_project_dir)
if self.engine.gemini_client:
success_msg = "βœ… Engine initialized successfully!"
self.log_message(success_msg)
return success_msg
else:
error_msg = "❌ Failed to initialize Gemini AI client"
self.log_message(error_msg)
return error_msg
except Exception as e:
error_msg = f"❌ Error initializing engine: {str(e)[:50]}..."
self.log_message(error_msg)
return error_msg
def generate_video_plan(self, prompt, api_key):
"""Generate a video plan from user prompt"""
self.log_message("πŸ“‹ Starting video plan generation...")
if not prompt:
error_msg = "❌ Please provide a video topic/prompt"
self.log_message(error_msg)
return error_msg, "", self.log_message("")
# Initialize engine if needed
init_result = self.initialize_engine(api_key)
if "❌" in init_result:
return init_result, "", self.log_message("")
try:
self.engine.project_config["prompt"] = prompt
start_time = time.time()
self.engine.step_1_plan_video()
elapsed = time.time() - start_time
self.log_message(f"⏱️ Plan generation completed in {elapsed:.2f}s")
plan = self.engine.project_config.get("video_plan", {})
if plan:
scenes_count = len(plan.get('scenes', []))
self.log_message(f"βœ… Plan created with {scenes_count} scenes")
plan_text = f"**Title:** {plan.get('video_title', 'Untitled')}\n\n"
plan_text += f"**Description:** {plan.get('video_description', 'No description')}\n\n"
plan_text += "**Scenes:**\n"
for i, scene in enumerate(plan.get('scenes', []), 1):
plan_text += f"{i}. **{scene.get('scene_name', 'Unnamed Scene')}**\n"
plan_text += f" - {scene.get('description', 'No description')}\n"
narration_preview = scene.get('narration', 'No narration')[:80] + "..." if len(scene.get('narration', '')) > 80 else scene.get('narration', 'No narration')
plan_text += f" - *Narration: {narration_preview}*\n\n"
return "βœ… Video plan generated successfully!", plan_text, self.log_message("")
else:
error_msg = "❌ Failed to generate video plan"
self.log_message(error_msg)
return error_msg, "", self.log_message("")
except Exception as e:
error_msg = f"❌ Error generating plan: {str(e)[:50]}..."
self.log_message(error_msg)
return error_msg, "", self.log_message("")
def generate_full_video(self, prompt, api_key, progress=gr.Progress()):
"""Generate complete video from prompt"""
self.log_message("🎬 Starting full video generation...")
if not prompt:
error_msg = "❌ Please provide a video topic/prompt"
self.log_message(error_msg)
return error_msg, None, self.log_message("")
# Initialize engine
init_result = self.initialize_engine(api_key)
if "❌" in init_result:
return init_result, None, self.log_message("")
try:
self.engine.project_config["prompt"] = prompt
total_start_time = time.time()
# Step 1: Plan video
self.log_message("πŸ“‹ Step 1/5: Planning video...")
progress(0.2, desc="Planning video...")
self.engine.step_1_plan_video()
if not self.engine.project_config.get("video_plan"):
error_msg = "❌ Failed to generate video plan"
self.log_message(error_msg)
return error_msg, None, self.log_message("")
scenes_count = len(self.engine.project_config["video_plan"].get("scenes", []))
self.log_message(f"βœ… Plan created with {scenes_count} scenes")
# Step 2: Generate scene code
self.log_message("πŸ€– Step 2/5: Generating animation code...")
progress(0.4, desc="Generating animation code...")
self.engine.step_2_generate_scene_code()
self.log_message("βœ… Animation code generated")
# Step 3: Generate audio
self.log_message("🎡 Step 3/5: Generating audio narration...")
progress(0.6, desc="Generating audio narration...")
self.engine.step_3_generate_audio()
self.log_message("βœ… Audio generation completed")
# Step 4: Render scenes
self.log_message("🎨 Step 4/5: Rendering video scenes...")
self.log_message("ℹ️ Note: Each scene gets max 2 attempts to avoid excessive costs")
progress(0.8, desc="Rendering video scenes...")
self.engine.step_4_render_scenes()
self.log_message("βœ… Scene rendering completed")
# Step 5: Assemble final video
self.log_message("🎞️ Step 5/5: Assembling final video...")
progress(0.9, desc="Assembling final video...")
self.engine.step_5_assemble_final_video()
total_elapsed = time.time() - total_start_time
self.log_message(f"🏁 Total generation time: {total_elapsed:.1f}s")
# Find the final video file
output_dir = Path(self.current_project_dir) / "output"
video_files = list(output_dir.glob("*.mp4"))
if video_files:
final_video = str(video_files[0])
file_size = Path(final_video).stat().st_size / (1024*1024)
self.log_message(f"πŸŽ₯ Video created: {file_size:.1f}MB")
return "βœ… Video generated successfully!", final_video, self.log_message("")
else:
error_msg = "❌ Video generation completed but no output file found"
self.log_message(error_msg)
return error_msg, None, self.log_message("")
except Exception as e:
error_msg = f"❌ Error generating video: {str(e)[:50]}..."
self.log_message(error_msg)
return error_msg, None, self.log_message("")
finally:
progress(1.0, desc="Complete!")
def cleanup(self):
"""Clean up temporary files"""
if self.current_project_dir and Path(self.current_project_dir).exists():
try:
shutil.rmtree(self.current_project_dir, ignore_errors=True)
self.log_message("🧹 Temporary files cleaned up")
except Exception:
pass
EXAMPLE_PROMPTS = [
{
"title": "Pythagorean Theorem",
"prompt": "Explain the Pythagorean theorem with a visual proof using a right triangle and squares on each side",
"description": "Mathematical proof with geometric visualization"
},
{
"title": "Photosynthesis Process",
"prompt": "Show how photosynthesis works in plants, explaining the conversion of sunlight, water and CO2 into glucose",
"description": "Biological process explanation with simple diagrams"
},
{
"title": "Compound Interest",
"prompt": "Demonstrate the concept of compound interest with a visual graph showing money growth over time",
"description": "Financial concept with animated graphs"
},
{
"title": "Neural Network Basics",
"prompt": "Illustrate how a simple neural network learns, showing input nodes, hidden layers, and output",
"description": "AI/ML concept with network visualization"
},
{
"title": "Laws of Motion",
"prompt": "Explain Newton's three laws of motion with simple examples using moving objects and forces",
"description": "Physics concepts with motion animations"
},
{
"title": "Cell Division",
"prompt": "Show the process of mitosis, illustrating how one cell divides into two identical cells",
"description": "Biology process with step-by-step visuals"
},
{
"title": "Quadratic Equations",
"prompt": "Explain how to solve quadratic equations using the quadratic formula with a visual graph",
"description": "Mathematical concept with formula derivation"
},
{
"title": "Water Cycle",
"prompt": "Demonstrate the water cycle showing evaporation, condensation, precipitation and collection",
"description": "Environmental science with cyclical process"
}
]
# Initialize the Gradio engine
gradio_engine = GradioVideoEngine()
def load_example_prompt(example_choice):
"""Load selected example prompt"""
if example_choice == "Select an example...":
return ""
for example in EXAMPLE_PROMPTS:
if f"{example['title']} - {example['description']}" == example_choice:
return example['prompt']
return ""
def create_interface():
with gr.Blocks(title="LVM Video Engine - Director AI", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 🎬 LVM Video Engine - Director AI Enhanced
Create educational videos with AI-powered Manim animations! Simply describe what you want to teach,
and the AI will generate a complete animated video with narration.
**Quick Start:** Choose from our example prompts below or write your own custom prompt.
""")
with gr.Row():
with gr.Column(scale=2):
api_key_input = gr.Textbox(
label="πŸ”‘ Gemini API Key",
placeholder="Enter your Google Gemini API key",
type="password",
info="Get your API key from https://aistudio.google.com/"
)
# Example prompts selector
example_choices = ["Select an example..."] + [f"{ex['title']} - {ex['description']}" for ex in EXAMPLE_PROMPTS]
example_dropdown = gr.Dropdown(
label="πŸ“š Example Prompts",
choices=example_choices,
value="Select an example...",
info="Choose a pre-made example or write your own prompt below"
)
prompt_input = gr.Textbox(
label="πŸ“ Video Topic/Prompt",
placeholder="Describe what you want to create (e.g., 'Explain quadratic equations with examples')",
lines=3
)
with gr.Row():
plan_btn = gr.Button("πŸ“‹ Generate Plan Only", variant="secondary")
generate_btn = gr.Button("🎬 Generate Complete Video", variant="primary")
with gr.Column(scale=3):
status_output = gr.Textbox(
label="πŸ“Š Status",
lines=2,
interactive=False
)
plan_output = gr.Markdown(
label="πŸ“‹ Video Plan",
value="Video plan will appear here...",
visible=True
)
video_output = gr.Video(
label="πŸŽ₯ Generated Video",
visible=True
)
with gr.Accordion("πŸ” Process Log", open=False):
console_output = gr.Textbox(
label="Generation Log",
lines=8,
max_lines=15,
interactive=False
)
gr.Markdown("""
### πŸ’‘ Tips for Better Results:
- Be specific about the concept you want to explain
- Mention if you want mathematical proofs, step-by-step explanations, or visual demonstrations
- The AI works best with educational and mathematical content
- Videos typically take 2-5 minutes to generate depending on complexity
### πŸ› οΈ Technical Notes:
- Uses Manim for professional mathematical animations
- AI-powered scene planning and code generation
- Automatic error detection and fixing
- Text-to-speech narration generation
""")
# Event handlers
example_dropdown.change(
fn=load_example_prompt,
inputs=[example_dropdown],
outputs=[prompt_input]
)
plan_btn.click(
fn=gradio_engine.generate_video_plan,
inputs=[prompt_input, api_key_input],
outputs=[status_output, plan_output, console_output]
)
generate_btn.click(
fn=gradio_engine.generate_full_video,
inputs=[prompt_input, api_key_input],
outputs=[status_output, video_output, console_output]
)
# Cleanup on close
demo.load(lambda: gradio_engine.cleanup())
return demo
if __name__ == "__main__":
demo = create_interface()
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=False
)