Cristobal299 commited on
Commit
d8a55e9
·
verified ·
1 Parent(s): 9787e23

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +38 -10
  2. gradio_theme_fenix.py +19 -0
  3. requirements.txt +1 -0
app.py CHANGED
@@ -5,13 +5,25 @@ Leer hebreo + español · escuchar (voces gratis) · estudiar con IA (Groq).
5
  Los libros se cargan desde la carpeta libros/ (un JSON por libro).
6
  Secret opcional para la pestaña Estudiar: GROQ_API_KEY
7
  """
8
- import os, glob, json, tempfile, html
9
  import urllib.request
10
  import gradio as gr
11
- from gtts import gTTS
 
 
 
 
12
 
13
  from gradio_theme_fenix import fenix_theme, FENIX_CSS
14
 
 
 
 
 
 
 
 
 
15
  MODELO_ESTUDIA = "openai/gpt-oss-120b"
16
  CARPETA = "libros"
17
  GROQ_URL = "https://api.groq.com/openai/v1/chat/completions"
@@ -77,20 +89,36 @@ def render(nombre, cap):
77
  return f"<div class='pasaje'>{''.join(filas)}</div>", " ".join(plano_es), " ".join(plano_he)
78
 
79
 
80
- # ---------- voces ----------
81
- def a_voz(texto, idioma):
82
  if not texto or not texto.strip():
83
  return None
 
84
  ruta = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False).name
85
- for lang in ([idioma] if idioma in ("es",) else ["iw", "he"]):
 
 
 
 
 
 
 
86
  try:
87
- gTTS(text=texto[:4500], lang=lang).save(ruta)
88
  return ruta
89
  except Exception:
90
- continue
91
  return None
92
 
93
 
 
 
 
 
 
 
 
 
94
  # ---------- estudio (Groq por HTTP) ----------
95
  def estudiar(mensaje, historial, contexto):
96
  if not GROQ_KEY:
@@ -145,8 +173,8 @@ with gr.Blocks(theme=fenix_theme(), css=FENIX_CSS, title="Centralita Torá") as
145
  return md, es, he, nombre, cap
146
 
147
  ver_btn.click(ver, [dd, ncap], [salida, st_es, st_he, st_nombre, st_cap])
148
- b_es.click(lambda t: a_voz(t, "es"), st_es, audio)
149
- b_he.click(lambda t: a_voz(t, "he"), st_he, audio)
150
 
151
  with gr.Tab("Buscar"):
152
  q = gr.Textbox(label="Palabra o frase (hebreo o español)", placeholder="ej. luz / אור")
@@ -207,4 +235,4 @@ with gr.Blocks(theme=fenix_theme(), css=FENIX_CSS, title="Centralita Torá") as
207
  )
208
 
209
  if __name__ == "__main__":
210
- demo.launch()
 
5
  Los libros se cargan desde la carpeta libros/ (un JSON por libro).
6
  Secret opcional para la pestaña Estudiar: GROQ_API_KEY
7
  """
8
+ import os, glob, json, tempfile, html, asyncio
9
  import urllib.request
10
  import gradio as gr
11
+ import edge_tts
12
+ try:
13
+ from gtts import gTTS
14
+ except Exception:
15
+ gTTS = None
16
 
17
  from gradio_theme_fenix import fenix_theme, FENIX_CSS
18
 
19
+ # Voz única y alegre de la app (edge-tts, gratis). Cámbiala aquí si quieres otra:
20
+ # es: es-ES-ElviraNeural · es-ES-XimenaNeural · es-MX-DaliaNeural · es-CO-SalomeNeural
21
+ # he: he-IL-HilaNeural · he-IL-AvriNeural
22
+ VOZ_ES = "es-ES-ElviraNeural"
23
+ VOZ_HE = "he-IL-HilaNeural"
24
+ VOZ_RATE = "+8%" # un poco más ágil = más alegre
25
+ VOZ_PITCH = "+10Hz" # tono algo más alto = más alegre
26
+
27
  MODELO_ESTUDIA = "openai/gpt-oss-120b"
