Cristobal299 commited on
Commit
b47246a
·
verified ·
1 Parent(s): 8bda87b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +30 -22
app.py CHANGED
@@ -1,7 +1,7 @@
1
  # -*- coding: utf-8 -*-
2
  """
3
  Centralita Torá — HuggingFace Space (Gradio)
4
- Leer hebreo + español · escuchar (Canto Trope Real y Voz Divina Especial en Español)
5
  """
6
  import os, glob, json, tempfile, html, asyncio, subprocess, shutil, re
7
  import urllib.request
@@ -33,17 +33,18 @@ GROQ_URL = "https://api.groq.com/openai/v1/chat/completions"
33
  GROQ_KEY = os.environ.get("GROQ_API_KEY")
34
 
35
 
36
- # --- PROCESAMIENTO ACÚSTICO FFMEPG (ESPAÑOL) ---
37
  def procesar_audio_narrador(ruta):
38
- """Efecto catedral estándar para el narrador."""
39
  if not shutil.which("ffmpeg"):
40
  return ruta
41
 
 
42
  filtros = [
43
- "atempo=0.90",
44
- "bass=g=8:f=133",
45
- "treble=g=-22",
46
- "aecho=0.75:0.70:120|240:0.4|0.25"
47
  ]
48
  salida = ruta[:-4] + "_narrador.mp3"
49
  cmd = ["ffmpeg", "-y", "-i", ruta, "-af", ", ".join(filtros), "-ac", "2", salida]
@@ -58,19 +59,19 @@ def procesar_audio_narrador(ruta):
58
 
59
  def procesar_audio_divino(ruta):
60
  """
61
- Efecto ACÚSTICO SAGRADO Y PROFUNDO para la Voz de Dios.
62
- Tono más grave, resonancia sub-bass, eco más amplio.
63
  """
64
  if not shutil.which("ffmpeg"):
65
  return ruta
66
 
67
  filtros = [
68
- "atempo=0.85",
69
- "asetrate=44100*0.85",
70
  "aresample=44100",
71
- "bass=g=15:f=200",
72
- "treble=g=-10",
73
- "aecho=0.8:0.8:150|300:0.5|0.3"
74
  ]
75
  salida = ruta[:-4] + "_divino.mp3"
76
  cmd = ["ffmpeg", "-y", "-i", ruta, "-af", ", ".join(filtros), "-ac", "2", salida]
@@ -127,9 +128,6 @@ def mezclar_fondo(voz_path):
127
 
128
  # --- SEPARACIÓN DE BLOQUES (NARRADOR vs DIOS) PARA ESPAÑOL ---
129
  def segmentar_texto_divino(texto):
130
- """
131
- Detecta patrones para dividir el texto entre 'narrador' y 'dios'.
132
- """
133
  patron = r'((?:Y\s+dijo\s+Dios|dijo\s+Dios|Y\s+llamó\s+Dios|Y\s+bendijo\s+Dios)[^:,.–—]*[:,.–—]?\s*)([^.\n]+)'
134
  bloques = []
135
  ultimo_idx = 0
@@ -159,7 +157,6 @@ async def generar_bloque_audio(texto, es_divino):
159
  rate = "-9%" if es_divino else VOZ_RATE
160
 
161
  try:
162
- # El texto se pasa limpio, sin corrupciones en las vocales.
163
  com = edge_tts.Communicate(texto[:4000], voz, rate=rate, pitch=pitch)
164
  await com.save(ruta)
165
  except Exception:
@@ -309,18 +306,29 @@ def render(nombre, cap):
309
  vs = versiculos(lid, cap)
310
  if not vs:
311
  return f"<div class='pasaje'><p>No hay texto para {html.escape(d.get('es',''))} {cap}.</p></div>", "", ""
 
312
  filas = [f"<div class='cab'><span class='h'>{html.escape(d.get('heb',''))}</span><br>"
313
  f"{html.escape(d.get('es',''))} {cap}</div>"]
314
  plano_es, plano_he = [], []
 
315
  for v in vs:
316
- he = html.escape(v.get("he", "")); es = html.escape(v.get("es", ""))
 
 
 
 
 
 
317
  fila = f"<div class='verso'><span class='num'>{v.get('n','')}</span>"
318
  if he:
319
- fila += f"<span class='he'>{he}</span>"; plano_he.append(v.get("he", ""))
 
320
  if es:
321
- fila += f"<span class='es'>{es}</span>"; plano_es.append(v.get("es", ""))
 
322
  fila += "</div>"
323
  filas.append(fila)
 
324
  return f"<div class='pasaje'>{''.join(filas)}</div>", " ".join(plano_es), " ".join(plano_he)
325
 
326
 
@@ -358,7 +366,7 @@ with gr.Blocks(theme=fenix_theme(), css=FENIX_CSS, title="Centralita Torá") as
358
  st_cap = gr.State(1)
359
 
