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)