gnosticdev commited on
Commit
fa201eb
verified
1 Parent(s): d7f3a60

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +249 -264
app.py CHANGED
@@ -1,336 +1,321 @@
1
  import os
 
2
  import random
3
  import requests
4
  import gradio as gr
5
- from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeVideoClip
6
  from moviepy.audio.fx.all import audio_loop
7
  import edge_tts
8
  import asyncio
9
  from datetime import datetime
10
  from pathlib import Path
11
  from transformers import pipeline
 
 
 
12
  import logging
 
13
 
14
  # Configure logging
15
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
16
  logger = logging.getLogger(__name__)
17
 
18
- # Pexels API key from environment variable
19
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
20
  if not PEXELS_API_KEY:
21
  logger.error("PEXELS_API_KEY no encontrada en variables de entorno")
22
- logger.info("Loaded PEXELS_API_KEY from environment")
23
 
24
- # Ensure asyncio works with Gradio
25
- def run_async(coro):
26
- logger.info("Running async coroutine")
27
- try:
28
- loop = asyncio.new_event_loop()
29
- asyncio.set_event_loop(loop)
30
- result = loop.run_until_complete(coro)
31
- loop.close()
32
- logger.info("Async coroutine completed")
33
- return result
34
- except Exception as e:
35
- logger.error(f"Error en run_async: {e}")
36
- return None
37
-
38
- # Load lightweight text generation model for Spanish
39
- logger.info("Loading text generation model: facebook/mbart-large-50")
40
  try:
41
- generator = pipeline("text-generation", model="facebook/mbart-large-50", device="cpu")
42
- logger.info("Model loaded successfully")
 
 
 
 
43
  except Exception as e:
44
- logger.error(f"Error loading model: {e}")
45
- generator = None
46
-
47
- # List available Spanish voices for Edge TTS
48
- SPANISH_VOICES = [
49
- "es-MX-DaliaNeural",
50
- "es-MX-JorgeNeural",
51
- "es-MX-CecilioNeural",
52
- "es-MX-BeatrizNeural",
53
- "es-MX-CandelaNeural",
54
- "es-MX-CarlosNeural",
55
- "es-MX-LarissaNeural",
56
- "es-MX-ManuelNeural",
57
- "es-MX-MarinaNeural",
58
- "es-MX-NuriaNeural"
59
- ]
60
 
61
- # Fetch videos from Pexels
62
- def fetch_pexels_videos(query, num_videos=5):
63
- logger.info(f"Fetching {num_videos} videos from Pexels with query: {query}")
 
 
 
 
 
64
  headers = {"Authorization": PEXELS_API_KEY}
65
- url = f"https://api.pexels.com/videos/search?query={query}&per_page={num_videos}"
 
66
  try:
