gnosticdev commited on
Commit
53ae22b
verified
1 Parent(s): 693bb37

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +145 -151
app.py CHANGED
@@ -1,120 +1,136 @@
1
  import os
2
  import re
3
  import requests
4
- import numpy as np
5
  import gradio as gr
6
- from datetime import datetime
7
  from moviepy.editor import *
8
- from transformers import pipeline, AutoTokenizer, AutoModel
9
- import torch
10
- import torch.nn.functional as F
11
  import edge_tts
12
  import tempfile
13
  import logging
14
- from sklearn.metrics.pairwise import cosine_similarity
 
15
  from sklearn.feature_extraction.text import TfidfVectorizer
16
- from nltk.tokenize import sent_tokenize
17
  import nltk
 
 
 
 
18
 
19
- # Descargar recursos para NLTK
20
  nltk.download('punkt')
21
-
22
- # Configuraci贸n avanzada
23
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
24
  logger = logging.getLogger(__name__)
25
 
26
  # Configuraci贸n de modelos
27
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
28
- HF_TOKEN = os.getenv("HF_TOKEN") # Para modelos privados
29
-
30
- # 1. Modelo para generaci贸n de guiones (MBART grande para espa帽ol)
31
- script_generator = pipeline(
32
- "text2text-generation",
33
- model="facebook/mbart-large-50",
34
- tokenizer="facebook/mbart-large-50",
35
- device=0 if torch.cuda.is_available() else -1
36
- )
37
-
38
- # 2. Modelo para embeddings sem谩nticos (multiling眉e)
39
- tokenizer = AutoTokenizer.from_pretrained("sentence-transformers/paraphrase-multilingual-mpnet-base-v2")
40
- embedding_model = AutoModel.from_pretrained("sentence-transformers/paraphrase-multilingual-mpnet-base-v2")
41
 
42
- # 3. Lista de voces disponibles
43
- VOICES = [v for v in edge_tts.list_voices() if 'es' in v['ShortName'] or 'en' in v['ShortName']]
44
  VOICE_NAMES = [f"{v['Name']} ({v['Gender']}, {v['LocaleName']})" for v in VOICES]
45
 
46
- def generar_guion_avanzado(prompt):
47
- """Genera un guion largo y detallado usando IA"""
48
  try:
49
- response = script_generator(
50
- f"Escribe un guion detallado para un video de YouTube sobre '{prompt}' con introducci贸n, 3 puntos principales y conclusi贸n. Usa un estilo atractivo y profesional.",
 
 
 
 
 
 
 
 
 
 
 
 
51
  max_length=1000,
52
- num_beams=5,
53
  temperature=0.7,
54
  top_k=50,
55
  top_p=0.95,
56
- do_sample=True
57
  )
58
- return response[0]['generated_text']
 
 
 
 
 
 
 
 
59
  except Exception as e:
60
- logger.error(f"Error en generaci贸n de guion: {str(e)}")
61
- # Fallback a guion predefinido
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  return f"""
63
- 隆Hola a todos! Hoy exploraremos el fascinante tema de {prompt}.
64
- En este video cubriremos tres aspectos clave:
65
- 1. Primer aspecto importante sobre {prompt}
66
- 2. Segundo elemento crucial
67
- 3. Tercer punto que no te puedes perder
68
- 隆Quedaos hasta el final para descubrir algo incre铆ble!
 
 
 
 
 
 
 
 
 
 
69
  """
70
 
71
- def obtener_embeddings(textos):
72
- """Obtiene embeddings sem谩nticos para los textos"""
73
- inputs = tokenizer(textos, padding=True, truncation=True, return_tensors="pt", max_length=512)
74
- with torch.no_grad():
75
- outputs = embedding_model(**inputs)
76
- embeddings = outputs.last_hidden_state.mean(dim=1).cpu().numpy()
77
- return embeddings
78
-
79
- def buscar_videos_semanticos(query, guion, num_videos=5):
80
- """Busca videos usando an谩lisis sem谩ntico"""
81
  try:
