INVIDEO_BASIC / app.py
gnosticdev's picture
Update app.py
9b7097e verified
raw
history blame
3.82 kB
import os
import requests
import asyncio
import tempfile
import logging
from datetime import datetime
import gradio as gr
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip
import edge_tts
from transformers import pipeline
import torch
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# --- CONFIG ---
PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
# Modelo de texto en español
MODEL_NAME = "DeepESP/gpt2-spanish"
# Inicializar generador de texto
device = 0 if torch.cuda.is_available() else -1
generator = pipeline("text-generation", model=MODEL_NAME, device=device)
async def listar_voces():
try:
return await edge_tts.list_voices()
except Exception as e:
logger.error(f"Error obteniendo voces: {e}")
return [{'ShortName': 'es-ES-ElviraNeural', 'Name': 'Elvira', 'Gender': 'Female'}]
VOCES = asyncio.run(listar_voces())
VOICE_NAMES = [f"{v['Name']} ({v['Gender']})" for v in VOCES]
def generar_guion(prompt):
prompt_text = f"Escribe un guion profesional para un vídeo de YouTube sobre '{prompt}':\n"
try:
out = generator(prompt_text, max_new_tokens=300, temperature=0.7, truncate=True, num_return_sequences=1)
texto = out[0]['generated_text']
return texto
except Exception as e:
logger.error(f"Error generando guion: {e}")
return f"Error generando guion: {e}"
def buscar_videos(prompt, max_videos=3):
try:
url = f"https://api.pexels.com/videos/search?query={prompt}&per_page={max_videos}"
resp = requests.get(url, headers={"Authorization": PEXELS_API_KEY}, timeout=10)
return resp.json().get("videos", [])
except Exception as e:
logger.error(f"Error en Pexels: {e}")
return []
async def crear_video(prompt, voz_index, musica_path=None):
texto = generar_guion(prompt)
voz_short = VOCES[voz_index]['ShortName']
audio_file = "tts.mp3"
await edge_tts.Communicate(texto, voz_short).save(audio_file)
tts_audio = AudioFileClip(audio_file)
dur = tts_audio.duration
# Música opcional
if musica_path:
music = AudioFileClip(musica_path).volumex(0.2).subclip(0, dur)
audio_comp = CompositeAudioClip([music, tts_audio])
else:
audio_comp = tts_audio
videos = buscar_videos(prompt)
if not videos:
return None
clips = []
for v in videos:
url = v['video_files'][0]['link']
r = requests.get(url, stream=True, timeout=15)
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
[tmp.write(c) for c in r.iter_content(1024*1024)]
tmp.close()
clip = VideoFileClip(tmp.name).subclip(0, min(dur/len(videos), v['duration']))
clips.append(clip)
final = concatenate_videoclips(clips).set_audio(audio_comp)
out_name = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
final.write_videofile(out_name, codec="libx264", audio_codec="aac", fps=24)
# Cleanup
tts_audio.close()
for c in clips:
c.close()
os.remove(c.filename)
os.remove(audio_file)
return out_name
def run_crear(prompt, voz_name, muz_file):
try:
idx = VOICE_NAMES.index(voz_name)
except:
idx = 0
return asyncio.run(crear_video(prompt, idx, muz_file.name if muz_file else None))
with gr.Blocks() as app:
tema = gr.Textbox(label="Tema para guion y video")
voz = gr.Dropdown(choices=VOICE_NAMES, value=VOICE_NAMES[0], label="Voz TTS")
muz = gr.File(label="Música fondo opcional (mp3/wav)", file_types=[".mp3", ".wav"])
btn = gr.Button("Crear video")
vid_out = gr.Video(label="Video generado")
btn.click(fn=run_crear, inputs=[tema, voz, muz], outputs=vid_out)
if __name__ == "__main__":
app.launch()