28
  CARPETA = "libros"
29
  GROQ_URL = "https://api.groq.com/openai/v1/chat/completions"
 
89
  return f"<div class='pasaje'>{''.join(filas)}</div>", " ".join(plano_es), " ".join(plano_he)
90
 
91
 
92
+ # ---------- voces (edge-tts: voz única y alegre, gratis) ----------
93
+ async def a_voz(texto, idioma):
94
  if not texto or not texto.strip():
95
  return None
96
+ voz = VOZ_HE if idioma == "he" else VOZ_ES
97
  ruta = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False).name
98
+ try:
99
+ com = edge_tts.Communicate(texto[:4000], voz, rate=VOZ_RATE, pitch=VOZ_PITCH)
100
+ await com.save(ruta)
101
+ if os.path.getsize(ruta) > 0:
102
+ return ruta
103
+ except Exception:
104
+ pass
105
+ if gTTS: # respaldo por si edge-tts falla
106
  try:
107
+ gTTS(text=texto[:4500], lang=("iw" if idioma == "he" else "es")).save(ruta)
108
  return ruta
109
  except Exception:
110
+ return None
111
  return None
112
 
113
 
114
+ async def leer_es(t):
115
+ return await a_voz(t, "es")
116
+
117
+
118
+ async def leer_he(t):
119
+ return await a_voz(t, "he")
120
+
121
+
122
  # ---------- estudio (Groq por HTTP) ----------
123
  def estudiar(mensaje, historial, contexto):
124
  if not GROQ_KEY:
 
173
  return md, es, he, nombre, cap
174
 
175
  ver_btn.click(ver, [dd, ncap], [salida, st_es, st_he, st_nombre, st_cap])
176
+ b_es.click(leer_es, st_es, audio)
177
+ b_he.click(leer_he, st_he, audio)
178
 
179
  with gr.Tab("Buscar"):
180
  q = gr.Textbox(label="Palabra o frase (hebreo o español)", placeholder="ej. luz / אור")
 
235
  )
236
 
237
  if __name__ == "__main__":
238
+ demo.launch(ssr_mode=False)
gradio_theme_fenix.py CHANGED
@@ -61,4 +61,23 @@ FENIX_CSS = """
61
  .resultado { padding:6px 2px; border-bottom:1px dashed rgba(120,150,200,.25); }
62
  .resultado .ref { color:#c69a34; font-weight:700; }
63
  footer { visibility: hidden; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  """
 
61
  .resultado { padding:6px 2px; border-bottom:1px dashed rgba(120,150,200,.25); }
62
  .resultado .ref { color:#c69a34; font-weight:700; }
63
  footer { visibility: hidden; }
64
+
65
+ /* ---------- responsive (móvil) ---------- */
66
+ @media (max-width: 680px) {
67
+ .gradio-container { max-width: 100% !important; padding: 0 8px !important; }
68
+ #cabecera { padding: 6px 0 2px; }
69
+ #cabecera .estrella { font-size: 26px; }
70
+ #cabecera h1 { font-size: 1.6rem; }
71
+ #cabecera p { font-size: .95rem; }
72
+ .pasaje .he { font-size: 1.45rem; line-height:1.7; }
73
+ .pasaje .es { font-size: 1rem; }
74
+ .pasaje .cab .h { font-size: 1.5rem; }
75
+ /* botones a lo ancho y fáciles de tocar */
76
+ .gr-button { min-height: 46px !important; font-size: 1rem !important; }
77
+ .gr-button, .gr-box, .gr-form { width: 100% !important; }
78
+ }
79
+ @media (max-width: 420px) {
80
+ #cabecera h1 { font-size: 1.4rem; }
81
+ .pasaje .he { font-size: 1.3rem; }
82
+ }
83
  """
requirements.txt CHANGED
@@ -1 +1,2 @@
 
1
  gtts
 
1
+ edge-tts
2
  gtts