82
  # Dividir el guion en oraciones
83
  oraciones = sent_tokenize(guion)
84
 
85
- # Obtener embeddings para cada oraci贸n
86
- embeddings_oraciones = obtener_embeddings(oraciones)
87
-
88
- # Embedding para la consulta general
89
- embedding_query = obtener_embeddings([query])[0]
90
-
91
- # Calcular similitud entre consulta y cada oraci贸n
92
- similitudes = cosine_similarity([embedding_query], embeddings_oraciones)[0]
93
-
94
- # Seleccionar las oraciones m谩s relevantes
95
- indices_relevantes = np.argsort(similitudes)[-3:]
96
- oraciones_relevantes = [oraciones[i] for i in indices_relevantes]
97
-
98
- # Extraer palabras clave de las oraciones relevantes
99
- vectorizer = TfidfVectorizer(stop_words=['el', 'la', 'los', 'las', 'de', 'en', 'y'])
100
- tfidf = vectorizer.fit_transform(oraciones_relevantes)
101
  palabras = vectorizer.get_feature_names_out()
102
  scores = np.asarray(tfidf.sum(axis=0)).ravel()
103
  indices_importantes = np.argsort(scores)[-5:]
104
  palabras_clave = [palabras[i] for i in indices_importantes]
105
 
106
- # Realizar b煤squeda en Pexels
 
 
 
 
107
  headers = {"Authorization": PEXELS_API_KEY}
108
  response = requests.get(
109
- f"https://api.pexels.com/videos/search?query={'+'.join(palabras_clave)}&per_page={num_videos}",
110
  headers=headers,
111
- timeout=20
112
  )
113
 
114
  videos = response.json().get('videos', [])
115
- logger.info(f"Encontrados {len(videos)} videos para palabras clave: {palabras_clave}")
116
 
