gnosticdev commited on
Commit
d68572a
verified
1 Parent(s): 9b819da

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -73
app.py CHANGED
@@ -1,90 +1,186 @@
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
- import random
14
  from transformers import pipeline
15
- import torch
16
- import asyncio
17
  from nltk.tokenize import sent_tokenize
 
 
 
18
 
19
  # Configuraci贸n inicial
20
  nltk.download('punkt', quiet=True)
21
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
22
  logger = logging.getLogger(__name__)
23
 
24
- # Configuraci贸n de modelos
25
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
26
- MODEL_NAME = "DeepESP/gpt2-spanish"
27
 
28
- # Lista de voces disponibles
29
- async def get_voices():
30
- voices = await edge_tts.list_voices()
31
- return [v['ShortName'] for v in voices]
 
 
32
 
33
- VOICE_NAMES = asyncio.run(get_voices())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- def generar_guion_profesional(prompt):
36
- """Genera guiones detallados con sistema de 3 niveles"""
37
  try:
38
- generator = pipeline(
39
- "text-generation",
40
- model=MODEL_NAME,
41
- device=0 if torch.cuda.is_available() else -1
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
- return guion
 
 
 
 
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  except Exception as e:
62
- logger.error(f"Error generando guion: {str(e)}")
63
- return f"Guion de ejemplo sobre {prompt}. Introducci贸n. Desarrollo. Conclusi贸n."
64
 
65
- async def crear_video_profesional(prompt, custom_script, voz_index, musica=None):
66
  try:
67
- # 1. Generar o usar guion
68
- guion = custom_script if custom_script else generar_guion_profesional(prompt)
69
- logger.info(f"Guion generado ({len(guion.split())} palabras)")
 
 
 
 
 
 
 
 
 
 
70
 
71
- # 2. Generar voz
72
  voz_archivo = "voz.mp3"
73
- await edge_tts.Communicate(guion, VOICE_NAMES[voz_index]).save(voz_archivo)
 
 
 
 
 
 
 
74
  audio = AudioFileClip(voz_archivo)
75
  duracion_total = audio.duration
76
 
77
- # 3. Crear video simple (versi贸n funcional)
78
- clip = ColorClip(size=(1280, 720), color=(0, 0, 0), duration=duracion_total)
79
- video_final = clip.set_audio(audio)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- # 4. Exportar video
 
 
 
 
82
  output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
83
  video_final.write_videofile(
84
  output_path,
85
  fps=24,
86
  codec="libx264",
87
- audio_codec="aac"
 
88
  )
89
 
90
  return output_path
@@ -93,33 +189,27 @@ async def crear_video_profesional(prompt, custom_script, voz_index, musica=None)
93
  logger.error(f"ERROR: {str(e)}")
94
  return None
95
  finally:
 
96
  if os.path.exists(voz_archivo):
97
  os.remove(voz_archivo)
 
 
 
 
98
 
99
- def run_async_func(prompt, custom_script, voz_index, musica=None):
100
- return asyncio.run(crear_video_profesional(prompt, custom_script, voz_index, musica))
101
-
102
- # Interfaz profesional CORREGIDA
103
- with gr.Blocks(theme=gr.themes.Soft(), title="Generador de Videos Profesional") as app:
104
- gr.Markdown("# 馃幀 GENERADOR DE VIDEOS CON IA")
105
 
106
  with gr.Row():
107
- with gr.Column(scale=1):
108
- gr.Markdown("### Configuraci贸n del Contenido")
109
- prompt = gr.Textbox(label="Tema principal", placeholder="Ej: 'Los misterios de la antigua Grecia'")
110
- voz = gr.Dropdown(
111
- label="Selecciona una voz",
112
- choices=VOICE_NAMES,
113
- value=VOICE_NAMES[0]
114
- )
115
- btn = gr.Button("馃殌 Generar Video", variant="primary", size="lg")
116
-
117
- with gr.Column(scale=2):
118
- output = gr.Video(label="Video Resultante", format="mp4")
119
-
120
- # CORRECCI脫N: Quitar el par谩metro timeout que causaba el error
121
  btn.click(
122
- fn=run_async_func,
123
  inputs=[prompt, gr.Textbox(visible=False), voz, gr.File(visible=False)],
124
  outputs=output
125
  )
 
1
  import os
2
+ import subprocess
3
  import requests
4
  import gradio as gr
5
  from moviepy.editor import *
6
+ from datetime import datetime
7
  import tempfile
8
  import logging
 
 
 
 
 
9
  from transformers import pipeline
10
+ import nltk
 
11
  from nltk.tokenize import sent_tokenize
12
+ import numpy as np
13
+ from sklearn.feature_extraction.text import TfidfVectorizer
14
+ import re
15
 
16
  # Configuraci贸n inicial
17
  nltk.download('punkt', quiet=True)
18
+ logging.basicConfig(level=logging.INFO)
19
  logger = logging.getLogger(__name__)
20
 
 
21
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
 
22
 
23
+ # Lista de voces v谩lidas
24
+ VOICES = [
25
+ "es-MX-DaliaNeural", "es-ES-ElviraNeural", "es-AR-ElenaNeural",
26
+ "en-US-JennyNeural", "fr-FR-DeniseNeural", "de-DE-KatjaNeural",
27
+ "it-IT-ElsaNeural", "pt-BR-FranciscaNeural", "ja-JP-NanamiNeural"
28
+ ]
29
 
30
+ # Inicializar el generador de texto
31
+ try:
32
+ script_generator = pipeline(
33
+ "text-generation",
34
+ model="gpt2", # Modelo m谩s flexible
35
+ device=0 if torch.cuda.is_available() else -1
36
+ )
37
+ except:
38
+ logger.warning("No se pudo cargar el modelo de generaci贸n de texto")
39
+ script_generator = None
40
+
41
+ def generar_guion(prompt):
42
+ """Genera un guion natural y extenso basado en el prompt"""
43
+ if script_generator:
44
+ try:
45
+ result = script_generator(
46
+ f"Genera un texto detallado y bien estructurado sobre '{prompt}' para un video de YouTube:",
47
+ max_length=500, # Texto m谩s largo
48
+ temperature=0.9, # M谩s creatividad
49
+ num_return_sequences=1
50
+ )
51
+ guion = result[0]['generated_text']
52
+
53
+ # Limpiar el guion generado
54
+ guion = re.sub(r'<.*?>', '', guion)
55
+ guion = re.sub(r'\n+', '\n', guion)
56
+ return guion.strip()
57
+ except Exception as e:
58
+ logger.error(f"Error generando guion: {str(e)}")
59
+
60
+ # Fallback natural
61
+ return f"En este video exploraremos en profundidad el tema de {prompt}. " \
62
+ "Analizaremos diversos aspectos y perspectivas para ofrecer una visi贸n completa. " \
63
+ "Veremos c贸mo este tema se relaciona con nuestra vida cotidiana y su impacto en la sociedad actual."
64
+
65
+ def extraer_palabras_clave(texto, n=7):
66
+ """Extrae palabras clave relevantes usando TF-IDF"""
67
+ # Preprocesamiento del texto
68
+ texto = re.sub(r'[^\w\s]', '', texto.lower())
69
+
70
+ # Tokenizar en oraciones
71
+ oraciones = sent_tokenize(texto)
72
+
73
+ # Crear matriz TF-IDF
74
+ vectorizer = TfidfVectorizer(
75
+ stop_words=['el', 'la', 'los', 'las', 'de', 'en', 'y', 'que', 'un', 'una', 'con', 'para'],
76
+ max_features=500
77
+ )
78
+ X = vectorizer.fit_transform(oraciones)
79
+
80
+ # Obtener palabras con mayor puntuaci贸n TF-IDF
81
+ suma_scores = np.asarray(X.sum(axis=0)).ravel()
82
+ indices = np.argsort(suma_scores)[::-1][:n]
83
+ palabras = vectorizer.get_feature_names_out()
84
+
85
+ return [palabras[i] for i in indices]
86
 
87
+ def buscar_videos_pexels(palabras_clave, num_videos=3):
88
+ """Busca videos en Pexels usando palabras clave con enfoque en relevancia"""
89
  try:
90
+ headers = {"Authorization": PEXELS_API_KEY}
91
+ query = "+".join(palabras_clave[:3]) # Usar las 3 palabras m谩s relevantes
 
 
 
92
 
93
+ logger.info(f"Buscando videos con palabras clave: {query}")
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
+ response = requests.get(
96
+ f"https://api.pexels.com/videos/search?query={query}&per_page={num_videos}",
97
+ headers=headers,
98
+ timeout=15
99
+ )
100
+ videos = response.json().get("videos", [])
101
 
102
+ # Filtrar videos de alta calidad
103
+ return sorted(
104
+ videos,
105
+ key=lambda x: x.get('duration', 0),
106
+ reverse=True
107
+ )[:num_videos]
108
+ except Exception as e:
109
+ logger.error(f"Error buscando videos: {str(e)}")
110
+ return []
111
+
112
+ def descargar_video(url, output_path):
113
+ """Descarga un video de manera eficiente"""
114
+ try:
115
+ with requests.get(url, stream=True, timeout=25) as r:
116
+ r.raise_for_status()
117
+ with open(output_path, 'wb') as f:
118
+ for chunk in r.iter_content(chunk_size=8192):
119
+ f.write(chunk)
120
+ return True
121
  except Exception as e:
122
+ logger.error(f"Error descargando video: {str(e)}")
123
+ return False
124
 
125
+ def crear_video(prompt, custom_script, voz_seleccionada, musica=None):
126
  try:
127
+ # 1. Generar guion natural
128
+ guion = custom_script if custom_script else generar_guion(prompt)
129
+ logger.info(f"Guion generado ({len(guion)} caracteres)")
130
+
131
+ # 2. Extraer palabras clave del guion completo
132
+ palabras_clave = extraer_palabras_clave(guion)
133
+ logger.info(f"Palabras clave extra铆das: {', '.join(palabras_clave)}")
134
+
135
+ # 3. Buscar videos relevantes usando IA
136
+ videos_data = buscar_videos_pexels(palabras_clave)
137
+
138
+ if not videos_data:
139
+ raise Exception("No se encontraron videos relevantes. Usando backup...")
140
 
141
+ # 4. Generar narraci贸n
142
  voz_archivo = "voz.mp3"
143
+ subprocess.run([
144
+ 'edge-tts',
145
+ '--voice', voz_seleccionada,
146
+ '--text', guion,
147
+ '--write-media', voz_archivo
148
+ ], check=True)
149
+
150
+ # 5. Procesar audio
151
  audio = AudioFileClip(voz_archivo)
152
  duracion_total = audio.duration
153
 
154
+ # 6. Descargar y preparar videos
155
+ clips = []
156
+ for i, video in enumerate(videos_data):
157
+ # Seleccionar la mejor calidad
158
+ video_file = max(
159
+ video['video_files'],
160
+ key=lambda x: x.get('width', 0) * x.get('height', 0)
161
+ )
162
+ video_url = video_file['link']
163
+ temp_path = f"temp_video_{i}.mp4"
164
+
165
+ if descargar_video(video_url, temp_path):
166
+ clip = VideoFileClip(temp_path)
167
+
168
+ # Ajustar duraci贸n proporcional
169
+ duracion_clip = min(duracion_total / len(videos_data), clip.duration)
170
+ clips.append(clip.subclip(0, duracion_clip))
171
 
172
+ # 7. Combinar videos
173
+ video_final = concatenate_videoclips(clips)
174
+ video_final = video_final.set_audio(audio)
175
+
176
+ # 8. Exportar
177
  output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
178
  video_final.write_videofile(
179
  output_path,
180
  fps=24,
181
  codec="libx264",
182
+ audio_codec="aac",
183
+ threads=2
184
  )
185
 
186
  return output_path
 
189
  logger.error(f"ERROR: {str(e)}")
190
  return None
191
  finally:
192
+ # Limpieza
193
  if os.path.exists(voz_archivo):
194
  os.remove(voz_archivo)
195
+ for i in range(3):
196
+ temp_file = f"temp_video_{i}.mp4"
197
+ if os.path.exists(temp_file):
198
+ os.remove(temp_file)
199
 
200
+ # Interfaz simplificada y funcional
201
+ with gr.Blocks(title="Generador de Videos") as app:
202
+ gr.Markdown("# 馃帴 Generador Autom谩tico de Videos")
 
 
 
203
 
204
  with gr.Row():
205
+ prompt = gr.Textbox(label="Tema del video", placeholder="Ej: Exploraci贸n espacial")
206
+ voz = gr.Dropdown(label="Voz Narradora", choices=VOICES, value=VOICES[0])
207
+
208
+ btn = gr.Button("Generar Video", variant="primary")
209
+ output = gr.Video(label="Resultado", format="mp4")
210
+
 
 
 
 
 
 
 
 
211
  btn.click(
212
+ fn=crear_video,
213
  inputs=[prompt, gr.Textbox(visible=False), voz, gr.File(visible=False)],
214
  outputs=output
215
  )