Spaces:
Running
Running
File size: 8,816 Bytes
43fcbe8 374c72e cbfaa69 374c72e 6d66777 374c72e 6d66777 cbfaa69 6d66777 374c72e 6d66777 374c72e 6d66777 374c72e 6d66777 374c72e 6d66777 c9d2e08 6d66777 c95f9ee 6d66777 374c72e cbfaa69 374c72e 9a06bc2 6d66777 374c72e 5654f17 6d66777 374c72e 9a06bc2 cbfaa69 374c72e 9a06bc2 374c72e 9a06bc2 374c72e 9a06bc2 6d66777 374c72e 9a06bc2 374c72e 9a06bc2 374c72e 9a06bc2 374c72e 5654f17 374c72e 9a06bc2 6d66777 374c72e 6d66777 9a06bc2 374c72e 9a06bc2 6d66777 374c72e 6d66777 374c72e 6d66777 374c72e 6d66777 374c72e 6d66777 374c72e 6d66777 374c72e 6d66777 374c72e 5654f17 9a06bc2 6d66777 374c72e 77c11ae 6d66777 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
import os
import re
import requests
import gradio as gr
from moviepy.editor import *
import edge_tts
import tempfile
import logging
from datetime import datetime
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
import nltk
from nltk.tokenize import sent_tokenize
import random
from transformers import pipeline
import torch
import asyncio
import nest_asyncio
# Aplicar patch para event loop en entornos como Jupyter o Gradio
nest_asyncio.apply()
# Configuración inicial
nltk.download('punkt', quiet=True)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Variables de configuración
PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
MODEL_NAME = "DeepESP/gpt2-spanish" # Modelo en español
# Función async para obtener voces de edge-tts
async def get_voices():
try:
voices = await edge_tts.list_voices()
return voices
except Exception as e:
logger.error(f"Error obteniendo voces: {e}")
return []
# Obtener voces sincrónicamente para inicializar
VOICES = asyncio.get_event_loop().run_until_complete(get_voices())
# Preparar lista segura de nombres de voces
VOICE_NAMES = [
f"{v.get('Name', 'Desconocido')} ({v.get('Gender', 'Desconocido')}, {v.get('LocaleName', 'es-ES')})"
for v in VOICES
]
# Fallback si no se pudieron obtener voces
if not VOICES:
VOICE_NAMES = ["Voz Predeterminada (Femenino, es-ES)"]
VOICES = [{'ShortName': 'es-ES-ElviraNeural'}]
def generar_guion_profesional(prompt):
try:
generator = pipeline(
"text-generation",
model=MODEL_NAME,
device=0 if torch.cuda.is_available() else -1
)
response = generator(
f"Escribe un guion profesional para un video de YouTube sobre '{prompt}'. "
"La estructura debe incluir:\n"
"1. Introducción atractiva\n"
"2. Tres secciones detalladas con subtítulos\n"
"3. Conclusión impactante\n"
"Usa un estilo natural para narración:",
max_length=1500,
temperature=0.7,
top_k=50,
top_p=0.95,
num_return_sequences=1,
truncation=True # Para evitar warnings y límites
)
guion = response[0]['generated_text']
if len(guion.split()) < 100:
raise ValueError("Guion demasiado breve")
return guion
except Exception as e:
logger.error(f"Error generando guion: {str(e)}")
temas = {
"historia": ["orígenes", "eventos clave", "impacto actual"],
"tecnología": ["funcionamiento", "aplicaciones", "futuro"],
"ciencia": ["teorías", "evidencia", "implicaciones"],
"misterio": ["enigma", "teorías", "explicaciones"],
"arte": ["orígenes", "características", "influencia"]
}
categoria = "general"
for key in temas:
if key in prompt.lower():
categoria = key
break
puntos_clave = temas.get(categoria, ["aspectos importantes", "datos relevantes", "conclusiones"])
return f"""
¡Hola a todos! Bienvenidos a este análisis completo sobre {prompt}.
En este video exploraremos a fondo este fascinante tema a través de tres secciones clave.
SECCIÓN 1: {puntos_clave[0].capitalize()}
Comenzaremos analizando los {puntos_clave[0]} fundamentales.
Esto nos permitirá entender mejor la base de {prompt}.
SECCIÓN 2: {puntos_clave[1].capitalize()}
En esta parte, examinaremos los {puntos_clave[1]} más relevantes
y cómo se relacionan con el tema principal.
SECCIÓN 3: {puntos_clave[2].capitalize()}
Finalmente, exploraremos las {puntos_clave[2]}
y qué significan para el futuro de este campo.
¿Listos para profundizar? ¡Empecemos!
"""
def buscar_videos_avanzado(prompt, guion, num_videos=5):
try:
oraciones = sent_tokenize(guion)
vectorizer = TfidfVectorizer(stop_words=['el', 'la', 'los', 'las', 'de', 'en', 'y', 'que'])
tfidf = vectorizer.fit_transform(oraciones)
palabras = vectorizer.get_feature_names_out()
scores = np.asarray(tfidf.sum(axis=0)).ravel()
indices_importantes = np.argsort(scores)[-5:]
palabras_clave = [palabras[i] for i in indices_importantes]
palabras_prompt = re.findall(r'\b\w{4,}\b', prompt.lower())
todas_palabras = list(set(palabras_clave + palabras_prompt))[:5]
headers = {"Authorization": PEXELS_API_KEY}
response = requests.get(
f"https://api.pexels.com/videos/search?query={'+'.join(todas_palabras)}&per_page={num_videos}",
headers=headers,
timeout=15
)
videos = response.json().get('videos', [])
logger.info(f"Palabras clave usadas: {todas_palabras}")
videos_ordenados = sorted(
videos,
key=lambda x: x.get('width', 0) * x.get('height', 0),
reverse=True
)
return videos_ordenados[:num_videos]
except Exception as e:
logger.error(f"Error en búsqueda de videos: {str(e)}")
response = requests.get(
f"https://api.pexels.com/videos/search?query={prompt}&per_page={num_videos}",
headers={"Authorization": PEXELS_API_KEY},
timeout=10
)
return response.json().get('videos', [])[:num_videos]
async def crear_video_profesional(prompt, custom_script, voz_index, musica=None):
voz_archivo = None
try:
guion = custom_script if custom_script else generar_guion_profesional(prompt)
logger.info(f"Guion generado ({len(guion.split())} palabras)")
voz_seleccionada = VOICES[voz_index]['ShortName'] if VOICES else 'es-ES-ElviraNeural'
voz_archivo = "voz.mp3"
await edge_tts.Communicate(guion, voz_seleccionada).save(voz_archivo)
audio = AudioFileClip(voz_archivo)
duracion_total = audio.duration
videos_data = buscar_videos_avanzado(prompt, guion)
if not videos_data:
raise Exception("No se encontraron videos relevantes")
clips = []
for video in videos_data[:3]:
video_files = sorted(
video['video_files'],
key=lambda x: x.get('width', 0) * x.get('height', 0),
reverse=True
)
video_url = video_files[0]['link']
response = requests.get(video_url, stream=True)
temp_video = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
for chunk in response.iter_content(chunk_size=1024 * 1024):
temp_video.write(chunk)
temp_video.close()
clip = VideoFileClip(temp_video.name)
clips.append(clip)
duracion_por_clip = duracion_total / len(clips)
clips_procesados = []
for clip in clips:
if clip.duration < duracion_por_clip:
clip = clip.loop(duration=duracion_por_clip)
else:
clip = clip.subclip(0, duracion_por_clip)
clips_procesados.append(clip)
video_final = concatenate_videoclips(clips_procesados)
if musica:
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 = CompositeAudioClip([audio, musica_clip.volumex(0.25)])
video_final = video_final.set_audio(audio)
output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
video_final.write_videofile(
output_path,
codec="libx264",
audio_codec="aac",
threads=2,
preset='fast',
fps=24
)
return output_path
except Exception as e:
logger.error(f"ERROR: {str(e)}")
return None
finally:
if voz_archivo and os.path.exists(voz_archivo):
os.remove(voz_archivo)
# Interfaz Gradio
with gr.Blocks(theme=gr.themes.Soft(), title="Generador de Videos Profesional") as app:
gr.Markdown("# 🎬 GENERADOR DE VIDEOS CON IA")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Configuración del Contenido")
prompt = gr.Textbox(label="Tema principal", placeholder="Ej: 'Los misterios de la antigua Grecia'")
custom_script = gr.TextArea(
label="Guion personalizado (opcional)",
placeholder="Pega aquí tu propio guion completo...",
|