117
- # Seleccionar los mejores videos por calidad
118
  videos_ordenados = sorted(
119
  videos,
120
  key=lambda x: x.get('width', 0) * x.get('height', 0),
@@ -124,31 +140,32 @@ def buscar_videos_semanticos(query, guion, num_videos=5):
124
  return videos_ordenados[:num_videos]
125
 
126
  except Exception as e:
127
- logger.error(f"Error en b煤squeda sem谩ntica: {str(e)}")
128
- # Fallback a b煤squeda simple
129
  response = requests.get(
130
- f"https://api.pexels.com/videos/search?query={query}&per_page={num_videos}",
131
  headers={"Authorization": PEXELS_API_KEY},
132
  timeout=10
133
  )
134
  return response.json().get('videos', [])[:num_videos]
135
 
136
- def crear_video_inteligente(prompt, custom_script, voz_index, musica=None):
137
  try:
138
  # 1. Generar o usar guion
139
- guion = custom_script if custom_script else generar_guion_avanzado(prompt)
140
- logger.info(f"Guion generado:\n{guion}")
141
 
142
  # 2. Seleccionar voz
143
  voz_seleccionada = VOICES[voz_index]['ShortName']
144
 
145
- # 3. Generar archivo de voz
146
  voz_archivo = "voz.mp3"
147
- communicate = edge_tts.Communicate(guion, voz_seleccionada)
148
- communicate.save(voz_archivo)
 
149
 
150
- # 4. Buscar videos usando an谩lisis sem谩ntico
151
- videos_data = buscar_videos_semanticos(prompt, guion, num_videos=5)
152
 
153
  if not videos_data:
154
  raise Exception("No se encontraron videos relevantes")
@@ -175,43 +192,42 @@ def crear_video_inteligente(prompt, custom_script, voz_index, musica=None):
175
  clip = VideoFileClip(temp_video.name)
176
  clips.append(clip)
177
 
178
- # 6. Procesar audio
179
- audio = AudioFileClip(voz_archivo)
180
- total_duration = audio.duration
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
 
182
  if musica:
183
  musica_clip = AudioFileClip(musica.name)
184
- if musica_clip.duration < total_duration:
185
- musica_clip = musica_clip.loop(duration=total_duration)
 
 
186
  audio = CompositeAudioClip([audio, musica_clip.volumex(0.25)])
187
 
188
- # 7. Crear video con sincronizaci贸n inteligente
189
- # Calcular duraci贸n por clip
190
- clip_durations = [c.duration for c in clips]
191
- total_clip_duration = sum(clip_durations)
192
-
193
- # Ajustar clips para que coincidan con la duraci贸n del audio
194
- if total_clip_duration < total_duration:
195
- # Repetir la secuencia de videos si es necesario
196
- repetitions = int(total_duration / total_clip_duration) + 1
197
- extended_clips = clips * repetitions
198
- final_clip = concatenate_videoclips(extended_clips).subclip(0, total_duration)
199
- else:
200
- # Ajustar velocidad para coincidir con la duraci贸n
201
- speed_factor = total_clip_duration / total_duration
202
- adjusted_clips = [clip.fx(vfx.speedx, speed_factor) for clip in clips]
203
- final_clip = concatenate_videoclips(adjusted_clips)
204
-
205
- final_clip = final_clip.set_audio(audio)
206
 
207
- # 8. Guardar video final
208
  output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
209
- final_clip.write_videofile(
210
  output_path,
211
  codec="libx264",
212
  audio_codec="aac",
213
- threads=4,
214
- preset='medium',
215
  fps=24
216
  )
217
 
@@ -221,79 +237,57 @@ def crear_video_inteligente(prompt, custom_script, voz_index, musica=None):
221
  logger.error(f"ERROR: {str(e)}")
222
  return None
223
  finally:
224
- # Limpieza
225
  if os.path.exists(voz_archivo):
226
  os.remove(voz_archivo)
227
 
228
  # Interfaz profesional
229
- with gr.Blocks(theme=gr.themes.Soft(), title="Generador de Videos con IA") as app:
230
- gr.Markdown("# 馃幀 GENERADOR AVANZADO DE VIDEOS CON IA")
231
 
232
  with gr.Row():
233
  with gr.Column(scale=1):
234
  gr.Markdown("### Configuraci贸n del Contenido")
235
- prompt = gr.Textbox(label="Tema principal", placeholder="Ej: 'Los misterios del universo'")
236
  custom_script = gr.TextArea(
237
  label="Guion personalizado (opcional)",
238
- placeholder="O escribe tu propio guion aqu铆...",
239
  lines=8
240
  )
241
  voz = gr.Dropdown(
242
- label="Selecciona una voz profesional",
243
  choices=VOICE_NAMES,
244
  value=VOICE_NAMES[0],
245
  type="index"
246
  )
247
  musica = gr.File(
248
- label="M煤sica de fondo profesional (opcional)",
249
- file_types=["audio"],
250
- type="filepath"
251
  )
252
- btn = gr.Button("馃殌 Generar Video Profesional", variant="primary", size="lg")
253
 
254
  with gr.Column(scale=2):
255
  output = gr.Video(
256
  label="Video Resultante",
257
  format="mp4",
258
- interactive=False,
259
- elem_id="video-output"
260
  )
261
 
262
- with gr.Accordion("Detalles t茅cnicos", open=False):
263
- gr.Markdown("""
264
- **Tecnolog铆as utilizadas:**
265
- - Generaci贸n de guiones: Meta MBART-large-50
266
- - B煤squeda sem谩ntica: Sentence Transformers multiling眉e
267
- - S铆ntesis de voz: Microsoft Edge TTS
268
- - Procesamiento de video: MoviePy
269
- """)
270
-
271
- # Ejemplos profesionales
272
- gr.Examples(
273
- examples=[
274
- ["Los secretos de la inteligencia artificial", "", 0, None],
275
- ["Lugares hist贸ricos de Europa", "", 3, None],
276
- ["Innovaciones tecnol贸gicas del futuro", "", 5, None]
277
- ],
278
- inputs=[prompt, custom_script, voz, musica],
279
- label="Ejemplos profesionales"
280
- )
281
 
282
  btn.click(
283
- fn=crear_video_inteligente,
284
  inputs=[prompt, custom_script, voz, musica],
285
  outputs=output
286
  )
287
 
288
- # CSS para mejor visualizaci贸n
289
- app.css = """
290
- #video-output {
291
- border-radius: 12px;
292
- box-shadow: 0 6px 16px rgba(0,0,0,0.15);
293
- margin: 20px auto;
294
- max-width: 100%;
295
- }
296
- """
297
-
298
  if __name__ == "__main__":
299
  app.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import os
2
  import re
3
  import requests
 
4
  import gradio as gr
 
5
  from moviepy.editor import *
 
 
 
6
  import edge_tts
7
  import tempfile
8
  import logging
9
+ from datetime import datetime
10
+ import numpy as np
11
  from sklearn.feature_extraction.text import TfidfVectorizer
 
12
  import nltk
13
+ from nltk.tokenize import sent_tokenize
14
+ import random
15
+ from transformers import pipeline
16
+ import torch
17
 
18
+ # Configuraci贸n inicial
19
  nltk.download('punkt')
 
 
20
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
21
  logger = logging.getLogger(__name__)
22
 
23
  # Configuraci贸n de modelos
24
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
25
+ MODEL_NAME = "DeepESP/gpt2-spanish" # Modelo en espa帽ol m谩s ligero
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ # Lista de voces disponibles
28
+ VOICES = asyncio.run(edge_tts.list_voices())
29
  VOICE_NAMES = [f"{v['Name']} ({v['Gender']}, {v['LocaleName']})" for v in VOICES]
30
 
31
+ def generar_guion_profesional(prompt):
32
+ """Genera guiones detallados con sistema de 3 niveles"""
33
  try:
34
+ # 1. Intento con modelo principal
35
+ generator = pipeline(
36
+ "text-generation",
37
+ model=MODEL_NAME,
38
+ device=0 if torch.cuda.is_available() else -1
39
+ )
40
+
41
+ response = generator(
42
+ f"Escribe un guion profesional para un video de YouTube sobre '{prompt}'. "
43
+ "La estructura debe incluir:\n"
44
+ "1. Introducci贸n atractiva\n"
45
+ "2. Tres secciones detalladas con subt铆tulos\n"
46
+ "3. Conclusi贸n impactante\n"
47
+ "Usa un estilo natural para narraci贸n:",
48
  max_length=1000,
 
49
  temperature=0.7,
50
  top_k=50,
51
  top_p=0.95,
52
+ num_return_sequences=1
53
  )
54
+
55
+ guion = response[0]['generated_text']
56
+
57
+ # 2. Verificar calidad del guion
58
+ if len(guion.split()) < 100: # Si es muy corto
59
+ raise ValueError("Guion demasiado breve")
60
+
61
+ return guion
62
+
63
  except Exception as e:
64
+ logger.error(f"Error generando guion: {str(e)}")
65
+
66
+ # 3. Respaldos inteligentes
67
+ temas = {
68
+ "historia": ["or铆genes", "eventos clave", "impacto actual"],
69
+ "tecnolog铆a": ["funcionamiento", "aplicaciones", "futuro"],
70
+ "ciencia": ["teor铆as", "evidencia", "implicaciones"],
71
+ "misterio": ["enigma", "teor铆as", "explicaciones"],
72
+ "arte": ["or铆genes", "caracter铆sticas", "influencia"]
73
+ }
74
+
75
+ # Detectar categor铆a del tema
76
+ categoria = "general"
77
+ for key in temas:
78
+ if key in prompt.lower():
79
+ categoria = key
80
+ break
81
+
82
+ puntos_clave = temas.get(categoria, ["aspectos importantes", "datos relevantes", "conclusiones"])
83
+
84
+ # Generar guion de respaldo con estructura profesional
85
  return f"""
86
+ 隆Hola a todos! Bienvenidos a este an谩lisis completo sobre {prompt}.
87
+ En este video exploraremos a fondo este fascinante tema a trav茅s de tres secciones clave.
88
+
89
+ SECCI脫N 1: {puntos_clave[0].capitalize()}
90
+ Comenzaremos analizando los {puntos_clave[0]} fundamentales.
91
+ Esto nos permitir谩 entender mejor la base de {prompt}.
92
+
93
+ SECCI脫N 2: {puntos_clave[1].capitalize()}
94
+ En esta parte, examinaremos los {puntos_clave[1]} m谩s relevantes
95
+ y c贸mo se relacionan con el tema principal.
96
+
97
+ SECCI脫N 3: {puntos_clave[2].capitalize()}
98
+ Finalmente, exploraremos las {puntos_clave[2]}
99
+ y qu茅 significan para el futuro de este campo.
100
+
101
+ 驴Listos para profundizar? 隆Empecemos!
102
  """
103
 
104
+ def buscar_videos_avanzado(prompt, guion, num_videos=5):
105
+ """B煤squeda inteligente de videos usando an谩lisis de contenido"""
 
 
 
 
 
 
 
 
106
  try:
107
  # Dividir el guion en oraciones
108
  oraciones = sent_tokenize(guion)
109
 
110
+ # Extraer palabras clave con TF-IDF
111
+ vectorizer = TfidfVectorizer(stop_words=['el', 'la', 'los', 'las', 'de', 'en', 'y', 'que'])
112
+ tfidf = vectorizer.fit_transform(oraciones)
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  palabras = vectorizer.get_feature_names_out()
114
  scores = np.asarray(tfidf.sum(axis=0)).ravel()
115
  indices_importantes = np.argsort(scores)[-5:]
116
  palabras_clave = [palabras[i] for i in indices_importantes]
117
 
118
+ # Mezclar palabras clave del prompt y del guion
119
+ palabras_prompt = re.findall(r'\b\w{4,}\b', prompt.lower())
120
+ todas_palabras = list(set(palabras_clave + palabras_prompt))[:5]
121
+
122
+ # Buscar en Pexels
123
  headers = {"Authorization": PEXELS_API_KEY}
124
  response = requests.get(
125
+ f"https://api.pexels.com/videos/search?query={'+'.join(todas_palabras)}&per_page={num_videos}",
126
  headers=headers,
127
+ timeout=15
128
  )
129
 
130
  videos = response.json().get('videos', [])
131
+ logger.info(f"Palabras clave usadas: {todas_palabras}")
132
 
133
+ # Seleccionar videos de mejor calidad
134
  videos_ordenados = sorted(
135
  videos,
136
  key=lambda x: x.get('width', 0) * x.get('height', 0),
 
140
  return videos_ordenados[:num_videos]
141
 
142
  except Exception as e:
143
+ logger.error(f"Error en b煤squeda de videos: {str(e)}")
144
+ # B煤squeda simple de respaldo
145
  response = requests.get(
146
+ f"https://api.pexels.com/videos/search?query={prompt}&per_page={num_videos}",
147
  headers={"Authorization": PEXELS_API_KEY},
148
  timeout=10
149
  )
150
  return response.json().get('videos', [])[:num_videos]
151
 
152
+ def crear_video_profesional(prompt, custom_script, voz_index, musica=None):
153
  try:
154
  # 1. Generar o usar guion
155
+ guion = custom_script if custom_script else generar_guion_profesional(prompt)
156
+ logger.info(f"Guion generado ({len(guion.split())} palabras)")
157
 
158
  # 2. Seleccionar voz
159
  voz_seleccionada = VOICES[voz_index]['ShortName']
160
 
161
+ # 3. Generar voz
162
  voz_archivo = "voz.mp3"
163
+ asyncio.run(edge_tts.Communicate(guion, voz_seleccionada).save(voz_archivo))
164
+ audio = AudioFileClip(voz_archivo)
165
+ duracion_total = audio.duration
166
 
167
+ # 4. Buscar videos relevantes
168
+ videos_data = buscar_videos_avanzado(prompt, guion)
169
 
170
  if not videos_data:
171
  raise Exception("No se encontraron videos relevantes")
 
192
  clip = VideoFileClip(temp_video.name)
193
  clips.append(clip)
194
 
195
+ # 6. Calcular duraci贸n por clip
196
+ duracion_por_clip = duracion_total / len(clips)
197
+
198
+ # 7. Procesar clips de video
199
+ clips_procesados = []
200
+ for clip in clips:
201
+ # Si el clip es m谩s corto que la duraci贸n asignada, hacer loop
202
+ if clip.duration < duracion_por_clip:
203
+ clip = clip.loop(duration=duracion_por_clip)
204
+ # Si es m谩s largo, recortar
205
+ else:
206
+ clip = clip.subclip(0, duracion_por_clip)
207
+ clips_procesados.append(clip)
208
+
209
+ # 8. Combinar videos
210
+ video_final = concatenate_videoclips(clips_procesados)
211
 
212
+ # 9. Procesar m煤sica
213
  if musica:
214
  musica_clip = AudioFileClip(musica.name)
215
+ if musica_clip.duration < duracion_total:
216
+ musica_clip = musica_clip.loop(duration=duracion_total)
217
+ else:
218
+ musica_clip = musica_clip.subclip(0, duracion_total)
219
  audio = CompositeAudioClip([audio, musica_clip.volumex(0.25)])
220
 
221
+ video_final = video_final.set_audio(audio)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
+ # 10. Exportar video
224
  output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
225
+ video_final.write_videofile(
226
  output_path,
227
  codec="libx264",
228
  audio_codec="aac",
229
+ threads=2,
230
+ preset='fast',
231
  fps=24
232
  )
233
 
 
237
  logger.error(f"ERROR: {str(e)}")
238
  return None
239
  finally:
240
+ # Limpieza de archivos temporales
241
  if os.path.exists(voz_archivo):
242
  os.remove(voz_archivo)
243
 
244
  # Interfaz profesional
245
+ with gr.Blocks(theme=gr.themes.Soft(), title="Generador de Videos Profesional") as app:
246
+ gr.Markdown("# 馃幀 GENERADOR DE VIDEOS CON IA")
247
 
248
  with gr.Row():
249
  with gr.Column(scale=1):
250
  gr.Markdown("### Configuraci贸n del Contenido")
251
+ prompt = gr.Textbox(label="Tema principal", placeholder="Ej: 'Los misterios de la antigua Grecia'")
252
  custom_script = gr.TextArea(
253
  label="Guion personalizado (opcional)",
254
+ placeholder="Pega aqu铆 tu propio guion completo...",
255
  lines=8
256
  )
257
  voz = gr.Dropdown(
258
+ label="Selecciona una voz",
259
  choices=VOICE_NAMES,
260
  value=VOICE_NAMES[0],
261
  type="index"
262
  )
263
  musica = gr.File(
264
+ label="M煤sica de fondo (opcional)",
265
+ file_types=["audio"]
 
266
  )
267
+ btn = gr.Button("馃殌 Generar Video", variant="primary", size="lg")
268
 
269
  with gr.Column(scale=2):
270
  output = gr.Video(
271
  label="Video Resultante",
272
  format="mp4",
273
+ interactive=False
 
274
  )
275
 
276
+ gr.Examples(
277
+ examples=[
278
+ ["Los secretos de las pir谩mides egipcias", "", 5, None],
279
+ ["La inteligencia artificial en medicina", "", 3, None],
280
+ ["Lugares abandonados m谩s misteriosos", "", 8, None]
281
+ ],
282
+ inputs=[prompt, custom_script, voz, musica],
283
+ label="Ejemplos: Haz clic en uno y luego en Generar"
284
+ )
 
 
 
 
 
 
 
 
 
 
285
 
286
  btn.click(
287
+ fn=crear_video_profesional,
288
  inputs=[prompt, custom_script, voz, musica],
289
  outputs=output
290
  )
291
 
 
 
 
 
 
 
 
 
 
 
292
  if __name__ == "__main__":
293
  app.launch(server_name="0.0.0.0", server_port=7860)