INVIDEO_BASIC / app.py
gnosticdev's picture
Update app.py
ab6a3fb verified
raw
history blame
9.36 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
import warnings
# Configuración inicial
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Suprimir warnings específicos de transformers
warnings.filterwarnings("ignore", category=UserWarning, module="transformers")
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 con configuración correcta"""
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 con configuración corregida
outputs = model.generate(
inputs.input_ids,
max_length=800,
do_sample=True,
temperature=0.9,
top_k=50,
top_p=0.95,
num_return_sequences=1,
pad_token_id=tokenizer.eos_token_id,
# early_stopping=True # Eliminado para evitar warnings
)
guion = tokenizer.decode(outputs[0], skip_special_tokens=True)
# Limpiar texto
guion = re.sub(r'<.*?>', '', guion)
guion = re.sub(r'\n+', '\n', guion)
guion = re.sub(r'\s+', ' ', guion).strip()
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
)
if response.status_code != 200:
return []
return response.json().get("videos", [])[: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
if 'video_files' not in video or not video['video_files']:
continue
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:
# Crear video de fondo negro si no hay videos
clip = ColorClip(size=(1280, 720), color=(0, 0, 0), duration=duracion_total)
clips = [clip]
# 6. Combinar videos
final_clip = concatenate_videoclips(clips, method="compose")
final_clip = final_clip.set_audio(audio)
# 7. Aplicar música de fondo si existe
if musica:
try:
musica_clip = AudioFileClip(musica.name)
if musica_clip.duration < duracion_total:
musica_clip = musica_clip.loop(duration=duracion_total)
else:
musica_clip = musica_clip.subclip(0, duracion_total)
audio_final = CompositeAudioClip([
audio.volumex(1.0),
musica_clip.volumex(0.25)
])
final_clip = final_clip.set_audio(audio_final)
except Exception as e:
logger.error(f"Error procesando música: {str(e)}")
# 8. Exportar video final
output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
final_clip.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 mejorada
with gr.Blocks(title="Generador de Videos", theme=gr.themes.Soft()) 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 piratería en el Caribe'",
max_lines=1
)
custom_script = gr.TextArea(
label="Guion personalizado (opcional)",
placeholder="Pega tu guion completo aquí...",
lines=8,
max_lines=20
)
voz = gr.Dropdown(
label="Voz Narradora",
choices=VOICES,
value=VOICES[0],
interactive=True
)
musica = gr.File(
label="Música de fondo (opcional)",
file_types=["audio"],
type="filepath"
)
btn = gr.Button("Generar Video", variant="primary")
with gr.Column():
output = gr.Video(
label="Video Resultado",
format="mp4",
interactive=False
)
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)