67
- response = requests.get(url, headers=headers)
68
- if response.status_code == 200:
69
- videos = [video["video_files"][0]["link"] for video in response.json()["videos"]]
70
- logger.info(f"Fetched {len(videos)} videos from Pexels")
71
- return videos
72
- logger.error(f"Failed to fetch videos from Pexels. Status code: {response.status_code}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  except Exception as e:
74
- logger.error(f"Error fetching videos from Pexels: {e}")
75
- return []
76
 
77
- # Generate script using local model or custom text
78
- def generate_script(prompt, custom_text=None):
79
- logger.info("Generating script")
80
  if custom_text and custom_text.strip():
81
- logger.info("Using custom text provided by user")
82
  return custom_text.strip()
83
- if not prompt or not prompt.strip():
84
- logger.error("No prompt or custom text provided")
85
- return "Error: Debes proporcionar un prompt o un guion personalizado."
86
 
87
- # Generate script with local model
88
- if generator:
89
- input_text = f"Genera un guion para un video sobre '{prompt}'. Crea una lista numerada con descripciones breves (m谩ximo 20 palabras por 铆tem) para un top 10 relacionado con el tema en espa帽ol."
90
- logger.info(f"Generating script with prompt: {prompt}")
91
- try:
92
- result = generator(input_text, max_length=300, num_return_sequences=1, do_sample=True, truncation=True)[0]['generated_text']
93
- logger.info("Script generated successfully")
94
- return result.strip()
95
- except Exception as e:
96
- logger.error(f"Error generating script: {e}")
97
 
98
- # Fallback mock response
99
- logger.info("Using fallback mock response")
100
- if "recetas" in prompt.lower():
101
- return """
102
- 1. Tacos al pastor: Jugosa carne marinada con pi帽a.
103
- 2. Lasagna: Capas de pasta, carne y queso fundido.
104
- 3. Sushi: Arroz y pescado fresco en rollos delicados.
105
- 4. Pizza casera: Masa crujiente con tus ingredientes favoritos.
106
- 5. Paella: Arroz con mariscos y azafr谩n.
107
- 6. Ceviche: Pescado fresco marinado en lim贸n.
108
- 7. Ramen: Caldo rico con fideos y cerdo.
109
- 8. Tiramis煤: Postre cremoso con caf茅 y mascarpone.
110
- 9. Enchiladas: Tortillas rellenas con salsa picante.
111
- 10. Curry: Especias intensas con carne o vegetales.
112
  """
113
- return f"Top 10 sobre {prompt}: No se pudo generar un guion espec铆fico."
114
-
115
- # Generate voice using Edge TTS
116
- async def generate_voice(text, output_file="output.mp3"):
117
- if not text or len(text.strip()) < 10:
118
- logger.error("Texto demasiado corto para generar voz")
119
- return None
120
 
121
- logger.info(f"Generating voice with Edge TTS")
122
- try:
123
- # Seleccionar una voz aleatoria
124
- voice = random.choice(SPANISH_VOICES)
125
- logger.info(f"Using voice: {voice}")
 
 
 
 
126
 
127
- communicate = edge_tts.Communicate(text, voice=voice)
128
- await communicate.save(output_file)
129
- logger.info(f"Voice generated and saved to {output_file}")
130
- return output_file
 
131
  except Exception as e:
132
- logger.error(f"Error generating voice: {e}")
133
- # Intentar con otra voz si falla
134
- for voice in SPANISH_VOICES:
135
- try:
136
- logger.info(f"Trying with voice: {voice}")
137
- communicate = edge_tts.Communicate(text, voice=voice)
138
- await communicate.save(output_file)
139
- logger.info(f"Voice generated with backup voice {voice}")
140
- return output_file
141
- except:
142
- continue
143
- logger.error("All voice attempts failed")
144
- return None
145
 
146
- # Download and trim video
147
- def download_and_trim_video(url, duration, output_path):
148
- logger.info(f"Downloading video from {url}")
 
 
149
  try:
150
- response = requests.get(url, stream=True)
151
- response.raise_for_status()
 
 
 
 
 
152
 
153
- with open("temp_video.mp4", "wb") as f:
154
- for chunk in response.iter_content(chunk_size=1024):
155
- f.write(chunk)
156
-
157
- logger.info("Trimming video")
158
- clip = VideoFileClip("temp_video.mp4")
159
- clip_duration = min(duration, clip.duration)
160
- clip = clip.subclip(0, clip_duration)
161
- clip.write_videofile(output_path, codec="libx264", audio_codec="aac", threads=4)
162
- clip.close()
163
- os.remove("temp_video.mp4")
164
- logger.info(f"Video trimmed and saved to {output_path}")
165
- return output_path
 
 
 
 
 
 
 
 
 
 
 
 
166
  except Exception as e:
167
- logger.error(f"Error downloading/trimming video: {e}")
168
- return None
 
 
 
169
 
170
- # Main video creation function
171
- def create_video(prompt, custom_text, music_file):
172
- logger.info("Starting video creation process")
 
 
 
173
 
174
- # Validar inputs
175
- if not prompt and not custom_text:
176
- logger.error("No prompt or custom text provided")
177
- return "Error: Debes proporcionar un prompt o un guion personalizado."
178
-
 
 
 
 
 
 
 
 
179
  output_dir = "output_videos"
180
  os.makedirs(output_dir, exist_ok=True)
181
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
182
- output_video = f"{output_dir}/video_{timestamp}.mp4"
183
- logger.info(f"Output video will be saved to {output_video}")
184
-
185
- # Generate or use provided script
186
- script = generate_script(prompt, custom_text)
187
- if "Error" in script:
188
- logger.error(script)
189
- return script
190
-
191
- # Generate voice
192
- voice_file = "temp_audio.mp3"
193
- voice_result = run_async(generate_voice(script, voice_file))
194
 
195
- if not voice_result or not os.path.exists(voice_file):
196
- logger.error("Voice generation failed")
197
- return "Error: No se pudo generar la voz. Intenta con un texto diferente."
198
-
199
  try:
200
- audio = AudioFileClip(voice_file)
201
- video_duration = audio.duration
202
- logger.info(f"Audio duration: {video_duration} seconds")
203
- except Exception as e:
204
- logger.error(f"Error loading audio file: {e}")
205
- return "Error: El archivo de audio generado es inv谩lido."
206
-
207
- # Fetch Pexels videos
208
- query = prompt.split()[0] if prompt else "generic"
209
- video_urls = fetch_pexels_videos(query, num_videos=5)
210
- if not video_urls:
211
- logger.error("No videos found on Pexels")
212
- return "Error: No se encontraron videos en Pexels."
213
-
214
- # Download and trim videos
215
- clips = []
216
- for i, url in enumerate(video_urls):
217
- clip_path = f"temp_clip_{i}.mp4"
218
- result = download_and_trim_video(url, video_duration / len(video_urls), clip_path)
219
- if result:
220
- try:
221
- clip = VideoFileClip(clip_path)
222
- clips.append(clip)
223
- logger.info(f"Processed video clip {i+1}/{len(video_urls)}")
224
- except Exception as e:
225
- logger.error(f"Error loading clip {i+1}: {e}")
226
- continue
227
-
228
- if not clips:
229
- logger.error("No valid video clips available")
230
- return "Error: No se pudieron procesar los videos descargados."
231
-
232
- # Concatenate video clips
233
- logger.info("Concatenating video clips")
234
- try:
235
- final_clip = concatenate_videoclips(clips, method="compose")
236
- final_clip = final_clip.set_duration(video_duration)
237
- except Exception as e:
238
- logger.error(f"Error concatenating clips: {e}")
239
- return "Error: No se pudieron unir los segmentos de video."
240
-
241
- # Add looped music or voice
242
- try:
243
- if music_file:
244
- logger.info("Adding user-uploaded music")
245
- music = AudioFileClip(music_file.name)
246
- music = audio_loop(music, duration=video_duration)
247
- final_audio = CompositeAudioClip([audio, music.volumex(0.3)])
248
  else:
249
- logger.info("Using generated voice as audio")
250
- final_audio = audio
251
-
252
- final_clip = final_clip.set_audio(final_audio)
253
- except Exception as e:
254
- logger.error(f"Error processing audio: {e}")
255
- return "Error: Problema al procesar el audio."
256
-
257
- # Write final video
258
- logger.info(f"Writing final video to {output_video}")
259
- try:
260
- final_clip.write_videofile(
261
- output_video,
262
- codec="libx264",
263
- audio_codec="aac",
264
  fps=24,
265
- threads=4,
266
- preset='ultrafast',
267
- bitrate="3000k"
268
  )
269
- except Exception as e:
270
- logger.error(f"Error writing video file: {e}")
271
- return "Error: No se pudo generar el video final."
272
 
273
- # Clean up
274
- logger.info("Cleaning up temporary files")
275
- try:
 
 
276
  for clip in clips:
277
  clip.close()
278
- audio.close()
279
- if music_file:
280
- music.close()
281
- final_clip.close()
282
- os.remove(voice_file)
283
  for i in range(len(video_urls)):
284
- if os.path.exists(f"temp_clip_{i}.mp4"):
285
- os.remove(f"temp_clip_{i}.mp4")
286
- except Exception as e:
287
- logger.error(f"Error during cleanup: {e}")
288
-
289
- logger.info("Video creation completed successfully")
290
- return output_video
291
 
292
- # Gradio interface
293
- with gr.Blocks() as demo:
294
- gr.Markdown("# Generador de Videos Autom谩ticos")
295
  gr.Markdown("""
296
- Crea videos autom谩ticos con voz en espa帽ol.
297
- Ingresa un tema (ej. 'Top 10 recetas mexicanas') o escribe tu propio guion.
298
  """)
299
 
300
  with gr.Row():
301
- with gr.Column():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  prompt = gr.Textbox(
303
- label="Tema del Video",
304
- placeholder="Ejemplo: Top 10 lugares para visitar en M茅xico",
305
  max_lines=2
306
  )
307
- custom_text = gr.Textbox(
308
- label="Guion Personalizado (opcional)",
309
- placeholder="Pega aqu铆 tu propio guion si prefieres...",
310
- max_lines=10
 
311
  )
 
312
  music_file = gr.File(
313
- label="M煤sica de Fondo (opcional, MP3)",
 
314
  file_types=[".mp3"]
315
  )
316
- submit = gr.Button("Generar Video", variant="primary")
317
 
318
- with gr.Column():
319
- output = gr.Video(label="Video Generado", format="mp4")
320
- gr.Examples(
321
- examples=[
322
- ["Top 10 ciudades de M茅xico", "", None],
323
- ["", "1. Ciudad de M茅xico - La capital vibrante\n2. Guadalajara - Cuna del mariachi\n3. Monterrey - Modernidad industrial", None]
324
- ],
325
- inputs=[prompt, custom_text, music_file],
326
- label="Ejemplos para probar"
327
- )
328
 
329
  submit.click(
330
- fn=create_video,
331
  inputs=[prompt, custom_text, music_file],
332
  outputs=output,
333
  api_name="generate_video"
334
  )
335
 
336
- demo.launch(server_name="0.0.0.0", server_port=7860, share=True)
 
 
 
 
 
 
 
1
  import os
2
+ import re
3
  import random
4
  import requests
5
  import gradio as gr
6
+ from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip
7
  from moviepy.audio.fx.all import audio_loop
8
  import edge_tts
9
  import asyncio
10
  from datetime import datetime
11
  from pathlib import Path
12
  from transformers import pipeline
13
+ from sentence_transformers import SentenceTransformer
14
+ from sklearn.metrics.pairwise import cosine_similarity
15
+ import numpy as np
16
  import logging
17
+ from typing import List, Optional, Tuple
18
 
19
  # Configure logging
20
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
21
  logger = logging.getLogger(__name__)
22
 
23
+ # Configuraci贸n de modelos de IA
24
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
25
  if not PEXELS_API_KEY:
26
  logger.error("PEXELS_API_KEY no encontrada en variables de entorno")
 
27
 
28
+ # Cargamos modelos de IA para an谩lisis sem谩ntico
29
+ logger.info("Cargando modelos de IA...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  try:
31
+ # Modelo para generaci贸n de texto
32
+ text_generator = pipeline("text-generation", model="facebook/mbart-large-50", device="cpu")
33
+
34
+ # Modelo para embeddings sem谩nticos (para matching de videos)
35
+ semantic_model = SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')
36
+ logger.info("Modelos de IA cargados exitosamente")
37
  except Exception as e:
38
+ logger.error(f"Error cargando modelos de IA: {e}")
39
+ raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
+ # Sistema mejorado de b煤squeda sem谩ntica
42
+ def fetch_semantic_videos(query: str, script: str, num_videos: int = 5) -> List[Tuple[str, float]]:
43
+ """Busca videos en Pexels usando matching sem谩ntico con el script"""
44
+ logger.info(f"Buscando videos sem谩nticos para: '{query}'")
45
+
46
+ # Generar embedding del script completo
47
+ script_embedding = semantic_model.encode(script, convert_to_tensor=True)
48
+
49
  headers = {"Authorization": PEXELS_API_KEY}
50
+ url = f"https://api.pexels.com/videos/search?query={query}&per_page={num_videos*2}" # Buscamos m谩s para filtrar
51
+
52
  try:
53
+ response = requests.get(url, headers=headers, timeout=15)
54
+ response.raise_for_status()
55
+
56
+ videos_data = []
57
+ for video in response.json().get("videos", []):
58
+ # Filtramos por calidad m铆nima
59
+ video_files = [vf for vf in video.get("video_files", [])
60
+ if vf.get("width", 0) >= 1280 and vf.get("duration", 0) >= 5]
61
+
62
+ if video_files:
63
+ best_file = max(video_files, key=lambda x: x.get("width", 0))
64
+ video_title = video.get("alt", "") or video.get("url", "")
65
+
66
+ # Calculamos similitud sem谩ntica
67
+ title_embedding = semantic_model.encode(video_title, convert_to_tensor=True)
68
+ similarity = cosine_similarity(
69
+ script_embedding.cpu().numpy().reshape(1, -1),
70
+ title_embedding.cpu().numpy().reshape(1, -1)
71
+ )[0][0]
72
+
73
+ videos_data.append((best_file["link"], similarity, video_title))
74
+
75
+ # Ordenamos por relevancia sem谩ntica
76
+ videos_data.sort(key=lambda x: x[1], reverse=True)
77
+
78
+ # Filtramos los m谩s relevantes
79
+ selected_videos = videos_data[:num_videos]
80
+
81
+ logger.info(f"Videos encontrados (relevancia):")
82
+ for idx, (url, score, title) in enumerate(selected_videos, 1):
83
+ logger.info(f"{idx}. {title} (score: {score:.2f})")
84
+
85
+ return [url for url, _, _ in selected_videos]
86
+
87
  except Exception as e:
88
+ logger.error(f"Error en b煤squeda sem谩ntica: {e}")
89
+ return []
90
 
91
+ # Generaci贸n de script con contexto mejorado
92
+ def generate_script(prompt: str, custom_text: Optional[str] = None) -> str:
93
+ """Genera un script contextualizado con IA"""
94
  if custom_text and custom_text.strip():
 
95
  return custom_text.strip()
 
 
 
96
 
97
+ if not prompt or not prompt.strip():
98
+ return "Error: Proporciona un tema o guion"
 
 
 
 
 
 
 
 
99
 
100
+ try:
101
+ # Prompt mejorado para generaci贸n contextual
102
+ context_prompt = f"""
103
+ Genera un guion detallado para un video sobre '{prompt}'.
104
+ El formato debe ser:
105
+ 1. [Concepto 1]: Descripci贸n breve (15-25 palabras)
106
+ 2. [Concepto 2]: Descripci贸n breve
107
+ ...
108
+ Incluye detalles visuales entre [] para ayudar a seleccionar im谩genes.
109
+ Ejemplo: [playa con palmeras] o [ciudad moderna con rascacielos]
 
 
 
 
110
  """
 
 
 
 
 
 
 
111
 
112
+ generated = text_generator(
113
+ context_prompt,
114
+ max_length=400,
115
+ num_return_sequences=1,
116
+ do_sample=True,
117
+ temperature=0.7,
118
+ top_k=50,
119
+ top_p=0.9
120
+ )[0]['generated_text']
121
 
122
+ # Post-procesamiento para limpiar el texto
123
+ cleaned = re.sub(r"<.*?>", "", generated) # Remove HTML tags
124
+ cleaned = re.sub(r"\n+", "\n", cleaned) # Remove extra newlines
125
+ return cleaned.strip()
126
+
127
  except Exception as e:
128
+ logger.error(f"Error generando script: {e}")
129
+ return f"Top 10 sobre {prompt}: [ejemplo 1] Descripci贸n breve..."
 
 
 
 
 
 
 
 
 
 
 
130
 
131
+ # Sistema mejorado de descarga de videos
132
+ def download_video_segment(url: str, duration: float, output_path: str) -> bool:
133
+ """Descarga y procesa segmentos de video con manejo robusto"""
134
+ temp_path = f"temp_{random.randint(1000,9999)}.mp4"
135
+
136
  try:
137
+ # Descarga con verificaci贸n
138
+ with requests.get(url, stream=True, timeout=20) as r:
139
+ r.raise_for_status()
140
+ with open(temp_path, 'wb') as f:
141
+ for chunk in r.iter_content(chunk_size=1024*1024):
142
+ if chunk:
143
+ f.write(chunk)
144
 
145
+ # Procesamiento con controles
146
+ with VideoFileClip(temp_path) as clip:
147
+ if clip.duration < 2:
148
+ raise ValueError("Video demasiado corto")
149
+
150
+ end_time = min(duration, clip.duration - 0.1)
151
+ subclip = clip.subclip(0, end_time)
152
+
153
+ # Configuraci贸n optimizada
154
+ subclip.write_videofile(
155
+ output_path,
156
+ codec="libx264",
157
+ audio_codec="aac",
158
+ fps=24,
159
+ threads=4,
160
+ preset='fast',
161
+ ffmpeg_params=[
162
+ '-max_muxing_queue_size', '1024',
163
+ '-crf', '23',
164
+ '-movflags', '+faststart'
165
+ ]
166
+ )
167
+
168
+ return True
169
+
170
  except Exception as e:
171
+ logger.error(f"Error procesando video: {e}")
172
+ return False
173
+ finally:
174
+ if os.path.exists(temp_path):
175
+ os.remove(temp_path)
176
 
177
+ # Funci贸n principal mejorada
178
+ def create_contextual_video(prompt: str, custom_text: Optional[str] = None, music_file: Optional[str] = None) -> str:
179
+ """Crea un video con matching sem谩ntico entre texto e im谩genes"""
180
+ # 1. Generaci贸n del script
181
+ script = generate_script(prompt, custom_text)
182
+ logger.info(f"Script generado:\n{script}")
183
 
184
+ # 2. B煤squeda sem谩ntica de videos
185
+ search_query = " ".join(extract_keywords(script)) or prompt
186
+ video_urls = fetch_semantic_videos(search_query, script)
187
+
188
+ if not video_urls:
189
+ return "Error: No se encontraron videos relevantes. Intenta con otro tema."
190
+
191
+ # 3. Generaci贸n de voz
192
+ voice_file = f"voice_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp3"
193
+ if not run_async(generate_voice(script, voice_file)):
194
+ return "Error: No se pudo generar la narraci贸n."
195
+
196
+ # 4. Procesamiento de videos
197
  output_dir = "output_videos"
198
  os.makedirs(output_dir, exist_ok=True)
199
+ output_path = f"{output_dir}/video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
 
 
 
 
 
 
 
 
 
 
 
 
200
 
 
 
 
 
201
  try:
202
+ # Descargar y preparar segmentos
203
+ clips = []
204
+ segment_duration = AudioFileClip(voice_file).duration / len(video_urls)
205
+
206
+ for idx, url in enumerate(video_urls):
207
+ clip_path = f"segment_{idx}.mp4"
208
+ if download_video_segment(url, segment_duration, clip_path):
209
+ clips.append(VideoFileClip(clip_path))
210
+
211
+ if not clips:
212
+ return "Error: No se pudieron procesar los videos."
213
+
214
+ # 5. Ensamblaje final
215
+ final_video = concatenate_videoclips(clips, method="compose")
216
+ audio_clip = AudioFileClip(voice_file)
217
+
218
+ # A帽adir m煤sica de fondo si existe
219
+ if music_file and os.path.exists(music_file.name):
220
+ music = audio_loop(AudioFileClip(music_file.name), duration=audio_clip.duration)
221
+ final_audio = CompositeAudioClip([audio_clip, music.volumex(0.2)])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  else:
223
+ final_audio = audio_clip
224
+
225
+ final_video = final_video.set_audio(final_audio)
226
+
227
+ # Renderizado final optimizado
228
+ final_video.write_videofile(
229
+ output_path,
230
+ codec="libx264",
231
+ audio_codec="aac",
 
 
 
 
 
 
232
  fps=24,
233
+ threads=6,
234
+ preset='fast',
235
+ bitrate="5000k"
236
  )
237
+
238
+ return output_path
 
239
 
240
+ except Exception as e:
241
+ logger.error(f"Error cr铆tico al crear video: {e}")
242
+ return f"Error: Fallo en la creaci贸n del video - {str(e)}"
243
+ finally:
244
+ # Limpieza
245
  for clip in clips:
246
  clip.close()
247
+ if os.path.exists(voice_file):
248
+ os.remove(voice_file)
 
 
 
249
  for i in range(len(video_urls)):
250
+ if os.path.exists(f"segment_{i}.mp4"):
251
+ os.remove(f"segment_{i}.mp4")
 
 
 
 
 
252
 
253
+ # Interfaz mejorada
254
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
255
  gr.Markdown("""
256
+ # 馃幀 Generador de Videos con IA Sem谩ntica
257
+ **Crea videos donde las im谩genes coinciden perfectamente con tu texto**
258
  """)
259
 
260
  with gr.Row():
261
+ with gr.Column(scale=1):
262
+ gr.Image("https://i.imgur.com/7X8P5R8.png", label="Ejemplo Visual")
263
+
264
+ with gr.Accordion("馃搶 Consejos para mejores resultados", open=False):
265
+ gr.Markdown("""
266
+ - **Describe tu tema con detalles**: "Playas del Caribe con arena blanca" en vez de solo "playas"
267
+ - **Usa sustantivos concretos**: "Animales de la selva amaz贸nica" > "naturaleza"
268
+ - **S茅 espec铆fico**: "Tecnolog铆a 2024" > "Avances en inteligencia artificial 2024"
269
+ """)
270
+
271
+ gr.Examples(
272
+ examples=[
273
+ ["Lugares hist贸ricos de Europa con arquitectura medieval"],
274
+ ["Tecnolog铆as emergentes en inteligencia artificial para 2024"],
275
+ ["Recetas tradicionales mexicanas con ingredientes aut贸ctonos"]
276
+ ],
277
+ inputs=[prompt],
278
+ label="Ejemplos de prompts efectivos"
279
+ )
280
+
281
+ with gr.Column(scale=2):
282
  prompt = gr.Textbox(
283
+ label="Tema principal del video",
284
+ placeholder="Ej: 'Top 5 innovaciones tecnol贸gicas de 2024'",
285
  max_lines=2
286
  )
287
+
288
+ custom_text = gr.TextArea(
289
+ label="O escribe tu propio guion (opcional)",
290
+ placeholder="Ej: 1. [Robot humanoide] Avances en rob贸tica...",
291
+ lines=6
292
  )
293
+
294
  music_file = gr.File(
295
+ label="M煤sica de fondo (opcional - MP3)",
296
+ type="filepath",
297
  file_types=[".mp3"]
298
  )
 
299
 
300
+ submit = gr.Button("馃殌 Generar Video", variant="primary")
301
+
302
+ output = gr.Video(
303
+ label="Video Generado",
304
+ format="mp4",
305
+ interactive=False
306
+ )
 
 
 
307
 
308
  submit.click(
309
+ fn=create_contextual_video,
310
  inputs=[prompt, custom_text, music_file],
311
  outputs=output,
312
  api_name="generate_video"
313
  )
314
 
315
+ if __name__ == "__main__":
316
+ demo.launch(
317
+ server_name="0.0.0.0",
318
+ server_port=7860,
319
+ share=True,
320
+ debug=True
321
+ )