gnosticdev commited on
Commit
3e716f3
verified
1 Parent(s): 77ffd33

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +63 -231
app.py CHANGED
@@ -10,10 +10,14 @@ from datetime import datetime
10
  import numpy as np
11
  from sklearn.feature_extraction.text import TfidfVectorizer
12
  import nltk
13
- import random
14
  from transformers import pipeline
15
  import torch
16
- import asyncio # 隆Importaci贸n cr铆tica que faltaba!
 
 
 
 
 
17
 
18
  # Configuraci贸n inicial
19
  nltk.download('punkt', quiet=True)
@@ -22,19 +26,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 (versi贸n simplificada)
28
  async def get_voices():
29
- return await edge_tts.list_voices()
 
30
 
31
- VOICES = asyncio.run(get_voices())
32
- VOICE_NAMES = [v['ShortName'] for v in VOICES] # Solo los nombres cortos de las voces
 
 
33
 
34
  def generar_guion_profesional(prompt):
35
- """Genera guiones detallados con sistema de 3 niveles"""
36
  try:
37
- # 1. Intento con modelo principal
38
  generator = pipeline(
39
  "text-generation",
40
  model=MODEL_NAME,
@@ -42,258 +48,84 @@ def generar_guion_profesional(prompt):
42
  )
43
 
44
  response = generator(
45
- f"Escribe un guion profesional para un video de YouTube sobre '{prompt}'. "
46
- "La estructura debe incluir:\n"
47
- "1. Introducci贸n atractiva\n"
48
- "2. Tres secciones detalladas con subt铆tulos\n"
49
- "3. Conclusi贸n impactante\n"
50
- "Usa un estilo natural para narraci贸n:",
51
- max_length=1000,
52
  temperature=0.7,
53
- top_k=50,
54
- top_p=0.95,
55
  num_return_sequences=1
56
  )
57
 
58
- guion = response[0]['generated_text']
59
-
60
- # 2. Verificar calidad del guion
61
- if len(guion.split()) < 100: # Si es muy corto
62
- raise ValueError("Guion demasiado breve")
63
-
64
- return guion
65
-
66
  except Exception as e:
67
  logger.error(f"Error generando guion: {str(e)}")
68
-
69
- # 3. Respaldos inteligentes
70
- temas = {
71
- "historia": ["or铆genes", "eventos clave", "impacto actual"],
72
- "tecnolog铆a": ["funcionamiento", "aplicaciones", "futuro"],
73
- "ciencia": ["teor铆as", "evidencia", "implicaciones"],
74
- "misterio": ["enigma", "teor铆as", "explicaciones"],
75
- "arte": ["or铆genes", "caracter铆sticas", "influencia"]
76
- }
77
-
78
- # Detectar categor铆a del tema
79
- categoria = "general"
80
- for key in temas:
81
- if key in prompt.lower():
82
- categoria = key
83
- break
84
-
85
- puntos_clave = temas.get(categoria, ["aspectos importantes", "datos relevantes", "conclusiones"])
86
-
87
- # Generar guion de respaldo con estructura profesional
88
- return f"""
89
- 隆Hola a todos! Bienvenidos a este an谩lisis completo sobre {prompt}.
90
- En este video exploraremos a fondo este fascinante tema a trav茅s de tres secciones clave.
91
-
92
- SECCI脫N 1: {puntos_clave[0].capitalize()}
93
- Comenzaremos analizando los {puntos_clave[0]} fundamentales.
94
- Esto nos permitir谩 entender mejor la base de {prompt}.
95
-
96
- SECCI脫N 2: {puntos_clave[1].capitalize()}
97
- En esta parte, examinaremos los {puntos_clave[1]} m谩s relevantes
98
- y c贸mo se relacionan con el tema principal.
99
-
100
- SECCI脫N 3: {puntos_clave[2].capitalize()}
101
- Finalmente, exploraremos las {puntos_clave[2]}
102
- y qu茅 significan para el futuro de este campo.
103
-
104
- 驴Listos para profundizar? 隆Empecemos!
105
- """
106
 
107
- def buscar_videos_avanzado(prompt, guion, num_videos=5):
108
- """B煤squeda inteligente de videos usando an谩lisis de contenido"""
109
  try:
