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