Spaces:
Sleeping
Sleeping
| 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() |