110
- # Dividir el guion en oraciones
111
- oraciones = sent_tokenize(guion)
112
-
113
- # Extraer palabras clave con TF-IDF
114
- vectorizer = TfidfVectorizer(stop_words=['el', 'la', 'los', 'las', 'de', 'en', 'y', 'que'])
115
- tfidf = vectorizer.fit_transform(oraciones)
116
- palabras = vectorizer.get_feature_names_out()
117
- scores = np.asarray(tfidf.sum(axis=0)).ravel()
118
- indices_importantes = np.argsort(scores)[-5:]
119
- palabras_clave = [palabras[i] for i in indices_importantes]
120
-
121
- # Mezclar palabras clave del prompt y del guion
122
- palabras_prompt = re.findall(r'\b\w{4,}\b', prompt.lower())
123
- todas_palabras = list(set(palabras_clave + palabras_prompt))[:5]
124
-
125
- # Buscar en Pexels
126
- headers = {"Authorization": PEXELS_API_KEY}
127
- response = requests.get(
128
- f"https://api.pexels.com/videos/search?query={'+'.join(todas_palabras)}&per_page={num_videos}",
129
- headers=headers,
130
- timeout=15
131
- )
132
-
133
- videos = response.json().get('videos', [])
134
- logger.info(f"Palabras clave usadas: {todas_palabras}")
135
-
136
- # Seleccionar videos de mejor calidad
137
- videos_ordenados = sorted(
138
- videos,
139
- key=lambda x: x.get('width', 0) * x.get('height', 0),
140
- reverse=True
141
- )
142
-
143
- return videos_ordenados[:num_videos]
144
-
145
- except Exception as e:
146
- logger.error(f"Error en b煤squeda de videos: {str(e)}")
147
- # B煤squeda simple de respaldo
148
- response = requests.get(
149
- f"https://api.pexels.com/videos/search?query={prompt}&per_page={num_videos}",
150
- headers={"Authorization": PEXELS_API_KEY},
151
- timeout=10
152
- )
153
- return response.json().get('videos', [])[:num_videos]
154
-
155
- async def crear_video_profesional(prompt, custom_script, voz_index, musica=None):
156
- try:
157
- # 1. Generar o usar guion
158
  guion = custom_script if custom_script else generar_guion_profesional(prompt)
159
- logger.info(f"Guion generado ({len(guion.split())} palabras)")
160
-
161
- # 2. Seleccionar voz
162
- voz_seleccionada = VOICES[voz_index]['ShortName']
163
-
164
- # 3. Generar voz
165
- voz_archivo = "voz.mp3"
166
- await edge_tts.Communicate(guion, voz_seleccionada).save(voz_archivo)
167
- audio = AudioFileClip(voz_archivo)
168
- duracion_total = audio.duration
169
-
170
- # 4. Buscar videos relevantes
171
- videos_data = buscar_videos_avanzado(prompt, guion)
172
-
173
- if not videos_data:
174
- raise Exception("No se encontraron videos relevantes")
175
-
176
- # 5. Descargar y preparar videos
177
- clips = []
178
- for video in videos_data:
179
- # Seleccionar la mejor calidad de video
180
- video_files = sorted(
181
- video['video_files'],
182
- key=lambda x: x.get('width', 0) * x.get('height', 0),
183
- reverse=True
184
- )
185
- video_url = video_files[0]['link']
186
-
187
- # Descargar video
188
- response = requests.get(video_url, stream=True)
189
- temp_video = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
190
- for chunk in response.iter_content(chunk_size=1024*1024):
191
- temp_video.write(chunk)
192
- temp_video.close()
193
 
194
- # Crear clip
195
- clip = VideoFileClip(temp_video.name)
196
- clips.append(clip)
197
-
198
- # 6. Calcular duraci贸n por clip
199
- duracion_por_clip = duracion_total / len(clips)
200
-
201
- # 7. Procesar clips de video
202
- clips_procesados = []
203
- for clip in clips:
204
- # Si el clip es m谩s corto que la duraci贸n asignada, hacer loop
205
- if clip.duration < duracion_por_clip:
206
- clip = clip.loop(duration=duracion_por_clip)
207
- # Si es m谩s largo, recortar
208
- else:
209
- clip = clip.subclip(0, duracion_por_clip)
210
- clips_procesados.append(clip)
211
-
212
- # 8. Combinar videos
213
- video_final = concatenate_videoclips(clips_procesados)
214
 
