gnosticdev commited on
Commit
9a06bc2
verified
1 Parent(s): 374c72e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -61
app.py CHANGED
@@ -14,38 +14,24 @@ import random
14
  from transformers import pipeline
15
  import torch
16
  import asyncio
17
- import nest_asyncio
18
  from nltk.tokenize import sent_tokenize
19
 
20
- # Setup
21
  nltk.download('punkt', quiet=True)
22
- nest_asyncio.apply()
23
-
24
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
25
  logger = logging.getLogger(__name__)
26
 
27
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
28
  MODEL_NAME = "DeepESP/gpt2-spanish"
29
 
30
- VOICE_NAMES, VOICES = [], []
31
-
32
  async def get_voices():
33
- voces = await edge_tts.list_voices()
34
- voice_names = [f"{v['Name']} ({v['Gender']}, {v['LocaleName']})" for v in voces]
35
- return voice_names, voces
36
-
37
- async def get_and_set_voices():
38
- global VOICE_NAMES, VOICES
39
  try:
40
- VOICE_NAMES, VOICES = await get_voices()
41
- if not VOICES:
42
- raise Exception("No se encontraron voces.")
43
- except Exception as e:
44
- logger.warning(f"Fallo al cargar voces: {e}")
45
- VOICE_NAMES = ["Voz Predeterminada (Femenino, es-ES)"]
46
- VOICES = [{'ShortName': 'es-ES-ElviraNeural'}]
47
 
48
- asyncio.get_event_loop().run_until_complete(get_and_set_voices())
49
 
50
  def generar_guion_profesional(prompt):
51
  try:
@@ -56,8 +42,11 @@ def generar_guion_profesional(prompt):
56
  )