360
  gr.HTML("<div id='cabecera'><div class='estrella'>✡</div>"
361
- "<h1>Centralita Torá</h1><p>hebreo y español · canto Trope real y Voz Divina limpia</p></div>")
362
 
363
  with gr.Tab("Leer"):
364
  with gr.Row():
 
1
  # -*- coding: utf-8 -*-
2
  """
3
  Centralita Torá — HuggingFace Space (Gradio)
4
+ Leer hebreo + español · canto Trope real y Voz Divina cercana y limpia
5
  """
6
  import os, glob, json, tempfile, html, asyncio, subprocess, shutil, re
7
  import urllib.request
 
33
  GROQ_KEY = os.environ.get("GROQ_API_KEY")
34
 
35
 
36
+ # --- PROCESAMIENTO ACÚSTICO FFMEPG (ESPAÑOL MÁS CERCANO Y SIN ECO EXCESIVO) ---
37
  def procesar_audio_narrador(ruta):
38
+ """Efecto cercano y limpio para el narrador."""
39
  if not shutil.which("ffmpeg"):
40
  return ruta
41
 
42
+ # Menos eco, más presencia cercana (frecuencias medias y altas claras)
43
  filtros = [
44
+ "atempo=0.92",
45
+ "bass=g=5:f=120",
46
+ "treble=g=2",
47
+ "aecho=0.8:0.88:40:0.15" # Eco muy corto y sutil para dar cuerpo sin alejar la voz
48
  ]
49
  salida = ruta[:-4] + "_narrador.mp3"
50
  cmd = ["ffmpeg", "-y", "-i", ruta, "-af", ", ".join(filtros), "-ac", "2", salida]
 
59
 
60
  def procesar_audio_divino(ruta):
61
  """
62
+ Efecto imponente pero CERCANO para la Voz de Dios.
63
+ Graves profundos sin eco lejano.
64
  """
65
  if not shutil.which("ffmpeg"):
66
  return ruta
67
 
68
  filtros = [
69
+ "atempo=0.88",
70
+ "asetrate=44100*0.88",
71
  "aresample=44100",
72
+ "bass=g=12:f=180",
73
+ "treble=g=-3",
74
+ "aecho=0.85:0.9:50:0.2" # Retardo mínimo para mantener la voz en primer plano
75
  ]
76
  salida = ruta[:-4] + "_divino.mp3"
77
  cmd = ["ffmpeg", "-y", "-i", ruta, "-af", ", ".join(filtros), "-ac", "2", salida]
 
128
 
129
  # --- SEPARACIÓN DE BLOQUES (NARRADOR vs DIOS) PARA ESPAÑOL ---
130
  def segmentar_texto_divino(texto):
 
 
 
131
  patron = r'((?:Y\s+dijo\s+Dios|dijo\s+Dios|Y\s+llamó\s+Dios|Y\s+bendijo\s+Dios)[^:,.–—]*[:,.–—]?\s*)([^.\n]+)'
132
  bloques = []
133
  ultimo_idx = 0
 
157
  rate = "-9%" if es_divino else VOZ_RATE
158
 
159
  try:
 
160
  com = edge_tts.Communicate(texto[:4000], voz, rate=rate, pitch=pitch)
161
  await com.save(ruta)
162
  except Exception:
 
306
  vs = versiculos(lid, cap)
307
  if not vs:
308
  return f"<div class='pasaje'><p>No hay texto para {html.escape(d.get('es',''))} {cap}.</p></div>", "", ""
309
+
310
  filas = [f"<div class='cab'><span class='h'>{html.escape(d.get('heb',''))}</span><br>"
311
  f"{html.escape(d.get('es',''))} {cap}</div>"]
312
  plano_es, plano_he = [], []
313
+
314
  for v in vs:
315
+ # Limpieza de residuos HTML del JSON
316
+ he_raw = v.get("he", "").replace("&nbsp;", " ").replace("{פ}", "").replace("{ס}", "").strip()
317
+ es_raw = v.get("es", "").replace("&nbsp;", " ").strip()
318
+
319
+ he = html.escape(he_raw)
320
+ es = html.escape(es_raw)
321
+
322
  fila = f"<div class='verso'><span class='num'>{v.get('n','')}</span>"
323
  if he:
324
+ fila += f"<span class='he'>{he}</span>"
325
+ plano_he.append(he_raw)
326
  if es:
327
+ fila += f"<span class='es'>{es}</span>"
328
+ plano_es.append(es_raw)
329
  fila += "</div>"
330
  filas.append(fila)
331
+
332
  return f"<div class='pasaje'>{''.join(filas)}</div>", " ".join(plano_es), " ".join(plano_he)
333
 
334
 
 
366
  st_cap = gr.State(1)
367
 
368
  gr.HTML("<div id='cabecera'><div class='estrella'>✡</div>"
369
+ "<h1>Centralita Torá</h1><p>hebreo y español · canto Trope real y Voz Divina cercana</p></div>")
370
 
371
  with gr.Tab("Leer"):
372
  with gr.Row():