215
- # 9. Procesar m煤sica
216
- if musica:
217
- musica_clip = AudioFileClip(musica.name)
218
- if musica_clip.duration < duracion_total:
219
- musica_clip = musica_clip.loop(duration=duracion_total)
220
- else:
221
- musica_clip = musica_clip.subclip(0, duracion_total)
222
- audio = CompositeAudioClip([audio, musica_clip.volumex(0.25)])
223
 
224
- video_final = video_final.set_audio(audio)
 
 
225
 
226
- # 10. Exportar video
227
- output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
228
- video_final.write_videofile(
229
  output_path,
 
230
  codec="libx264",
231
  audio_codec="aac",
232
- threads=2,
233
- preset='fast',
234
- fps=24
235
  )
236
 
237
  return output_path
238
 
239
  except Exception as e:
240
- logger.error(f"ERROR: {str(e)}")
241
  return None
242
  finally:
243
- # Limpieza de archivos temporales
244
  if os.path.exists(voz_archivo):
245
  os.remove(voz_archivo)
246
 
247
- def run_async_func(prompt, custom_script, voz_index, musica=None):
248
- return asyncio.run(crear_video_profesional(prompt, custom_script, voz_index, musica))
 
 
 
 
 
249
 
250
- # Interfaz profesional
251
- with gr.Blocks(theme=gr.themes.Soft(), title="Generador de Videos Profesional") as app:
252
- gr.Markdown("# 馃幀 GENERADOR DE VIDEOS CON IA")
253
 
254
  with gr.Row():
255
- with gr.Column(scale=1):
256
- gr.Markdown("### Configuraci贸n del Contenido")
257
- prompt = gr.Textbox(label="Tema principal", placeholder="Ej: 'Los misterios de la antigua Grecia'")
258
- custom_script = gr.TextArea(
259
- label="Guion personalizado (opcional)",
260
- placeholder="Pega aqu铆 tu propio guion completo...",
261
- lines=8
262
- )
263
- voz = gr.Dropdown(
264
- label="Selecciona una voz",
265
- choices=VOICE_NAMES,
266
- value=VOICE_NAMES[0],
267
- type="index"
268
- )
269
- musica = gr.File(
270
- label="M煤sica de fondo (opcional)",
271
- file_types=["audio"]
272
- )
273
- btn = gr.Button("馃殌 Generar Video", variant="primary", size="lg")
274
-
275
- with gr.Column(scale=2):
276
- output = gr.Video(
277
- label="Video Resultante",
278
- format="mp4",
279
- interactive=False
280
- )
281
-
282
- gr.Examples(
283
- examples=[
284
- ["Los secretos de las pir谩mides egipcias", "", 5, None],
285
- ["La inteligencia artificial en medicina", "", 3, None],
286
- ["Lugares abandonados m谩s misteriosos", "", 8, None]
287
- ],
288
- inputs=[prompt, custom_script, voz, musica],
289
- label="Ejemplos: Haz clic en uno y luego en Generar"
290
- )
291
-
292
  btn.click(
293
- fn=run_async_func,
294
- inputs=[prompt, custom_script, voz, musica],
295
- outputs=output
 
296
  )
297
 
298
  if __name__ == "__main__":
299
- app.launch(server_name="0.0.0.0", server_port=7860)
 
10
  import numpy as np
11
  from sklearn.feature_extraction.text import TfidfVectorizer
12
  import nltk
 
13
  from transformers import pipeline
14
  import torch
15
+ import asyncio
16
+ from nltk.tokenize import sent_tokenize
17
+ import nest_asyncio # Nueva importaci贸n importante
18
+
19
+ # Aplicar parche para el event loop
20
+ nest_asyncio.apply()
21
 
22
  # Configuraci贸n inicial
23
  nltk.download('punkt', quiet=True)
 
26
 
27
  # Configuraci贸n de modelos
28
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
29
+ MODEL_NAME = "DeepESP/gpt2-spanish"
30
 
