INVIDEO_BASIC / app.py
gnosticdev's picture
Update app.py
9143db2 verified
raw
history blame
7.81 kB
import os
import subprocess
import requests
import gradio as gr
from moviepy.editor import *
from datetime import datetime
import logging
import re
import torch
from transformers import GPT2LMHeadModel, GPT2Tokenizer
# Configuración inicial
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
# Lista de voces válidas
VOICES = [
"es-MX-DaliaNeural", "es-ES-ElviraNeural", "es-AR-ElenaNeural",
"es-MX-JorgeNeural", "es-ES-AlvaroNeural", "es-AR-TomasNeural",
"en-US-JennyNeural", "fr-FR-DeniseNeural", "de-DE-KatjaNeural",
"it-IT-ElsaNeural", "pt-BR-FranciscaNeural", "ja-JP-NanamiNeural",
"en-GB-SoniaNeural", "es-CL-CatalinaNeural", "es-CO-GonzaloNeural"
]
# Cargar modelo y tokenizador de GPT-2 en español
try:
tokenizer = GPT2Tokenizer.from_pretrained("datificate/gpt2-small-spanish")
model = GPT2LMHeadModel.from_pretrained("datificate/gpt2-small-spanish")
logger.info("Modelo GPT-2 en español cargado correctamente")
except Exception as e:
logger.error(f"Error cargando el modelo: {str(e)}")
model = None
tokenizer = None
def generar_guion_largo(tema, custom_script=None):
"""Genera un texto largo sobre el tema usando GPT-2"""
if custom_script:
return custom_script
if model is None or tokenizer is None:
return f"Texto generado automáticamente sobre {tema}. " * 50
try:
# Prompt directo como solicitaste
prompt = f"Escribe un texto largo y detallado sobre {tema}"
inputs = tokenizer(prompt, return_tensors="pt", max_length=512, truncation=True)
# Generar texto
outputs = model.generate(
inputs.input_ids,
max_length=800,
temperature=0.9,
top_k=50,
top_p=0.95,
num_return_sequences=1,
pad_token_id=tokenizer.eos_token_id
)
guion = tokenizer.decode(outputs[0], skip_special_tokens=True)
# Limpiar texto
guion = re.sub(r'<.*?>', '', guion)
guion = re.sub(r'\n+', '\n', guion)
logger.info(f"Guion generado: {len(guion)} caracteres")
return guion
except Exception as e:
logger.error(f"Error generando guion: {str(e)}")
return f"Texto generado automáticamente sobre {tema}. " * 50
def buscar_videos_pexels(tema, num_videos=4):
"""Busca videos en Pexels usando el tema directamente"""
try:
headers = {"Authorization": PEXELS_API_KEY}
logger.info(f"Buscando videos para: {tema}")
response = requests.get(
f"https://api.pexels.com/videos/search?query={tema}&per_page={num_videos}",
headers=headers,
timeout=15
)
videos = response.json().get("videos", [])
# Filtrar videos de alta calidad
return sorted(
videos,
key=lambda x: (x.get('width', 0) * x.get('height', 0), x.get('duration', 0)),
reverse=True
)[:num_videos]
except Exception as e:
logger.error(f"Error buscando videos: {str(e)}")
return []
def descargar_video(url, output_path):
"""Descarga un video de manera eficiente"""
try:
with requests.get(url, stream=True, timeout=30) as r:
r.raise_for_status()
with open(output_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
return True
except Exception as e:
logger.error(f"Error descargando video: {str(e)}")
return False
def crear_video(prompt, custom_script, voz_seleccionada, musica=None):
try:
# 1. Generar guion largo
guion = generar_guion_largo(prompt, custom_script)
# 2. Generar narración
voz_archivo = "voz.mp3"
subprocess.run([
'edge-tts',
'--voice', voz_seleccionada,
'--text', guion,
'--write-media', voz_archivo
], check=True)
# 3. Procesar audio principal
audio = AudioFileClip(voz_archivo)
duracion_total = audio.duration
# 4. Buscar videos relevantes
videos_data = buscar_videos_pexels(prompt)
if not videos_data:
logger.warning("No se encontraron videos. Usando videos genéricos...")
videos_data = buscar_videos_pexels("nature")
# 5. Descargar y preparar videos
clips = []
for i, video in enumerate(videos_data):
try:
# Seleccionar la mejor calidad disponible
video_file = max(
video['video_files'],
key=lambda x: x.get('width', 0) * x.get('height', 0)
)
video_url = video_file['link']
temp_path = f"temp_video_{i}.mp4"
if descargar_video(video_url, temp_path):
clip = VideoFileClip(temp_path)
# Calcular duración proporcional para cada clip
duracion_clip = duracion_total / len(videos_data)
# Ajustar clip a la duración requerida
if clip.duration < duracion_clip:
clip = clip.loop(duration=duracion_clip)
else:
clip = clip.subclip(0, duracion_clip)
clips.append(clip)
except Exception as e:
logger.error(f"Error procesando video {i}: {str(e)}")
if not clips:
raise Exception("No se pudieron cargar videos válidos")
# 6. Combinar videos
video_final = concatenate_videoclips(clips)
video_final = video_final.set_audio(audio)
# 7. Exportar video final
output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
video_final.write_videofile(
output_path,
fps=24,
codec="libx264",
audio_codec="aac",
threads=4,
preset='fast'
)
logger.info(f"Video generado exitosamente: {output_path}")
return output_path
except Exception as e:
logger.error(f"ERROR: {str(e)}")
return None
finally:
# Limpieza garantizada
if os.path.exists(voz_archivo):
os.remove(voz_archivo)
for i in range(5):
temp_file = f"temp_video_{i}.mp4"
if os.path.exists(temp_file):
os.remove(temp_file)
# Interfaz simplificada y funcional
with gr.Blocks(title="Generador de Videos") as app:
gr.Markdown("# 🎬 GENERADOR AUTOMÁTICO DE VIDEOS")
with gr.Row():
with gr.Column():
prompt = gr.Textbox(label="Tema del video", placeholder="Ej: 'La historia de la inteligencia artificial'")
custom_script = gr.TextArea(
label="Guion personalizado (opcional)",
placeholder="Pega tu guion completo aquí...",
lines=5
)
voz = gr.Dropdown(label="Voz Narradora", choices=VOICES, value=VOICES[0])
musica = gr.File(label="Música de fondo (opcional)", file_types=["audio"])
btn = gr.Button("Generar Video", variant="primary")
with gr.Column():
output = gr.Video(label="Video Resultado", format="mp4")
btn.click(
fn=crear_video,
inputs=[prompt, custom_script, voz, musica],
outputs=output
)
if __name__ == "__main__":
app.launch(server_name="0.0.0.0", server_port=7860)