import gradio as gr import edge_tts import asyncio import requests import os from moviepy.editor import ( VideoFileClip, AudioFileClip, TextClip, CompositeVideoClip, concatenate_videoclips ) # 1. Voiceover Generator async def generate_voice(text, voice="hi-IN-SwaraNeural", output_audio="reel_audio.mp3"): communicate = edge_tts.Communicate(text, voice=voice, rate="+5%") await communicate.save(output_audio) return output_audio # 2. Free Stock Video Fetcher (Pexels Public / Stock endpoint) def fetch_vertical_video(query="nature", duration_needed=10): # Free standard HD vertical clip fetcher via direct CDN sample fallback sample_urls = [ "https://assets.mixkit.co/videos/preview/mixkit-vertical-aerial-view-of-waves-crashing-41005-large.mp4", "https://assets.mixkit.co/videos/preview/mixkit-tree-branches-in-the-breeze-1188-large.mp4" ] local_video = "bg_stock.mp4" r = requests.get(sample_urls[0], stream=True) with open(local_video, "wb") as f: for chunk in r.iter_content(chunk_size=1024*1024): if chunk: f.write(chunk) return local_video # 3. Main Reel Engine def make_ai_reel(script_text, voice_choice, bg_theme): try: # Step A: Audio Generation audio_file = asyncio.run(generate_voice(script_text, voice=voice_choice)) audio = AudioFileClip(audio_file) reel_duration = audio.duration # Step B: Background Video Loop / Fit (9:16 vertical) raw_bg = fetch_vertical_video(query=bg_theme, duration_needed=reel_duration) bg_clip = VideoFileClip(raw_bg).resize(height=1920) # Center crop to 1080x1920 (Standard Reels format) w, h = bg_clip.size target_w = int(h * 9 / 16) x_center = (w - target_w) // 2 bg_clip = bg_clip.crop(x1=max(0, x_center), width=target_w, height=h).resize((1080, 1920)) # Loop if audio is longer than stock video if bg_clip.duration < reel_duration: loops = int(reel_duration // bg_clip.duration) + 1 bg_clip = concatenate_videoclips([bg_clip] * loops) bg_clip = bg_clip.subclip(0, reel_duration) # Step C: Subtitle Overlay (Dynamic text on screen) # 30-40 characters per line splitting for clean captions lines = [script_text[i:i+40] for i in range(0, len(script_text), 40)] step_time = reel_duration / max(1, len(lines)) text_clips = [] for idx, line in enumerate(lines): txt = (TextClip(line, fontsize=48, color='yellow', font='DejaVu-Sans-Bold', stroke_color='black', stroke_width=2) .set_position(('center', 1400)) .set_start(idx * step_time) .set_duration(step_time)) text_clips.append(txt) # Step D: Composite and Render final_video = CompositeVideoClip([bg_clip] + text_clips).set_audio(audio) output_reel = "final_reel_output.mp4" final_video.write_videofile( output_reel, fps=30, codec="libx264", audio_codec="aac", preset="ultrafast", threads=4 ) return output_reel except Exception as err: print("Rendering error:", err) return None # Gradio Web UI with gr.Blocks(title="AI Short Reel Studio") as demo: gr.Markdown("# 📱 AI Auto Reel Generator (9:16)") gr.Markdown("Script daaliye aur captions + cinematic background ke sath ready-to-post Reel banaiye.") with gr.Row(): with gr.Column(): script_box = gr.Textbox( label="Reel Script", lines=5, placeholder="Yahan apni 30-60 second ki reel script paste karein..." ) voice_dropdown = gr.Dropdown( choices=["hi-IN-SwaraNeural", "hi-IN-MadhurNeural", "en-US-ChristopherNeural"], value="hi-IN-SwaraNeural", label="AI Voice Select Karein" ) theme_box = gr.Textbox(label="Background Theme Keyword", value="nature", placeholder="luxury, motivation, dark...") create_btn = gr.Button("Reel Generate Karein 🚀", variant="primary") with gr.Column(): output_player = gr.Video(label="Generated 9:16 Reel", height=600) create_btn.click( fn=make_ai_reel, inputs=[script_box, voice_dropdown, theme_box], outputs=output_player ) demo.launch(share=True)