Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import google.generativeai as genai | |
| import os | |
| import re | |
| import tempfile | |
| import shutil | |
| from pathlib import Path | |
| import time | |
| from moviepy.editor import * | |
| from PIL import Image, ImageDraw, ImageFont | |
| import numpy as np | |
| # Check if API key is available | |
| api_key = os.getenv("GEMINI_API_KEY") | |
| if not api_key: | |
| print("β οΈ Warning: GEMINI_API_KEY not found in environment variables") | |
| print("Please set your Gemini API key in the Hugging Face Spaces secrets") | |
| # Configure Gemini API | |
| if api_key: | |
| genai.configure(api_key=api_key) | |
| model = genai.GenerativeModel( | |
| model_name="gemini-1.5-flash", | |
| generation_config={ | |
| "temperature": 0.3, | |
| "top_p": 0.95, | |
| "top_k": 40, | |
| "max_output_tokens": 8000, | |
| } | |
| ) | |
| def create_text_clip(text, duration=3, fontsize=48, color='black', bg_color='white', size=(1280, 720)): | |
| """Create a video clip with text using PIL and MoviePy""" | |
| # Create image with text | |
| img = Image.new('RGB', size, bg_color) | |
| draw = ImageDraw.Draw(img) | |
| # Try to use a default font, fallback to basic if not available | |
| try: | |
| font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", fontsize) | |
| except: | |
| try: | |
| font = ImageFont.truetype("arial.ttf", fontsize) | |
| except: | |
| font = ImageFont.load_default() | |
| # Calculate text position (centered) | |
| bbox = draw.textbbox((0, 0), text, font=font) | |
| text_width = bbox[2] - bbox[0] | |
| text_height = bbox[3] - bbox[1] | |
| position = ((size[0] - text_width) // 2, (size[1] - text_height) // 2) | |
| # Draw text | |
| draw.text(position, text, fill=color, font=font) | |
| # Convert PIL image to numpy array | |
| img_array = np.array(img) | |
| # Create video clip | |
| clip = ImageClip(img_array, duration=duration) | |
| return clip | |
| def generate_video(topic, duration_minutes=1): | |
| if not api_key: | |
| return None, "β Error: GEMINI_API_KEY not configured. Please set it in Hugging Face Spaces secrets." | |
| try: | |
| # Step 1: Generate content plan | |
| planning_prompt = f""" | |
| Create a simple presentation plan for the topic: "{topic}" | |
| Requirements: | |
| - Create 10-11 slides maximum for {duration_minutes} minute(s) | |
| - Each slide should have ONE clear, concise message (max 10 words) | |
| - Start with a title slide | |
| - Include 3-4 key points about the topic | |
| - End with a conclusion slide | |
| - Keep text simple and readable | |
| Format your response as: | |
| Slide 1: [Title text][details in points] | |
| Slide 2: [Key point 1][details in points] | |
| Slide 3: [Key point 2][details in points] | |
| Slide 4: [Key point 3][details in points] | |
| Slide 5: [Conclusion][details in points] | |
| Example for "Python Programming": | |
| Slide 1 β Python Programming Basics | |
| What is Python? | |
| High-level, general-purpose language | |
| Popular for web, data, AI, automation, and more | |
| Slide 2 β Why Python? | |
| Versatile and widely used in multiple industries | |
| Large community and active development | |
| Open-source and free to use | |
| Slide 3 β Easy to Learn and Read | |
| English-like syntax for easy understanding | |
| Minimal boilerplate code | |
| Example: print("Hello, World!") | |
| Slide 4 β Python's Key Features | |
| Cross-platform compatibility | |
| Large standard library | |
| Object-oriented and functional programming support | |
| Interactive interpreter | |
| Slide 5 β Powerful Libraries Available | |
| Data Analysis: NumPy, Pandas | |
| Visualization: Matplotlib, Seaborn | |
| Web Development: Django, Flask | |
| Machine Learning: TensorFlow, PyTorch | |
| Slide 6 β Popular Use Cases | |
| Web and app development | |
| Data science and analytics | |
| Artificial intelligence and machine learning | |
| Game development | |
| Automation and scripting | |
| Slide 7 β Great for Beginners | |
| Clear documentation and tutorials | |
| Friendly learning curve | |
| Lots of free resources available online | |
| Slide 8 β Tools Youβll Need | |
| Python interpreter (python.org) | |
| Code editor (VS Code, PyCharm, Sublime Text) | |
| Optional: Jupyter Notebook for interactive coding | |
| Slide 9 β Start Your Python Journey! | |
| Install Python and set up your editor | |
| Practice with small programs daily | |
| Work on mini-projects to gain confidence | |
| Join coding communities for support | |
| Slide 10 β Resources to Learn Python | |
| Official Python documentation | |
| FreeCodeCamp, Real Python, W3Schools | |
| Online coding platforms (HackerRank, LeetCode) | |
| """ | |
| plan_response = model.generate_content(planning_prompt) | |
| plan = plan_response.text.strip() | |
| # Parse the slides from the plan | |
| slides = [] | |
| for line in plan.split('\n'): | |
| if line.strip() and 'Slide' in line and ':' in line: | |
| slide_text = line.split(':', 1)[1].strip() | |
| if slide_text: | |
| slides.append(slide_text) | |
| if not slides: | |
| return None, f"β Could not parse slides from plan:\n{plan}" | |
| # Create video clips | |
| clips = [] | |
| slide_duration = max(3, (duration_minutes * 60) / len(slides)) # At least 3 seconds per slide | |
| for i, slide_text in enumerate(slides): | |
| if i == 0: # Title slide | |
| clip = create_text_clip(slide_text, duration=slide_duration, fontsize=56, color='navy') | |
| else: # Content slides | |
| clip = create_text_clip(slide_text, duration=slide_duration, fontsize=42, color='darkblue') | |
| clips.append(clip) | |
| # Concatenate all clips | |
| final_video = concatenate_videoclips(clips, method="compose") | |
| # Save video | |
| timestamp = int(time.time()) | |
| output_file = f"video_{timestamp}.mp4" | |
| final_video.write_videofile( | |
| output_file, | |
| fps=24, | |
| codec='libx264', | |
| audio_codec='aac' if final_video.audio else None, | |
| temp_audiofile='temp-audio.m4a', | |
| remove_temp=True | |
| ) | |
| # Clean up | |
| final_video.close() | |
| for clip in clips: | |
| clip.close() | |
| success_msg = f"""β Video generated successfully using MoviePy! | |
| **π Slides Created:** | |
| {chr(10).join([f"{i+1}. {slide}" for i, slide in enumerate(slides)])} | |
| **π¬ Video Details:** | |
| - Duration: {duration_minutes} minute(s) | |
| - Slides: {len(slides)} | |
| - Resolution: 1280x720 | |
| - FPS: 24 | |
| **π Original Plan:** | |
| {plan} | |
| """ | |
| return output_file, success_msg | |
| except Exception as e: | |
| return None, f"β Error generating video: {str(e)}" | |
| # Create Gradio interface | |
| with gr.Blocks(title="AI Video Generator", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(""" | |
| # π¬ AI Video Generator (MoviePy Edition) | |
| Generate educational videos using AI and MoviePy! | |
| **Features:** | |
| - π€ AI-powered content generation using Gemini | |
| - π₯ Clean slide-based videos using MoviePy | |
| - β±οΈ Customizable duration (1-3 minutes) | |
| - π± Simple, professional presentation style | |
| - βοΈ Fully compatible with Hugging Face Spaces | |
| **Note:** Video generation takes 30-90 seconds. | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| topic_input = gr.Textbox( | |
| label="π Video Topic", | |
| placeholder="Enter an educational topic (e.g., 'Introduction to Python', 'Basic Math Concepts')", | |
| value="Introduction to Python Programming", | |
| lines=2 | |
| ) | |
| duration_input = gr.Slider( | |
| minimum=1, | |
| maximum=3, | |
| value=1, | |
| step=1, | |
| label="β±οΈ Duration (minutes)", | |
| info="Shorter durations generate faster" | |
| ) | |
| generate_btn = gr.Button("π Generate Video", variant="primary", size="lg") | |
| with gr.Column(scale=1): | |
| gr.Markdown(""" | |
| ### π‘ Tips for Best Results: | |
| - Use clear, educational topics | |
| - Simple topics work better | |
| - Shorter durations are faster | |
| - Educational content works best | |
| ### π― Great Topic Examples: | |
| - "Introduction to Python" | |
| - "Basic Math Operations" | |
| - "How Plants Grow" | |
| - "Understanding Colors" | |
| - "Solar System Facts" | |
| - "Healthy Eating Tips" | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| video_output = gr.Video( | |
| label="π₯ Generated Video", | |
| height=400 | |
| ) | |
| with gr.Column(): | |
| log_output = gr.Textbox( | |
| label="π Generation Details", | |
| lines=15, | |
| max_lines=25, | |
| show_copy_button=True, | |
| placeholder="Generation logs and AI-created content will appear here..." | |
| ) | |
| # Progress indicator | |
| with gr.Row(): | |
| status_text = gr.Textbox( | |
| label="π Status", | |
| value="Ready to generate video - MoviePy edition loaded", | |
| interactive=False | |
| ) | |
| def generate_with_status(topic, duration): | |
| yield None, "π Starting video generation...", "π Generating content with AI..." | |
| try: | |
| result_video, result_log = generate_video(topic, duration) | |
| if result_video: | |
| yield result_video, result_log, "β Video generated successfully!" | |
| else: | |
| yield None, result_log, "β Video generation failed" | |
| except Exception as e: | |
| yield None, f"β Error: {str(e)}", "β Generation failed with error" | |
| generate_btn.click( | |
| fn=generate_with_status, | |
| inputs=[topic_input, duration_input], | |
| outputs=[video_output, log_output, status_text], | |
| show_progress=True | |
| ) | |
| if __name__ == "__main__": | |
| print("π¬ Starting AI Video Generator with MoviePy...") | |
| demo.launch( | |
| server_name="0.0.0.0" if os.getenv("SPACE_ID") else "127.0.0.1", | |
| server_port=7860, | |
| show_error=True | |
| ) | |