import os import requests import edge_tts import gradio as gr from moviepy.editor import * from PIL import Image import io import asyncio import json from openai import OpenAI # Configura APIs (gratis) client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) # Para GPT-3.5-turbo PEXELS_API_KEY = os.getenv("PEXELS_API_KEY") # 1. Generar guion automático con IA (si el usuario no proporciona uno) async def generate_script(topic): prompt = f""" Genera un guion para un video de YouTube sobre '{topic}'. Formato JSON ejemplo: [ {{"prompt": "imagen de ejemplo", "text": "narración correspondiente"}}, ... ] """ response = client.chat.completions.create( model="gpt-3.5-turbo", messages=[{"role": "user", "content": prompt}], temperature=0.7 ) return response.choices[0].message.content # 2. Descargar imágenes de Pexels/Unsplash def get_stock_media(query): url = f"https://api.pexels.com/v1/photos/search?query={query}&per_page=1" headers = {"Authorization": PEXELS_API_KEY} response = requests.get(url, headers=headers).json() image_url = response["photos"][0]["src"]["large"] return Image.open(io.BytesIO(requests.get(image_url).content)) # 3. Generar voz con Edge TTS async def generate_voice(text, voice="es-ES-AlvaroNeural"): communicate = edge_tts.Communicate(text=text, voice=voice) await communicate.save("voice.mp3") return AudioFileClip("voice.mp3") # 4. Crear video final async def create_video(script_json, voice_model, music_file=None): try: script = json.loads(script_json) except json.JSONDecodeError: raise gr.Error("¡Error en el formato del guion! Usa JSON válido.") clips = [] for i, scene in enumerate(script): img = get_stock_media(scene["prompt"]) img.save(f"scene_{i}.jpg") audio = await generate_voice(scene["text"], voice_model) clip = ImageClip(f"scene_{i}.jpg").set_duration(audio.duration) # Subtítulos dinámicos text_clip = TextClip( scene["text"], fontsize=30, color="white", stroke_color="black", size=(clip.w * 0.9, None), method="caption" ).set_position(("center", "bottom")).set_duration(audio.duration) clips.append(CompositeVideoClip([clip, text_clip]).set_audio(audio)) final_video = concatenate_videoclips(clips) if music_file: music = AudioFileClip(music_file).volumex(0.2) final_video.audio = CompositeAudioClip([final_video.audio, music.set_start(0)]) output_path = "final_video.mp4" final_video.write_videofile(output_path, fps=24, codec="libx264") return output_path # 5. Interfaz Gradio (2 modos: automático o manual) with gr.Blocks() as demo: gr.Markdown("## 🎥 Generador de Videos con IA (Modo Automático o Manual)") with gr.Tab("Modo Automático"): topic_input = gr.Textbox(label="Tema del video (ej: 'Top 10 misterios del mundo')") auto_voice = gr.Dropdown(label="Voz", choices=["es-ES-AlvaroNeural", "en-US-JennyNeural"]) generate_auto_btn = gr.Button("Generar Guion y Video") with gr.Tab("Modo Manual"): script_input = gr.Textbox( label="Pega tu guion (JSON)", placeholder='[{"prompt": "ciudad futurista", "text": "Bienvenidos al futuro..."}]', lines=10 ) manual_voice = gr.Dropdown(label="Voz", choices=["es-ES-AlvaroNeural", "en-US-JennyNeural"]) music_upload = gr.File(label="Música de fondo (opcional)", type="filepath") generate_manual_btn = gr.Button("Generar Video") output_video = gr.Video(label="Video Generado", format="mp4") # Modo Automático: Generar guion + video async def auto_mode(topic, voice): script = await generate_script(topic) return await create_video(script, voice) # Modo Manual: Usar guion existente async def manual_mode(script, voice, music): return await create_video(script, voice, music) generate_auto_btn.click( fn=auto_mode, inputs=[topic_input, auto_voice], outputs=output_video ) generate_manual_btn.click( fn=manual_mode, inputs=[script_input, manual_voice, music_upload], outputs=output_video ) demo.launch()