Spaces:
Blakus
/
Runtime error

Blakus commited on
Commit
4a7acd8
·
verified ·
1 Parent(s): 2f915ae

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -77
app.py CHANGED
@@ -4,12 +4,13 @@ import sys
4
  import logging
5
  from pathlib import Path
6
  import os
7
- import threading # Adición para carga paralela del modelo
8
 
9
  # Configuración inicial ANTES de importaciones pesadas
10
  os.environ["COQUI_TOS_AGREED"] = "1"
11
- os.environ["OMP_NUM_THREADS"] = "1" # Corrección: Suprime advertencia libgomp
12
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
 
13
 
14
  # Configurar logging
15
  logging.basicConfig(
@@ -34,8 +35,8 @@ try:
34
  import scipy.io.wavfile as wavfile
35
  import warnings
36
  import shutil
37
- import pydantic # Para logging de versión
38
- logger.info(f"Gradio: {gr.__version__}, Pydantic: {pydantic.__version__}") # Adición: Logging de versiones para diagnóstico
39
  logger.info("✅ Todas las dependencias importadas correctamente")
40
  except ImportError as e:
41
  logger.error(f"❌ Error importando dependencias: {e}")
@@ -85,14 +86,12 @@ class PedroTTSApp:
85
  try:
86
  logger.info("📦 Iniciando configuración del modelo XTTS...")
87
 
88
- # El modelo está pre-cargado en el Space
89
  repo_id = "Blakus/Pedro_Lab_XTTS"
90
  local_dir = Path(get_user_data_dir("tts")) / "tts_models--multilingual--multi-dataset--xtts_v2"
91
  local_dir.mkdir(parents=True, exist_ok=True)
92
 
93
  files_to_download = ["config.json", "model.pth", "vocab.json"]
94
 
95
- # Descargar archivos del modelo
96
  for file_name in files_to_download:
97
  file_path = local_dir / file_name
98
  if not file_path.exists():
@@ -107,7 +106,6 @@ class PedroTTSApp:
107
  logger.info(f"✅ {file_name} descargado")
108
  except Exception as e:
109
  logger.warning(f"⚠️ Error en descarga directa de {file_name}: {e}")
110
- # Intentar método alternativo
111
  downloaded_file = hf_hub_download(
112
  repo_id=repo_id,
113
  filename=file_name
@@ -117,7 +115,6 @@ class PedroTTSApp:
117
  else:
118
  logger.info(f"✅ {file_name} ya existe")
119
 
120
- # Verificar archivos
121
  config_path = str(local_dir / "config.json")
122
  checkpoint_path = str(local_dir / "model.pth")
123
  vocab_path = str(local_dir / "vocab.json")
@@ -126,26 +123,22 @@ class PedroTTSApp:
126
  if not os.path.exists(path):
127
  raise FileNotFoundError(f"Archivo no encontrado: {name} en {path}")
128
 
129
- # Cargar configuración
130
  logger.info("⚙️ Cargando configuración...")
131
  self.config = XttsConfig()
132
  self.config.load_json(config_path)
133
 
134
- # Inicializar modelo
135
  logger.info("🔧 Inicializando modelo...")
136
  self.model = Xtts.init_from_config(self.config)
137
 
138
- # Cargar checkpoint
139
  logger.info("📂 Cargando checkpoint (esto puede tomar unos minutos)...")
140
  self.model.load_checkpoint(
141
  self.config,
142
  checkpoint_path=checkpoint_path,
143
  vocab_path=vocab_path,
144
  eval=True,
145
- use_deepspeed=False
146
  )
147
 
148
- # Mover a dispositivo apropiado
149
  if self.device == "cuda":
150
  self.model.cuda()
151
  logger.info("🚀 Modelo cargado en GPU")
@@ -208,11 +201,9 @@ class PedroTTSApp:
208
  def generate_speech(self, text, language, reference_audio, speed, temperature, enable_text_splitting):
209
  """Genera audio de voz"""
210
  try:
211
- # Adición: Verificación de modelo cargado
212
  if not self.model_loaded or not self.model:
213
  return None, "⏳ Modelo cargando... Intente en unos minutos o contacte al administrador."
214
 
215
- # Validaciones
216
  if not text or len(text.strip()) < 2:
217
  return None, "❌ El texto debe tener al menos 2 caracteres"
218
 
@@ -225,7 +216,6 @@ class PedroTTSApp:
225
  text = text.strip()
226
  logger.info(f"🎙️ Generando: '{text[:50]}{'...' if len(text) > 50 else ''}'")
227
 
228
- # Obtener latentes del audio de referencia
229
  try:
230
  gpt_cond_latent, speaker_embedding = self.model.get_conditioning_latents(
231
  audio_path=reference_audio
@@ -236,7 +226,6 @@ class PedroTTSApp:
236
 
237
  start_time = time.time()
238
 
239
- # Generar audio
240
  out = self.model.inference(
241
  text,
242
  language,
@@ -244,8 +233,8 @@ class PedroTTSApp:
244
  speaker_embedding,
245
  temperature=float(temperature),
246
  length_penalty=1.0,
247
- repetition_penalty=5.0,
248
- top_k=50,
249
  top_p=0.85,
250
  speed=float(speed),
251
  enable_text_splitting=enable_text_splitting,
@@ -254,11 +243,9 @@ class PedroTTSApp:
254
 
255
  inference_time = time.time() - start_time
256
 
257
- # Verificar output
258
  if "wav" not in out or out["wav"] is None:
259
  return None, "❌ No se generó audio"
260
 
261
- # Guardar audio
262
  timestamp = int(time.time())
263
  output_path = f"output_{timestamp}.wav"
264
  sample_rate = self.config.audio.get("output_sample_rate", 22050)
@@ -293,10 +280,7 @@ def create_interface():
293
  try:
294
  logger.info("🎨 Creando interfaz...")
295
 
296
- # Cargar audios de referencia
297
  available_audios = app.load_reference_audios()
298
-
299
- # Configurar opciones
300
  languages = [("Español", "es"), ("English", "en")]
301
 
302
  ref_mapping = {
@@ -316,11 +300,7 @@ def create_interface():
316
  label = ref_mapping.get(filename, filename)
317
  audio_refs.append((label, audio_file))
318
 
319
- # CSS personalizado
320
  custom_css = """
321
- .gradio-container {
322
- font-family: 'Inter', sans-serif;
323
- }
324
  .auth-box {
325
  max-width: 450px;
326
  margin: 40px auto;
@@ -329,13 +309,58 @@ def create_interface():
329
  background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
330
  box-shadow: 0 8px 32px rgba(0,0,0,0.4);
331
  }
332
- .header-box {
333
- text-align: center;
334
- padding: 30px;
335
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
 
336
  border-radius: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  color: white;
338
- margin-bottom: 25px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  }
340
  """
341
 
@@ -369,13 +394,25 @@ def create_interface():
369
  # Interfaz principal
370
  with gr.Column(visible=False) as main_column:
371
 
372
- # Header
373
- gr.HTML("""
374
- <div class="header-box">
375
- <h1 style="margin: 0 0 10px 0;">🎙️ Pedro Labattaglia</h1>
376
- <p style="margin: 0; font-size: 18px;">Síntesis de Voz Profesional</p>
377
- </div>
378
- """)
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
  with gr.Row():
381
  gr.Markdown("### ✅ Sesión activa")
@@ -385,39 +422,40 @@ def create_interface():
385
  with gr.Row():
386
  with gr.Column(scale=2):
387
 
388
- text_input = gr.Textbox(
389
- label="📝 Texto a sintetizar",
390
- placeholder="Escriba aquí el texto que desea convertir a voz...",
391
- lines=5
392
  )
393
 
394
- with gr.Row():
395
- language = gr.Dropdown(
396
- choices=languages,
397
- value="es",
398
- label="🌐 Idioma"
399
- )
400
-
401
- reference = gr.Dropdown(
402
- choices=audio_refs,
403
- value=audio_refs[0][1] if audio_refs else "",
404
- label="🎭 Estilo de voz"
405
- )
406
 
407
- with gr.Row():
408
- speed = gr.Slider(
409
- 0.5, 2.0, 1.0, 0.1,
410
- label=" Velocidad"
411
- )
412
-
413
- temperature = gr.Slider(
414
- 0.1, 1.5, 0.75, 0.05,
415
- label="🎨 Creatividad"
416
- )
417
 
 
418
  enable_text_splitting = gr.Checkbox(
419
  value=True,
420
- label="📖 Segmentación inteligente (recomendado para textos largos)"
 
 
 
 
 
 
421
  )
422
 
423
  generate_btn = gr.Button("🎵 Generar Audio", variant="primary", size="lg")
@@ -434,7 +472,18 @@ def create_interface():
434
  lines=10
435
  )
436
 
437
- # Event handlers
 
 
 
 
 
 
 
 
 
 
 
438
  generate_btn.click(
439
  fn=app.generate_speech,
440
  inputs=[text_input, language, reference, speed, temperature, enable_text_splitting],
@@ -499,39 +548,30 @@ def create_interface():
499
  def main():
500
  try:
501
  logger.info("🚀 Iniciando aplicación...")
502
-
503
- # CRÍTICO: Desactivar SSR para evitar errores con Node
504
- os.environ["GRADIO_SSR_MODE"] = "false"
505
 
506
- # Verificar entorno
507
  is_spaces = os.environ.get("SPACE_ID") is not None
508
  logger.info(f"🌍 Entorno: {'HuggingFace Spaces' if is_spaces else 'Local'}")
509
 
510
- # Verificar credenciales
511
  has_auth = os.environ.get("AUTH_USERNAME") and os.environ.get("AUTH_PASSWORD")
512
  if not has_auth:
513
  logger.warning("⚠️ Credenciales no configuradas en secrets")
514
  else:
515
  logger.info("✅ Credenciales configuradas")
516
 
517
- # Crear interfaz
518
  logger.info("🎨 Creando interfaz Gradio...")
519
  demo = create_interface()
520
  logger.info("✅ Interfaz creada")
521
 
522
- # Adición: Carga paralela del modelo (no bloquea UI)
523
  logger.info("📦 Cargando modelo XTTS en hilo de fondo...")
524
  model_thread = threading.Thread(target=app.setup_model, daemon=True)
525
  model_thread.start()
526
 
527
- # Lanzar
528
  port = int(os.environ.get("PORT", 7860))
529
  logger.info(f"🌐 Preparando lanzamiento en puerto {port}")
530
 
531
- # Corrección: Lanzamiento condicional para Spaces vs. Local
532
  if is_spaces:
533
  logger.info("🏠 Modo Spaces (auto-config)")
534
- demo.launch(share=False, quiet=True) # Spaces maneja binding automáticamente
535
  else:
536
  logger.info("🔗 Habilitando modo share (local)")
537
  logger.info("=" * 60)
 
4
  import logging
5
  from pathlib import Path
6
  import os
7
+ import threading
8
 
9
  # Configuración inicial ANTES de importaciones pesadas
10
  os.environ["COQUI_TOS_AGREED"] = "1"
11
+ os.environ["OMP_NUM_THREADS"] = "1"
12
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
13
+ os.environ["GRADIO_SSR_MODE"] = "false" # FIX: Desactivar SSR para evitar errores con Node
14
 
15
  # Configurar logging
16
  logging.basicConfig(
 
35
  import scipy.io.wavfile as wavfile
36
  import warnings
37
  import shutil
38
+ import pydantic
39
+ logger.info(f"Gradio: {gr.__version__}, Pydantic: {pydantic.__version__}")
40
  logger.info("✅ Todas las dependencias importadas correctamente")
41
  except ImportError as e:
42
  logger.error(f"❌ Error importando dependencias: {e}")
 
86
  try:
87
  logger.info("📦 Iniciando configuración del modelo XTTS...")
88
 
 
89
  repo_id = "Blakus/Pedro_Lab_XTTS"
90
  local_dir = Path(get_user_data_dir("tts")) / "tts_models--multilingual--multi-dataset--xtts_v2"
91
  local_dir.mkdir(parents=True, exist_ok=True)
92
 
93
  files_to_download = ["config.json", "model.pth", "vocab.json"]
94
 
 
95
  for file_name in files_to_download:
96
  file_path = local_dir / file_name
97
  if not file_path.exists():
 
106
  logger.info(f"✅ {file_name} descargado")
107
  except Exception as e:
108
  logger.warning(f"⚠️ Error en descarga directa de {file_name}: {e}")
 
109
  downloaded_file = hf_hub_download(
110
  repo_id=repo_id,
111
  filename=file_name
 
115
  else:
116
  logger.info(f"✅ {file_name} ya existe")
117
 
 
118
  config_path = str(local_dir / "config.json")
119
  checkpoint_path = str(local_dir / "model.pth")
120
  vocab_path = str(local_dir / "vocab.json")
 
123
  if not os.path.exists(path):
124
  raise FileNotFoundError(f"Archivo no encontrado: {name} en {path}")
125
 
 
126
  logger.info("⚙️ Cargando configuración...")
127
  self.config = XttsConfig()
128
  self.config.load_json(config_path)
129
 
 
130
  logger.info("🔧 Inicializando modelo...")
131
  self.model = Xtts.init_from_config(self.config)
132
 
 
133
  logger.info("📂 Cargando checkpoint (esto puede tomar unos minutos)...")
134
  self.model.load_checkpoint(
135
  self.config,
136
  checkpoint_path=checkpoint_path,
137
  vocab_path=vocab_path,
138
  eval=True,
139
+ use_deepspeed=True
140
  )
141
 
 
142
  if self.device == "cuda":
143
  self.model.cuda()
144
  logger.info("🚀 Modelo cargado en GPU")
 
201
  def generate_speech(self, text, language, reference_audio, speed, temperature, enable_text_splitting):
202
  """Genera audio de voz"""
203
  try:
 
204
  if not self.model_loaded or not self.model:
205
  return None, "⏳ Modelo cargando... Intente en unos minutos o contacte al administrador."
206
 
 
207
  if not text or len(text.strip()) < 2:
208
  return None, "❌ El texto debe tener al menos 2 caracteres"
209
 
 
216
  text = text.strip()
217
  logger.info(f"🎙️ Generando: '{text[:50]}{'...' if len(text) > 50 else ''}'")
218
 
 
219
  try:
220
  gpt_cond_latent, speaker_embedding = self.model.get_conditioning_latents(
221
  audio_path=reference_audio
 
226
 
227
  start_time = time.time()
228
 
 
229
  out = self.model.inference(
230
  text,
231
  language,
 
233
  speaker_embedding,
234
  temperature=float(temperature),
235
  length_penalty=1.0,
236
+ repetition_penalty=max(1.01, 5.0),
237
+ top_k=int(50),
238
  top_p=0.85,
239
  speed=float(speed),
240
  enable_text_splitting=enable_text_splitting,
 
243
 
244
  inference_time = time.time() - start_time
245
 
 
246
  if "wav" not in out or out["wav"] is None:
247
  return None, "❌ No se generó audio"
248
 
 
249
  timestamp = int(time.time())
250
  output_path = f"output_{timestamp}.wav"
251
  sample_rate = self.config.audio.get("output_sample_rate", 22050)
 
280
  try:
281
  logger.info("🎨 Creando interfaz...")
282
 
 
283
  available_audios = app.load_reference_audios()
 
 
284
  languages = [("Español", "es"), ("English", "en")]
285
 
286
  ref_mapping = {
 
300
  label = ref_mapping.get(filename, filename)
301
  audio_refs.append((label, audio_file))
302
 
 
303
  custom_css = """
 
 
 
304
  .auth-box {
305
  max-width: 450px;
306
  margin: 40px auto;
 
309
  background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
310
  box-shadow: 0 8px 32px rgba(0,0,0,0.4);
311
  }
312
+ .speaker-info {
 
 
313
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
314
+ color: white;
315
+ padding: 20px;
316
  border-radius: 15px;
317
+ margin-bottom: 20px;
318
+ text-align: center;
319
+ }
320
+ .speaker-image {
321
+ width: 180px;
322
+ height: 180px;
323
+ border-radius: 50%;
324
+ margin: 0 auto 15px;
325
+ border: 4px solid rgba(255,255,255,0.3);
326
+ object-fit: cover;
327
+ }
328
+ .social-links {
329
+ display: flex;
330
+ justify-content: center;
331
+ gap: 15px;
332
+ margin-top: 15px;
333
+ }
334
+ .social-link {
335
  color: white;
336
+ text-decoration: none;
337
+ font-size: 16px;
338
+ padding: 8px 12px;
339
+ border-radius: 20px;
340
+ background: rgba(255,255,255,0.2);
341
+ transition: all 0.3s ease;
342
+ }
343
+ .social-link:hover {
344
+ background: rgba(255,255,255,0.3);
345
+ transform: translateY(-2px);
346
+ }
347
+ .credits-section {
348
+ margin-top: 15px;
349
+ text-align: center;
350
+ }
351
+ .credits-text {
352
+ color: #6c757d;
353
+ font-size: 12px;
354
+ margin: 5px 0;
355
+ }
356
+ .credits-link {
357
+ color: #007bff;
358
+ text-decoration: none;
359
+ font-size: 11px;
360
+ transition: color 0.3s ease;
361
+ }
362
+ .credits-link:hover {
363
+ color: #0056b3;
364
  }
365
  """
366
 
 
394
  # Interfaz principal
395
  with gr.Column(visible=False) as main_column:
396
 
397
+ # Header con info del locutor
398
+ with gr.Column(elem_classes="speaker-info"):
399
+ gr.HTML("""
400
+ <div style="text-align: center;">
401
+ <img src="https://labattaglia.com.ar/images/about_me_pic2.jpg"
402
+ class="speaker-image" alt="Pedro Labattaglia">
403
+ <h2 style="margin: 10px 0 5px 0;">Pedro Labattaglia</h2>
404
+ <p style="margin: 0; font-style: italic; opacity: 0.9;">
405
+ 🎙️ Locutor profesional | +20 años dando voz a marcas líderes en Argentina, LATAM y EE.UU. |
406
+ Español rioplatense / neutro | Voice Over | Source Connect: pedrovotalent |
407
408
+ </p>
409
+ <div class="social-links">
410
+ <a href="https://www.instagram.com/locutor.fit/" class="social-link" target="_blank">📸 Instagram</a>
411
+ <a href="https://www.linkedin.com/in/pedro-labattaglia/" class="social-link" target="_blank">💼 LinkedIn</a>
412
+ <a href="https://labattaglia.com.ar/" class="social-link" target="_blank">🌐 Web</a>
413
+ </div>
414
+ </div>
415
+ """)
416
 
417
  with gr.Row():
418
  gr.Markdown("### ✅ Sesión activa")
 
422
  with gr.Row():
423
  with gr.Column(scale=2):
424
 
425
+ language = gr.Dropdown(
426
+ choices=languages,
427
+ value="es",
428
+ label="🌐 Idioma"
429
  )
430
 
431
+ reference = gr.Dropdown(
432
+ choices=audio_refs,
433
+ value=audio_refs[0][1] if audio_refs else "",
434
+ label="🎭 Estilo de voz"
435
+ )
436
+
437
+ gr.Markdown("**Velocidad de reproducción del audio**")
438
+ speed = gr.Slider(
439
+ 0.5, 2.0, 1.0, 0.1,
440
+ label="⚡ Velocidad"
441
+ )
 
442
 
443
+ gr.Markdown("**🛡️ Más estable pero menos creativo/expresivo ← → 🎭 Menos estable pero más creativo/expresivo**")
444
+ temperature = gr.Slider(
445
+ 0.1, 1.5, 0.75, 0.05,
446
+ label="🎨 Creatividad"
447
+ )
 
 
 
 
 
448
 
449
+ gr.Markdown("**✅ Puede generar mejor coherencia con textos largos | ⚠️ A costa de estabilidad o pequeños errores**")
450
  enable_text_splitting = gr.Checkbox(
451
  value=True,
452
+ label="📖 Segmentación inteligente"
453
+ )
454
+
455
+ text_input = gr.Textbox(
456
+ label="📝 Texto a sintetizar",
457
+ placeholder="Escriba aquí el texto que desea convertir a voz...",
458
+ lines=5
459
  )
460
 
461
  generate_btn = gr.Button("🎵 Generar Audio", variant="primary", size="lg")
 
472
  lines=10
473
  )
474
 
475
+ # Créditos
476
+ with gr.Column(elem_classes="credits-section"):
477
+ gr.HTML("""
478
+ <div style="text-align: center;">
479
+ <p class="credits-text">Desarrollado por <strong>Ezequiel Casas</strong></p>
480
+ <a href="https://www.linkedin.com/in/ezequiel-c-592641142/"
481
+ class="credits-link"
482
+ target="_blank">LinkedIn</a>
483
+ </div>
484
+ """)
485
+
486
+ # Event handler para generación
487
  generate_btn.click(
488
  fn=app.generate_speech,
489
  inputs=[text_input, language, reference, speed, temperature, enable_text_splitting],
 
548
  def main():
549
  try:
550
  logger.info("🚀 Iniciando aplicación...")
 
 
 
551
 
 
552
  is_spaces = os.environ.get("SPACE_ID") is not None
553
  logger.info(f"🌍 Entorno: {'HuggingFace Spaces' if is_spaces else 'Local'}")
554
 
 
555
  has_auth = os.environ.get("AUTH_USERNAME") and os.environ.get("AUTH_PASSWORD")
556
  if not has_auth:
557
  logger.warning("⚠️ Credenciales no configuradas en secrets")
558
  else:
559
  logger.info("✅ Credenciales configuradas")
560
 
 
561
  logger.info("🎨 Creando interfaz Gradio...")
562
  demo = create_interface()
563
  logger.info("✅ Interfaz creada")
564
 
 
565
  logger.info("📦 Cargando modelo XTTS en hilo de fondo...")
566
  model_thread = threading.Thread(target=app.setup_model, daemon=True)
567
  model_thread.start()
568
 
 
569
  port = int(os.environ.get("PORT", 7860))
570
  logger.info(f"🌐 Preparando lanzamiento en puerto {port}")
571
 
 
572
  if is_spaces:
573
  logger.info("🏠 Modo Spaces (auto-config)")
574
+ demo.launch(share=False, quiet=True, ssr_mode=False)
575
  else:
576
  logger.info("🔗 Habilitando modo share (local)")
577
  logger.info("=" * 60)