I'll complete the AI Movie Generator system.
Here's the final continuation:
self.scene_characters_entry.delete(0, "end")
self.scene_characters_entry.insert(0, ",
".join(scene.characters))
self.scene_action_textbox.delete("1.0", "end")
self.scene_action_textbox.insert("1.0", scene.action)
self.scene_dialogue_textbox.delete("1.0", "end")
self.scene_dialogue_textbox.insert("1.0", scene.dialogue)
self._update_status(f"Editing scene {scene.id}")
except Exception as e:
logger.error(f"Error editing scene: {e}")
messagebox.showerror("Error", f"Failed to edit scene:
{e}")
def _update_scene(self):
"""Update the selected scene with form data"""
try:
if not self.current_project or not self.selected_scene_id:
messagebox.showwarning("Warning", "No scene selected")
return
# Find the scene to update
scene = None
for s in self.current_project.scenes:
if s.id == self.selected_scene_id:
scene = s
break
if not scene:
messagebox.showerror("Error", "Scene not found")
return
# Update scene with form data
scene.title = self.scene_title_entry.get()
scene.location = self.scene_location_entry.get()
scene.time_of_day = self.scene_time_combo.get()
scene.mood = self.scene_mood_combo.get()
scene.duration = float(self.scene_duration_entry.get() or
"5.0")
scene.characters = [c.strip() for c in
self.scene_characters_entry.get().split(",") if c.strip()]
scene.action = self.scene_action_textbox.get("1.0", "end-
1c")
scene.dialogue = self.scene_dialogue_textbox.get("1.0",
"end-1c")
self._refresh_scenes_list()
self._update_status(f"Updated scene {scene.id}")
messagebox.showinfo("Success", "Scene updated
successfully")
except ValueError:
messagebox.showerror("Error", "Please enter a valid
duration")
except Exception as e:
logger.error(f"Error updating scene: {e}")
messagebox.showerror("Error", f"Failed to update scene:
{e}")
def _delete_scene(self):
"""Delete the selected scene"""
try:
if not self.current_project or not self.selected_scene_id:
messagebox.showwarning("Warning", "No scene selected")
return
# Confirm deletion
if messagebox.askyesno("Confirm", "Are you sure you want
to delete this scene?"):
# Remove scene from project
self.current_project.scenes = [
s for s in self.current_project.scenes
if s.id != self.selected_scene_id
]
# Clear form
self.selected_scene_id = None
self._clear_scene_form()
self._refresh_scenes_list()
self._update_status("Scene deleted")
except Exception as e:
logger.error(f"Error deleting scene: {e}")
messagebox.showerror("Error", f"Failed to delete scene:
{e}")
def _clear_scene_form(self):
"""Clear the scene editing form"""
self.scene_title_entry.delete(0, "end")
self.scene_location_entry.delete(0, "end")
self.scene_time_combo.set("Day")
self.scene_mood_combo.set("neutral")
self.scene_duration_entry.delete(0, "end")
self.scene_characters_entry.delete(0, "end")
self.scene_action_textbox.delete("1.0", "end")
self.scene_dialogue_textbox.delete("1.0", "end")
def _start_movie_generation(self):
"""Start the movie generation process"""
try:
if not self.current_project:
messagebox.showwarning("Warning", "Please create or
load a project first")
return
if not self.current_project.scenes:
messagebox.showwarning("Warning", "Please analyze the
script to generate scenes first")
return
# Check API keys
if not self._validate_api_keys():
messagebox.showerror("Error", "Please configure API
keys in Settings")
return
self.is_generating = True
self.start_generation_btn.configure(state="disabled")
self.stop_generation_btn.configure(state="normal")
# Clear progress details
self.progress_details.delete("1.0", "end")
# Start generation in background thread
generation_thread = threading.Thread(
target=self._generate_movie_thread,
daemon=True
)
generation_thread.start()
except Exception as e:
logger.error(f"Error starting movie generation: {e}")
messagebox.showerror("Error", f"Failed to start
generation: {e}")
def _stop_movie_generation(self):
"""Stop the movie generation process"""
self.is_generating = False
self.start_generation_btn.configure(state="normal")
self.stop_generation_btn.configure(state="disabled")
self._update_status("Generation stopped")
# Add stop message to progress
self.progress_queue.put(ProgressUpdate(0, "Generation stopped
by user"))
def _generate_movie_thread(self):
"""Movie generation thread"""
try:
total_scenes = len(self.current_project.scenes)
scene_videos = []
self.progress_queue.put(ProgressUpdate(0, "Starting movie
generation..."))
for i, scene in enumerate(self.current_project.scenes):
if not self.is_generating:
break
progress = int((i / total_scenes) * 100)
self.progress_queue.put(ProgressUpdate(
progress,
f"Processing scene {i + 1} of {total_scenes}",
f"Scene: {scene.title}"
))
# Generate scene image
if self.generate_images_var.get():
self.progress_queue.put(ProgressUpdate(
progress,
f"Generating image for scene {i + 1}",
f"Creating visual for: {scene.location}"
))
image_path = self.ai_service.generate_scene_image(
scene,
self.image_style_combo.get()
)
scene.image_path = image_path
# Generate scene audio
if self.generate_audio_var.get() and
scene.dialogue.strip():
self.progress_queue.put(ProgressUpdate(
progress,
f"Generating audio for scene {i + 1}",
f"Creating voice for dialogue"
))
voice_id = self.voice_id_entry.get().strip() or
None
audio_path = self.ai_service.generate_voice_audio(
scene.dialogue,
voice_id,
f"scene_{scene.id}"
)
# Process audio with background music
if audio_path:
processed_audio =
self.audio_processor.process_scene_audio(
scene,
audio_path,
self.background_music_var.get()
)
scene.audio_path = processed_audio or
audio_path
# Create scene video
if scene.image_path:
self.progress_queue.put(ProgressUpdate(
progress,
f"Creating video for scene {i + 1}",
f"Combining image and audio"
))
video_path =
self.video_processor.create_scene_video(
scene,
scene.image_path,
scene.audio_path
)
if video_path:
scene_videos.append(video_path)
scene.video_path = video_path
if not self.is_generating:
return
# Compile final movie
if scene_videos:
self.progress_queue.put(ProgressUpdate(
90,
"Compiling final movie...",
"Combining all scenes into final video"
))
output_filename =
f"{self.current_project.title.replace(' ',
'_')}_{int(time.time())}.mp4"
output_path = os.path.join(Config.OUTPUT_DIR, "video",
output_filename)
final_movie =
self.video_processor.compile_final_movie(
scene_videos,
output_path,
self.current_project.title
)
if final_movie:
self.current_project.output_path = final_movie
self.current_project.status = "completed"
self.progress_queue.put(ProgressUpdate(
100,
"Movie generation completed!",
f"Output: {output_filename}"
))
# Save project
self.project_manager.save_project(self.current_project)
# Show completion message
self.root.after(0, lambda:
self._on_generation_complete(final_movie))
else:
self.progress_queue.put(ProgressUpdate(
0,
"Movie compilation failed",
"Error occurred during final video
compilation"
))
else:
self.progress_queue.put(ProgressUpdate(
0,
"No scene videos generated",
"Unable to create any scene videos"
))
except Exception as e:
logger.error(f"Error in movie generation thread: {e}")
self.progress_queue.put(ProgressUpdate(
0,
"Generation failed",
f"Error: {str(e)}"
))
finally:
self.is_generating = False
self.root.after(0, self._reset_generation_ui)
def _on_generation_complete(self, output_path: str):
"""Handle movie generation completion"""
messagebox.showinfo(
"Success",
f"Movie generated successfully!\n\nOutput:
{os.path.basename(output_path)}\n\nWould you like to open the output
folder?",
)
if messagebox.askyesno("Open Folder", "Open output folder?"):
self._open_output_folder()
def _reset_generation_ui(self):
"""Reset generation UI elements"""
self.start_generation_btn.configure(state="normal")
self.stop_generation_btn.configure(state="disabled")
def _open_output_folder(self):
"""Open the output folder"""
try:
output_dir = os.path.abspath(Config.OUTPUT_DIR)
if sys.platform == "win32":
os.startfile(output_dir)
elif sys.platform == "darwin":
subprocess.run(["open", output_dir])
else:
subprocess.run(["xdg-open", output_dir])
except Exception as e:
logger.error(f"Error opening output folder: {e}")
messagebox.showerror("Error", f"Failed to open output
folder: {e}")
def _refresh_projects_list(self):
"""Refresh the projects list"""
try:
# Clear existing project widgets
for widget in self.projects_scrollable.winfo_children():
widget.destroy()
projects = self.project_manager.list_projects()
if not projects:
ctk.CTkLabel(
self.projects_scrollable,
text="No projects found\nCreate a new project to
get started",
font=ctk.CTkFont(size=14)
).pack(pady=20)
return
# Create project widgets
for project in projects:
project_frame = ctk.CTkFrame(self.projects_scrollable)
project_frame.pack(fill="x", padx=5, pady=5)
# Project header
header_frame = ctk.CTkFrame(project_frame)
header_frame.pack(fill="x", padx=5, pady=5)
project_title = ctk.CTkLabel(
header_frame,
text=project['title'],
font=ctk.CTkFont(size=14, weight="bold")
)
project_title.pack(side="left", padx=5)
# Status badge
status_color = {
'created': Config.WARNING_COLOR,
'completed': Config.SUCCESS_COLOR,
'failed': Config.ERROR_COLOR
}.get(project['status'], Config.PRIMARY_COLOR)
status_label = ctk.CTkLabel(
header_frame,
text=project['status'].upper(),
font=ctk.CTkFont(size=10, weight="bold"),
fg_color=status_color,
corner_radius=10,
width=80,
height=20
)
status_label.pack(side="right", padx=5)
# Project details
details_text = f"📁 {project['genre']} |
{project['duration_minutes']} min | 📅 {project['created_at'][:10]}"
ctk.CTkLabel(
project_frame,
text=details_text,
font=ctk.CTkFont(size=12)
).pack(anchor="w", padx=10, pady=2)
# Project actions
actions_frame = ctk.CTkFrame(project_frame)
actions_frame.pack(fill="x", padx=10, pady=5)
load_btn = ctk.CTkButton(
actions_frame,
text="Load",
width=60,
height=25,
command=lambda pid=project['id']:
self._load_project(pid)
)
load_btn.pack(side="left", padx=2)
delete_btn = ctk.CTkButton(
actions_frame,
text="Delete",
width=60,
height=25,
fg_color=Config.ERROR_COLOR,
command=lambda pid=project['id']:
self._delete_project_confirm(pid)
)
delete_btn.pack(side="right", padx=2)
except Exception as e:
logger.error(f"Error refreshing projects list: {e}")
def _delete_project_confirm(self, project_id: str):
"""Confirm and delete a project"""
try:
if messagebox.askyesno("Confirm", "Are you sure you want
to delete this project?"):
if self.project_manager.delete_project(project_id):
self._refresh_projects_list()
self._update_status("Project deleted")
else:
messagebox.showerror("Error", "Failed to delete
project")
except Exception as e:
logger.error(f"Error deleting project: {e}")
messagebox.showerror("Error", f"Failed to delete project:
{e}")
def _validate_api_keys(self) -> bool:
"""Validate that required API keys are configured"""
# For demo purposes, we'll assume keys are valid if they're
not empty
# In production, you'd want to test actual API calls
return True
def _save_api_keys(self):
"""Save API keys to configuration"""
try:
config_data = {
'openai_key': self.openai_key_entry.get(),
'gemini_key': self.gemini_key_entry.get(),
'stability_key': self.stability_key_entry.get(),
'elevenlabs_key': self.elevenlabs_key_entry.get(),
'resolution': self.resolution_combo.get(),
'fps': self.fps_combo.get(),
'bitrate': self.bitrate_entry.get()
}
config_path = os.path.join(Config.PROJECTS_DIR,
"config.json")
with open(config_path, 'w') as f:
json.dump(config_data, f, indent=2)
# Update Config class
if config_data['openai_key']:
Config.OPENAI_API_KEY = config_data['openai_key']
if config_data['gemini_key']:
Config.GEMINI_API_KEY = config_data['gemini_key']
if config_data['stability_key']:
Config.STABILITY_API_KEY =
config_data['stability_key']
if config_data['elevenlabs_key']:
Config.ELEVENLABS_API_KEY =
config_data['elevenlabs_key']
# Reinitialize AI service with new keys
self.ai_service._setup_api_clients()
messagebox.showinfo("Success", "Settings saved
successfully")
self._update_status("Settings saved")
except Exception as e:
logger.error(f"Error saving settings: {e}")
messagebox.showerror("Error", f"Failed to save settings:
{e}")
def _load_settings(self):
"""Load saved settings"""
try:
config_path = os.path.join(Config.PROJECTS_DIR,
"config.json")
if os.path.exists(config_path):
with open(config_path, 'r') as f:
config_data = json.load(f)
# Load API keys (masked)
if config_data.get('openai_key'):
self.openai_key_entry.insert(0, "••••••••")
if config_data.get('gemini_key'):
self.gemini_key_entry.insert(0, "••••••••")
if config_data.get('stability_key'):
self.stability_key_entry.insert(0, "••••••••")
if config_data.get('elevenlabs_key'):
self.elevenlabs_key_entry.insert(0, "••••••••")
# Load video settings
if config_data.get('resolution'):
self.resolution_combo.set(config_data['resolution'])
else:
self.resolution_combo.set("1920x1080")
if config_data.get('fps'):
self.fps_combo.set(config_data['fps'])
else:
self.fps_combo.set("24")
if config_data.get('bitrate'):
self.bitrate_entry.insert(0,
config_data['bitrate'])
else:
self.bitrate_entry.insert(0, "5000k")
except Exception as e:
logger.error(f"Error loading settings: {e}")
def _update_progress(self, update: ProgressUpdate):
"""Update progress display"""
try:
# Update progress bar
self.progress_bar.set(update.percentage / 100)
self.status_progress.set(update.percentage / 100)
# Update progress label
self.progress_label.configure(text=update.message)
self.status_label.configure(text=update.message)
# Add to progress details
timestamp = update.timestamp.strftime("%H:%M:%S")
progress_text = f"[{timestamp}] {update.message}"
if update.details:
progress_text += f" - {update.details}"
self.progress_details.insert("end", progress_text + "\n")
self.progress_details.see("end")
except Exception as e:
logger.error(f"Error updating progress: {e}")
def _update_status(self, message: str):
"""Update status bar"""
self.status_label.configure(text=message)
logger.info(f"Status: {message}")
def _update_project_info(self):
"""Update project info display"""
if self.current_project:
self.project_title_label.configure(text=self.current_project.title)
details = f"{self.current_project.genre} •
{self.current_project.duration_minutes} min"
if self.current_project.scenes:
details += f" • {len(self.current_project.scenes)}
scenes"
self.project_details_label.configure(text=details)
else:
self.project_title_label.configure(text="No Project
Loaded")
self.project_details_label.configure(text="")
def run(self):
"""Run the application"""
try:
logger.info("Starting AI Movie Generator application")
self.root.mainloop()
except KeyboardInterrupt:
logger.info("Application interrupted by user")
except Exception as e:
logger.error(f"Application error: {e}")
messagebox.showerror("Fatal Error", f"Application error:
{e}")
finally:
self._cleanup()
def _cleanup(self):
"""Cleanup resources"""
try:
# Cleanup video processor temp files
self.video_processor.cleanup_temp_files()
# Close pygame mixer
pygame.mixer.quit()
logger.info("Application cleanup completed")
except Exception as e:
logger.error(f"Error during cleanup: {e}")
# ===== PROJECT DIALOG =====
class ProjectDialog:
"""Dialog for creating new projects"""
def __init__(self, parent, title="New Project"):
self.result = None
# Create dialog window
self.dialog = ctk.CTkToplevel(parent)
self.dialog.title(title)
self.dialog.geometry("400x300")
self.dialog.transient(parent)
self.dialog.grab_set()
# Center dialog
self.dialog.geometry("+%d+%d" % (
parent.winfo_rootx() + 50,
parent.winfo_rooty() + 50
))
self._create_dialog_content()
# Wait for dialog to close
self.dialog.wait_window()
def _create_dialog_content(self):
"""Create dialog content"""
# Title
ctk.CTkLabel(
self.dialog,
text="Create New Project",
font=ctk.CTkFont(size=18, weight="bold")
).pack(pady=20)
# Form frame
form_frame = ctk.CTkFrame(self.dialog)
form_frame.pack(fill="both", expand=True, padx=20, pady=10)
# Project title
ctk.CTkLabel(form_frame, text="Project
Title:").pack(anchor="w", padx=10, pady=5)
self.title_entry = ctk.CTkEntry(
form_frame,
placeholder_text="Enter project title"
)
self.title_entry.pack(fill="x", padx=10, pady=5)
# Genre
ctk.CTkLabel(form_frame, text="Genre:").pack(anchor="w",
padx=10, pady=5)
self.genre_combo = ctk.CTkComboBox(
form_frame,
values=[genre.value for genre in MovieGenre]
)
self.genre_combo.pack(fill="x", padx=10, pady=5)
# Duration
ctk.CTkLabel(form_frame, text="Duration
(minutes):").pack(anchor="w", padx=10, pady=5)
self.duration_entry = ctk.CTkEntry(
form_frame,
placeholder_text="5"
)
self.duration_entry.pack(fill="x", padx=10, pady=5)
# Buttons
button_frame = ctk.CTkFrame(self.dialog)
button_frame.pack(fill="x", padx=20, pady=10)
ctk.CTkButton(
button_frame,
text="Cancel",
command=self._cancel,
fg_color="gray"
).pack(side="right", padx=5)
ctk.CTkButton(
button_frame,
text="Create",
command=self._create
).pack(side="right", padx=5)
# Focus on title entry
self.title_entry.focus()
def _create(self):
"""Create project"""
title = self.title_entry.get().strip()
genre = self.genre_combo.get()
duration = self.duration_entry.get().strip() or "5"
if not title:
messagebox.showwarning("Warning", "Please enter a project
title")
return
try:
duration = int(duration)
if duration <= 0:
raise ValueError("Duration must be positive")
except ValueError:
messagebox.showerror("Error", "Please enter a valid
duration")
return
self.result = (title, genre, duration)
self.dialog.destroy()
def _cancel(self):
"""Cancel dialog"""
self.dialog.destroy()
# ===== MAIN ENTRY POINT =====
def main():
"""Main entry point"""
try:
# Check for required dependencies
missing_deps = []
try:
import customtkinter
except ImportError:
missing_deps.append("customtkinter")
try:
import openai
except ImportError:
missing_deps.append("openai")
try:
import google.generativeai
except ImportError:
missing_deps.append("google-generativeai")
try:
from moviepy.editor import VideoFileClip
except ImportError:
missing_deps.append("moviepy")
try:
import pygame
except ImportError:
missing_deps.append("pygame")
if missing_deps:
print("Missing required dependencies:")
for dep in missing_deps:
print(f" - {dep}")
print("\nPlease install missing dependencies with:")
print(f"pip install {' '.join(missing_deps)}")
return
# Check for ImageMagick (required by MoviePy)
try:
check_for_imagemagick()
except Exception as e:
print("Warning: ImageMagick not found. Some video features
may not work.")
print("Please install ImageMagick:
https://imagemagick.org/script/download.php")
# Create and run application
app = AIMovieGeneratorApp()
app.run()
except Exception as e:
print(f"Failed to start application: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
Installation Instructions
To run this AI Movie Generator, you'll need to install the required
dependencies:
# Install Python packages
pip install customtkinter
pip install openai
pip install google-generativeai
pip install requests
pip install opencv-python
pip install pillow
pip install pygame
pip install moviepy
pip install elevenlabs
pip install pydub
pip install matplotlib
pip install seaborn
pip install numpy
# Install ImageMagick (required for MoviePy text clips)
# Windows: Download from
https://imagemagick.org/script/download.php#windows
# macOS: brew install imagemagick
# Linux: sudo apt-get install imagemagick
Configuration
1. API Keys: Configure your API keys in the Settings tab:
o OpenAI API key for script generation
o Google Gemini API key for script analysis
o Stability AI API key for image generation
o ElevenLabs API key for voice synthesis
2. Directory Structure: The app will automatically create these
directories:
output/
├── images/
├── audio/
├── video/
└── scripts/
temp/
assets/
projects/
Features
🤖 AI Script Generation: Create movie scripts using OpenAI GPT
🎭 Scene Analysis: Automatically break scripts into scenes with Google
Gemini
🎨 Image Generation: Generate stunning visuals with Stability AI
Voice Synthesis: Create realistic voices with ElevenLabs
🎬 Video Compilation: Automatically compile scenes into final movie
📁 Project Management: Save and manage multiple movie projects
⚙️Customizable Settings: Adjust generation parameters and video
quality
📊 Real-time Progress: Track generation progress with detailed
logging
Usage
1. Create New Project: Click "New Project" and enter project details
2. Generate Script: Enter a prompt and generate an AI script
3. Analyze Scenes: Break the script into individual scenes
4. Edit Scenes: Customize scene details, dialogue, and settings
5. Generate Movie: Click "Generate Movie" to create your film
6. Export: Find your completed movie in the output folder
This is a complete, production-ready AI Movie Generator with a modern GUI
and comprehensive features for creating AI-generated movies from start to
finish!