57
  response = generator(
58
  f"Escribe un guion profesional para un video de YouTube sobre '{prompt}'. "
59
- "Incluye introducci贸n, desarrollo en 3 secciones y conclusi贸n:",
60
- max_length=1000,
 
 
 
61
  temperature=0.7,
62
  top_k=50,
63
  top_p=0.95,
@@ -68,95 +57,130 @@ def generar_guion_profesional(prompt):
68
  raise ValueError("Guion demasiado breve")
69
  return guion
70
  except Exception as e:
71
- logger.error(f"Error generando guion: {e}")
72
- return f"""Introducci贸n sobre {prompt}.
73
- Secci贸n 1: Or铆genes e historia.
74
- Secci贸n 2: Estado actual.
75
- Secci贸n 3: Futuro e impacto.
76
- Conclusi贸n reflexiva."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  def buscar_videos_avanzado(prompt, guion, num_videos=5):
79
  try:
80
  oraciones = sent_tokenize(guion)
81
- vectorizer = TfidfVectorizer(stop_words='spanish')
82
  tfidf = vectorizer.fit_transform(oraciones)
83
  palabras = vectorizer.get_feature_names_out()
84
  scores = np.asarray(tfidf.sum(axis=0)).ravel()
85
- top_indices = np.argsort(scores)[-5:]
86
- palabras_clave = [palabras[i] for i in top_indices]
87
-
88
  palabras_prompt = re.findall(r'\b\w{4,}\b', prompt.lower())
89
- todas = list(set(palabras_clave + palabras_prompt))[:5]
90
-
91
  headers = {"Authorization": PEXELS_API_KEY}
92
  response = requests.get(
93
- f"https://api.pexels.com/videos/search?query={'+'.join(todas)}&per_page={num_videos}",
94
  headers=headers,
95
  timeout=15
96
  )
97
- return response.json().get('videos', [])
 
 
 
 
 
 
 
98
  except Exception as e:
99
- logger.error(f"Error buscando videos: {e}")
100
- return []
 
 
 
 
 
101
 
102
  async def crear_video_profesional(prompt, custom_script, voz_index, musica=None):
103
- voz_archivo = "voz.mp3"
104
  try:
105
- guion = custom_script if custom_script.strip() else generar_guion_profesional(prompt)
 
106
  voz_seleccionada = VOICES[voz_index]['ShortName'] if VOICES else 'es-ES-ElviraNeural'
107
-
108
- # Generar audio
109
  await edge_tts.Communicate(guion, voz_seleccionada).save(voz_archivo)
110
  audio = AudioFileClip(voz_archivo)
111
-
112
- # Obtener videos
113
  videos_data = buscar_videos_avanzado(prompt, guion)
114
  if not videos_data:
115
  raise Exception("No se encontraron videos")
116
-
117
- # Procesar videos
118
  clips = []
119
  for video in videos_data[:3]:
120
  video_file = next((vf for vf in video['video_files'] if vf['quality'] == 'sd'), video['video_files'][0])
121
  with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as temp_video:
122
  response = requests.get(video_file['link'], stream=True)
123
- for chunk in response.iter_content(chunk_size=1024 * 1024):
124
  temp_video.write(chunk)
125
  clip = VideoFileClip(temp_video.name).subclip(0, min(10, video['duration']))
126
  clips.append(clip)
127
-
128
- # Crear video final
129
  video_final = concatenate_videoclips(clips)
 
 
 
 
 
 
 
130
  video_final = video_final.set_audio(audio)
131
-
132
  output_path = f"video_output_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
133
  video_final.write_videofile(output_path, fps=24, threads=2)
134
  return output_path
135
-
136
  except Exception as e:
137
- logger.error(f"Error cr铆tico: {e}")
138
  return None
139
  finally:
140
- if os.path.exists(voz_archivo):
141
- os.remove(voz_archivo)
 
 
 
142
 
143
- # Gradio app
144
  with gr.Blocks(title="Generador de Videos") as app:
145
  with gr.Row():
146
  with gr.Column():
147
  prompt = gr.Textbox(label="Tema del video")
148
  custom_script = gr.TextArea(label="Gui贸n personalizado (opcional)")
149
  voz = gr.Dropdown(VOICE_NAMES, label="Voz", value=VOICE_NAMES[0])
 
150
  btn = gr.Button("Generar Video", variant="primary")
151
  with gr.Column():
152
  output = gr.Video(label="Resultado", format="mp4")
153
 
154
- async def wrapper(p, cs, v):
155
- return await crear_video_profesional(p, cs, VOICE_NAMES.index(v))
156
-
157
  btn.click(
158
- fn=wrapper,
159
- inputs=[prompt, custom_script, voz],
160
  outputs=output
161
  )
162
 
 
14
  from transformers import pipeline
15
  import torch
16
  import asyncio
 
17
  from nltk.tokenize import sent_tokenize
18
 
 
19
  nltk.download('punkt', quiet=True)
20
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 
 
21
  logger = logging.getLogger(__name__)
22
 
23
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
24
  MODEL_NAME = "DeepESP/gpt2-spanish"
25
 
 
 
26
  async def get_voices():
 
 
 
 
 
 
27
  try:
28
+ voices = await edge_tts.list_voices()
29
+ voice_names = [f"{v['Name']} ({v['Gender']}, {v['LocaleName']})" for v in voices]
30
+ return voice_names, voices
31
+ except:
32
+ return ["Voz Predeterminada (Femenino, es-ES)"], [{'ShortName': 'es-ES-ElviraNeural'}]
 
 
33
 
34
+ VOICE_NAMES, VOICES = asyncio.run(get_voices())
35
 
36
  def generar_guion_profesional(prompt):
37
  try:
 
42
  )
43
  response = generator(
44
  f"Escribe un guion profesional para un video de YouTube sobre '{prompt}'. "
45
+ "La estructura debe incluir:\n"
46
+ "1. Introducci贸n atractiva\n"
47
+ "2. Tres secciones detalladas con subt铆tulos\n"
48
+ "3. Conclusi贸n impactante\n"
49
+ "Usa un estilo natural para narraci贸n:",
50
  temperature=0.7,
51
  top_k=50,
52
  top_p=0.95,
 
57
  raise ValueError("Guion demasiado breve")
58
  return guion
59
  except Exception as e:
60
+ logger.error(f"Error generando guion: {str(e)}")
61
+ temas = {
62
+ "historia": ["or铆genes", "eventos clave", "impacto actual"],
63
+ "tecnolog铆a": ["funcionamiento", "aplicaciones", "futuro"],
64
+ "ciencia": ["teor铆as", "evidencia", "implicaciones"],
65
+ "misterio": ["enigma", "teor铆as", "explicaciones"],
66
+ "arte": ["or铆genes", "caracter铆sticas", "influencia"]
67
+ }
68
+ categoria = "general"
69
+ for key in temas:
70
+ if key in prompt.lower():
71
+ categoria = key
72
+ break
73
+ puntos_clave = temas.get(categoria, ["aspectos importantes", "datos relevantes", "conclusiones"])
74
+ return f"""
75
+ 隆Hola a todos! Bienvenidos a este an谩lisis completo sobre {prompt}.
76
+ En este video exploraremos a fondo este fascinante tema a trav茅s de tres secciones clave.
77
+
78
+ SECCI脫N 1: {puntos_clave[0].capitalize()}
79
+ Comenzaremos analizando los {puntos_clave[0]} fundamentales.
80
+ Esto nos permitir谩 entender mejor la base de {prompt}.
81
+
82
+ SECCI脫N 2: {puntos_clave[1].capitalize()}
83
+ En esta parte, examinaremos los {puntos_clave[1]} m谩s relevantes
84
+ y c贸mo se relacionan con el tema principal.
85
+
86
+ SECCI脫N 3: {puntos_clave[2].capitalize()}
87
+ Finalmente, exploraremos las {puntos_clave[2]}
88
+ y qu茅 significan para el futuro de este campo.
89
+
90
+ 驴Listos para profundizar? 隆Empecemos!
91
+ """
92
 
93
  def buscar_videos_avanzado(prompt, guion, num_videos=5):
94
  try:
95
  oraciones = sent_tokenize(guion)
96
+ vectorizer = TfidfVectorizer(stop_words=['el', 'la', 'los', 'las', 'de', 'en', 'y', 'que'])
97
  tfidf = vectorizer.fit_transform(oraciones)
98
  palabras = vectorizer.get_feature_names_out()
99
  scores = np.asarray(tfidf.sum(axis=0)).ravel()
100
+ indices_importantes = np.argsort(scores)[-5:]
101
+ palabras_clave = [palabras[i] for i in indices_importantes]
 
102
  palabras_prompt = re.findall(r'\b\w{4,}\b', prompt.lower())
103
+ todas_palabras = list(set(palabras_clave + palabras_prompt))[:5]
 
104
  headers = {"Authorization": PEXELS_API_KEY}
105
  response = requests.get(
106
+ f"https://api.pexels.com/videos/search?query={'+'.join(todas_palabras)}&per_page={num_videos}",
107
  headers=headers,
108
  timeout=15
109
  )
110
+ videos = response.json().get('videos', [])
111
+ logger.info(f"Palabras clave usadas: {todas_palabras}")
112
+ videos_ordenados = sorted(
113
+ videos,
114
+ key=lambda x: x.get('width', 0) * x.get('height', 0),
115
+ reverse=True
116
+ )
117
+ return videos_ordenados[:num_videos]
118
  except Exception as e:
119
+ logger.error(f"Error en b煤squeda de videos: {str(e)}")
120
+ response = requests.get(
121
+ f"https://api.pexels.com/videos/search?query={prompt}&per_page={num_videos}",
122
+ headers={"Authorization": PEXELS_API_KEY},
123
+ timeout=10
124
+ )
125
+ return response.json().get('videos', [])[:num_videos]
126
 
127
  async def crear_video_profesional(prompt, custom_script, voz_index, musica=None):
 
128
  try:
129
+ guion = custom_script if custom_script else generar_guion_profesional(prompt)
130
+ logger.info(f"Guion generado ({len(guion.split())} palabras)")
131
  voz_seleccionada = VOICES[voz_index]['ShortName'] if VOICES else 'es-ES-ElviraNeural'
132
+ voz_archivo = "voz.mp3"
 
133
  await edge_tts.Communicate(guion, voz_seleccionada).save(voz_archivo)
134
  audio = AudioFileClip(voz_archivo)
135
+ duracion_total = audio.duration
 
136
  videos_data = buscar_videos_avanzado(prompt, guion)
137
  if not videos_data:
138
  raise Exception("No se encontraron videos")
 
 
139
  clips = []
140
  for video in videos_data[:3]:
141
  video_file = next((vf for vf in video['video_files'] if vf['quality'] == 'sd'), video['video_files'][0])
142
  with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as temp_video:
143
  response = requests.get(video_file['link'], stream=True)
144
+ for chunk in response.iter_content(chunk_size=1024*1024):
145
  temp_video.write(chunk)
146
  clip = VideoFileClip(temp_video.name).subclip(0, min(10, video['duration']))
147
  clips.append(clip)
 
 
148
  video_final = concatenate_videoclips(clips)
149
+ if musica:
150
+ musica_clip = AudioFileClip(musica.name)
151
+ if musica_clip.duration < duracion_total:
152
+ musica_clip = musica_clip.loop(duration=duracion_total)
153
+ else:
154
+ musica_clip = musica_clip.subclip(0, duracion_total)
155
+ audio = CompositeAudioClip([audio, musica_clip.volumex(0.25)])
156
  video_final = video_final.set_audio(audio)
 
157
  output_path = f"video_output_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
158
  video_final.write_videofile(output_path, fps=24, threads=2)
159
  return output_path
 
160
  except Exception as e:
161
+ logger.error(f"Error cr铆tico: {str(e)}")
162
  return None
163
  finally:
164
+ if os.path.exists("voz.mp3"):
165
+ os.remove("voz.mp3")
166
+
167
+ def run_async_func(prompt, custom_script, voz_index, musica=None):
168
+ return asyncio.run(crear_video_profesional(prompt, custom_script, voz_index, musica))
169
 
 
170
  with gr.Blocks(title="Generador de Videos") as app:
171
  with gr.Row():
172
  with gr.Column():
173
  prompt = gr.Textbox(label="Tema del video")
174
  custom_script = gr.TextArea(label="Gui贸n personalizado (opcional)")
175
  voz = gr.Dropdown(VOICE_NAMES, label="Voz", value=VOICE_NAMES[0])
176
+ musica = gr.File(label="M煤sica de fondo (opcional)", file_types=["audio"])
177
  btn = gr.Button("Generar Video", variant="primary")
178
  with gr.Column():
179
  output = gr.Video(label="Resultado", format="mp4")
180
 
 
 
 
181
  btn.click(
182
+ fn=lambda p, cs, v, m: asyncio.run(crear_video_profesional(p, cs, VOICE_NAMES.index(v), m)),
183
+ inputs=[prompt, custom_script, voz, musica],
184
  outputs=output
185
  )
186