Spaces:
Running
Running
import os | |
import subprocess | |
import requests | |
import gradio as gr | |
from moviepy.editor import * | |
from datetime import datetime | |
import tempfile | |
import logging | |
from transformers import pipeline | |
import nltk | |
from nltk.tokenize import sent_tokenize | |
import numpy as np | |
from sklearn.feature_extraction.text import TfidfVectorizer | |
import re | |
# Configuraci贸n inicial | |
nltk.download('punkt', quiet=True) | |
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", | |
"en-US-JennyNeural", "fr-FR-DeniseNeural", "de-DE-KatjaNeural", | |
"it-IT-ElsaNeural", "pt-BR-FranciscaNeural", "ja-JP-NanamiNeural" | |
] | |
# Inicializar el generador de texto | |
try: | |
script_generator = pipeline( | |
"text-generation", | |
model="gpt2", # Modelo m谩s flexible | |
device=0 if torch.cuda.is_available() else -1 | |
) | |
except: | |
logger.warning("No se pudo cargar el modelo de generaci贸n de texto") | |
script_generator = None | |
def generar_guion(prompt): | |
"""Genera un guion natural y extenso basado en el prompt""" | |
if script_generator: | |
try: | |
result = script_generator( | |
f"Genera un texto detallado y bien estructurado sobre '{prompt}' para un video de YouTube:", | |
max_length=500, # Texto m谩s largo | |
temperature=0.9, # M谩s creatividad | |
num_return_sequences=1 | |
) | |
guion = result[0]['generated_text'] | |
# Limpiar el guion generado | |
guion = re.sub(r'<.*?>', '', guion) | |
guion = re.sub(r'\n+', '\n', guion) | |
return guion.strip() | |
except Exception as e: | |
logger.error(f"Error generando guion: {str(e)}") | |
# Fallback natural | |
return f"En este video exploraremos en profundidad el tema de {prompt}. " \ | |
"Analizaremos diversos aspectos y perspectivas para ofrecer una visi贸n completa. " \ | |
"Veremos c贸mo este tema se relaciona con nuestra vida cotidiana y su impacto en la sociedad actual." | |
def extraer_palabras_clave(texto, n=7): | |
"""Extrae palabras clave relevantes usando TF-IDF""" | |
# Preprocesamiento del texto | |
texto = re.sub(r'[^\w\s]', '', texto.lower()) | |
# Tokenizar en oraciones | |
oraciones = sent_tokenize(texto) | |
# Crear matriz TF-IDF | |
vectorizer = TfidfVectorizer( | |
stop_words=['el', 'la', 'los', 'las', 'de', 'en', 'y', 'que', 'un', 'una', 'con', 'para'], | |
max_features=500 | |
) | |
X = vectorizer.fit_transform(oraciones) | |
# Obtener palabras con mayor puntuaci贸n TF-IDF | |
suma_scores = np.asarray(X.sum(axis=0)).ravel() | |
indices = np.argsort(suma_scores)[::-1][:n] | |
palabras = vectorizer.get_feature_names_out() | |
return [palabras[i] for i in indices] | |
def buscar_videos_pexels(palabras_clave, num_videos=3): | |
"""Busca videos en Pexels usando palabras clave con enfoque en relevancia""" | |
try: | |
headers = {"Authorization": PEXELS_API_KEY} | |
query = "+".join(palabras_clave[:3]) # Usar las 3 palabras m谩s relevantes | |
logger.info(f"Buscando videos con palabras clave: {query}") | |
response = requests.get( | |
f"https://api.pexels.com/videos/search?query={query}&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('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=25) 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 natural | |
guion = custom_script if custom_script else generar_guion(prompt) | |
logger.info(f"Guion generado ({len(guion)} caracteres)") | |
# 2. Extraer palabras clave del guion completo | |
palabras_clave = extraer_palabras_clave(guion) | |
logger.info(f"Palabras clave extra铆das: {', '.join(palabras_clave)}") | |
# 3. Buscar videos relevantes usando IA | |
videos_data = buscar_videos_pexels(palabras_clave) | |
if not videos_data: | |
raise Exception("No se encontraron videos relevantes. Usando backup...") | |
# 4. Generar narraci贸n | |
voz_archivo = "voz.mp3" | |
subprocess.run([ | |
'edge-tts', | |
'--voice', voz_seleccionada, | |
'--text', guion, | |
'--write-media', voz_archivo | |
], check=True) | |
# 5. Procesar audio | |
audio = AudioFileClip(voz_archivo) | |
duracion_total = audio.duration | |
# 6. Descargar y preparar videos | |
clips = [] | |
for i, video in enumerate(videos_data): | |
# Seleccionar la mejor calidad | |
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) | |
# Ajustar duraci贸n proporcional | |
duracion_clip = min(duracion_total / len(videos_data), clip.duration) | |
clips.append(clip.subclip(0, duracion_clip)) | |
# 7. Combinar videos | |
video_final = concatenate_videoclips(clips) | |
video_final = video_final.set_audio(audio) | |
# 8. Exportar | |
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=2 | |
) | |
return output_path | |
except Exception as e: | |
logger.error(f"ERROR: {str(e)}") | |
return None | |
finally: | |
# Limpieza | |
if os.path.exists(voz_archivo): | |
os.remove(voz_archivo) | |
for i in range(3): | |
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(): | |
prompt = gr.Textbox(label="Tema del video", placeholder="Ej: Exploraci贸n espacial") | |
voz = gr.Dropdown(label="Voz Narradora", choices=VOICES, value=VOICES[0]) | |
btn = gr.Button("Generar Video", variant="primary") | |
output = gr.Video(label="Resultado", format="mp4") | |
btn.click( | |
fn=crear_video, | |
inputs=[prompt, gr.Textbox(visible=False), voz, gr.File(visible=False)], | |
outputs=output | |
) | |
if __name__ == "__main__": | |
app.launch(server_name="0.0.0.0", server_port=7860) |