31
+ # Lista de voces disponibles (versi贸n optimizada)
32
  async def get_voices():
33
+ voices = await edge_tts.list_voices()
34
+ return [v['ShortName'] for v in voices]
35
 
36
+ # Ejecutar en un nuevo event loop
37
+ loop = asyncio.new_event_loop()
38
+ asyncio.set_event_loop(loop)
39
+ VOICE_NAMES = loop.run_until_complete(get_voices())
40
 
41
  def generar_guion_profesional(prompt):
42
+ """Genera guiones optimizados para voz"""
43
  try:
 
44
  generator = pipeline(
45
  "text-generation",
46
  model=MODEL_NAME,
 
48
  )
49
 
50
  response = generator(
51
+ f"Escribe un guion conciso (m谩ximo 500 caracteres) sobre '{prompt}':",
52
+ max_length=500,
 
 
 
 
 
53
  temperature=0.7,
 
 
54
  num_return_sequences=1
55
  )
56
 
57
+ return response[0]['generated_text']
 
 
 
 
 
 
 
58
  except Exception as e:
59
  logger.error(f"Error generando guion: {str(e)}")
60
+ return f"Guion de ejemplo sobre {prompt}. Esto es una introducci贸n. Aqu铆 est谩n los puntos principales. Conclusi贸n final."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ # Funci贸n as铆ncrona optimizada
63
+ async def async_video_creation(prompt, custom_script, voz_index, musica=None):
64
  try:
65
+ # 1. Generar guion
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  guion = custom_script if custom_script else generar_guion_profesional(prompt)
67
+ if len(guion) > 2000:
68
+ guion = guion[:2000] # Limitar tama帽o para TTS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
+ # 2. Generar voz
71
+ voz_archivo = "voz.mp3"
72
+ communicate = edge_tts.Communicate(text=guion, voice=VOICE_NAMES[voz_index])
73
+ await communicate.save(voz_archivo)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
+ # 3. Crear clip de audio
76
+ audio = AudioFileClip(voz_archivo)
77
+ duracion = audio.duration
 
 
 
 
 
78
 
79
+ # 4. Crear video simple (versi贸n simplificada)
80
+ clip = ColorClip(size=(1280, 720), color=(0, 0, 0), duration=duracion)
81
+ clip = clip.set_audio(audio)
82
 
83
+ # 5. Exportar
84
+ output_path = f"video_output_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
85
+ clip.write_videofile(
86
  output_path,
87
+ fps=24,
88
  codec="libx264",
89
  audio_codec="aac",
90
+ threads=2
 
 
91
  )
92
 
93
  return output_path
94
 
95
  except Exception as e:
96
+ logger.error(f"Error cr铆tico: {str(e)}")
97
  return None
98
  finally:
 
99
  if os.path.exists(voz_archivo):
100
  os.remove(voz_archivo)
101
 
102
+ # Wrapper sincr贸nico para Gradio
103
+ def generar_video(prompt, custom_script, voz_index, musica=None):
104
+ try:
105
+ return asyncio.run(async_video_creation(prompt, custom_script, voz_index, musica))
106
+ except Exception as e:
107
+ logger.error(f"Error en wrapper: {str(e)}")
108
+ return None
109
 
110
+ # Interfaz simplificada
111
+ with gr.Blocks(title="Generador de Videos") as app:
112
+ gr.Markdown("## 馃帴 Generador Autom谩tico de Videos")
113
 
114
  with gr.Row():
115
+ with gr.Column():
116
+ prompt = gr.Textbox(label="Tema del video", placeholder="Ej: Inteligencia Artificial")
117
+ voz = gr.Dropdown(label="Voz Narradora", choices=VOICE_NAMES, value=VOICE_NAMES[0])
118
+ btn = gr.Button("Generar Video", variant="primary")
119
+
120
+ with gr.Column():
121
+ output = gr.Video(label="Resultado", format="mp4")
122
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  btn.click(
124
+ fn=generar_video,
125
+ inputs=[prompt, gr.Textbox(visible=False), voz],
126
+ outputs=output,
127
+ timeout=300 # 5 minutos de timeout
128
  )
129
 
130
  if __name__ == "__main__":
131
+ app.launch(server_port=7860, server_name="0.0.0.0")