Nolist commited on
Commit
9d56db4
·
verified ·
1 Parent(s): 8fd7b39

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -77
app.py CHANGED
@@ -11,34 +11,26 @@ import gradio as gr
11
  from transformers import pipeline
12
  from transformers.pipelines.audio_utils import ffmpeg_read
13
 
14
- # --- Firebase config (try gradio secrets, otherwise environment) --
15
- # On HF Spaces you can set secrets; fallback to environment variables if needed.
16
- try:
17
- FIREBASE_URL = gr.secrets.get("databaseURL") # gradio secrets object; use .get for safety
18
- except Exception:
19
- # older gradio versions expose a dict-like object; try attribute access
20
  try:
21
- FIREBASE_URL = gr.secrets("databaseURL")
22
  except Exception:
23
- FIREBASE_URL = None
 
 
 
 
24
 
25
- FIREBASE_AUTH = None
26
- try:
27
- FIREBASE_AUTH = gr.secrets.get("apiKey")
28
- except Exception:
29
- try:
30
- FIREBASE_AUTH = gr.secrets("apiKey")
31
- except Exception:
32
- FIREBASE_AUTH = None
33
 
34
- # fallback to environment variables if secrets not provided
35
- if not FIREBASE_URL:
36
- FIREBASE_URL = os.environ.get("FIREBASE_DATABASE_URL") or os.environ.get("FIREBASE_URL")
37
  if FIREBASE_URL:
38
  FIREBASE_URL = FIREBASE_URL.rstrip("/")
39
 
40
- if not FIREBASE_AUTH:
41
- FIREBASE_AUTH = os.environ.get("FIREBASE_API_KEY") or os.environ.get("FIREBASE_AUTH")
42
 
43
  # requests (required for Firebase REST calls)
44
  try:
@@ -66,12 +58,8 @@ SLEEP_BETWEEN_CHUNKS = 0.01
66
 
67
 
68
  def _merge_with_overlap(accumulated: str, chunk_text: str, max_overlap_words: int = 12) -> str:
69
- """
70
- Merge chunk_text into accumulated, removing duplicated overlap (word-based).
71
- """
72
  if not accumulated:
73
  return chunk_text
74
-
75
  acc_words = accumulated.strip().split()
76
  chunk_words = chunk_text.strip().split()
77
  if not chunk_words:
@@ -93,27 +81,29 @@ def _merge_with_overlap(accumulated: str, chunk_text: str, max_overlap_words: in
93
 
94
 
95
  # ------------------------------
96
- # Firebase helpers (GET + PUT with merging) - using key "/transcribe"
97
  # ------------------------------
98
  def _firebase_headers():
99
- """Return headers for Firebase REST call (none required for Realtime DB default)."""
100
  return {"Content-Type": "application/json"}
101
 
102
 
103
  def _firebase_get(path: str) -> Optional[object]:
104
- """
105
- GET value from Firebase at given path (path without leading slash, e.g. 'transcribe').
106
- Returns parsed JSON value or None if not found / error.
107
- """
108
  if not FIREBASE_URL or requests is None:
109
  print("_firebase_get: Firebase not configured or requests missing.")
110
  return None
111
  try:
112
  url = f"{FIREBASE_URL}/{path}.json"
113
  params = {"auth": FIREBASE_AUTH} if FIREBASE_AUTH else None
114
- resp = requests.get(url, params=params, timeout=6)
115
- resp.raise_for_status()
116
- return resp.json()
 
 
 
 
 
 
 
117
  except Exception as e:
118
  print("Firebase GET failed:", e)
119
  return None
@@ -121,29 +111,42 @@ def _firebase_get(path: str) -> Optional[object]:
121
 
122
  def _firebase_put(path: str, value: object) -> bool:
123
  """
124
- PUT value to Firebase at given path (overwrites that key).
125
- Returns True on success, False otherwise.
126
  """
127
  if not FIREBASE_URL or requests is None:
128
  print("Firebase PUT skipped: config or requests missing.")
129
  return False
 
 
 
 
130
  try:
131
- url = f"{FIREBASE_URL}/{path}.json"
132
- params = {"auth": FIREBASE_AUTH} if FIREBASE_AUTH else None
133
- resp = requests.put(url, params=params, json=value, timeout=6)
134
- resp.raise_for_status()
135
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  except Exception as e:
137
- print("Firebase PUT failed:", e)
138
  return False
139
 
140
 
141
  def send_to_firebase_merge(new_transcript: str) -> bool:
142
- """
143
- Merge new_transcript with existing /transcribe value in Firebase (if present),
144
- using the same overlap dedupe logic as the app, then write merged result back.
145
- Returns True if write succeeded, False otherwise.
146
- """
147
  if not FIREBASE_URL:
148
  print("send_to_firebase_merge: FIREBASE_URL not configured.")
149
  return False
@@ -152,32 +155,32 @@ def send_to_firebase_merge(new_transcript: str) -> bool:
152
  return False
153
 
154
  try:
155
- # Read existing transcribe (if any) under the key "transcribe"
156
- existing = _firebase_get("transcribe") # could be None or a string or structured JSON
157
  existing_text = ""
158
  if isinstance(existing, str):
159
  existing_text = existing
160
- elif isinstance(existing, dict) and "transcribe" in existing and isinstance(existing["transcribe"], str):
161
- # handle previous structure that stored object with transcribe key
162
- existing_text = existing["transcribe"]
163
  elif existing is None:
164
  existing_text = ""
165
  else:
166
- # if other types, coerce to string safely
167
  existing_text = str(existing)
168
 
169
- # Merge using overlap dedupe
170
  merged = _merge_with_overlap(existing_text, new_transcript)
171
-
172
- # Debug/logging
173
- print(f"Existing length: {len(existing_text)}, New chunk length: {len(new_transcript)}, Merged length: {len(merged)}")
174
-
175
- # Write merged plain string under /transcribe (overwrites)
176
- success = _firebase_put("transcribe", merged)
 
 
 
177
  if success:
178
  print("Successfully wrote merged transcribe to Firebase (/transcribe).")
179
  else:
180
- print("Failed to write merged transcribe to Firebase.")
181
  return success
182
  except Exception as e:
183
  print("send_to_firebase_merge: exception:", e)
@@ -185,25 +188,18 @@ def send_to_firebase_merge(new_transcript: str) -> bool:
185
 
186
 
187
  # ------------------------------
188
- # Transcription generator
189
  # ------------------------------
190
  def transcribe_file(filepath: str):
191
- """
192
- Generator that yields (transcript_text, continue_html_update).
193
- After final chunk: attempt to merge+write to Firebase under /transcribe.
194
- Show Continue only if write succeeds; otherwise show an error HTML message.
195
- """
196
  if not filepath:
197
  raise gr.Error("No audio recorded! Please record audio (max 59s recommended).")
198
 
199
  try:
200
  sr = pipe.feature_extractor.sampling_rate
201
 
202
- # Read audio bytes
203
  if isinstance(filepath, (bytes, bytearray)):
204
  audio_array = ffmpeg_read(filepath, sr)
205
  else:
206
- # filepath is a local path provided by gradio when type="filepath"
207
  with open(filepath, "rb") as f:
208
  audio_array = ffmpeg_read(f.read(), sr)
209
 
@@ -221,7 +217,6 @@ def transcribe_file(filepath: str):
221
  for i in range(num_chunks):
222
  start = i * chunk_samples
223
  end = min((i + 1) * chunk_samples, total_samples)
224
-
225
  if i > 0:
226
  start = max(0, start - overlap_samples)
227
  if i < num_chunks - 1:
@@ -237,12 +232,10 @@ def transcribe_file(filepath: str):
237
 
238
  accumulated_text = _merge_with_overlap(accumulated_text, chunk_text)
239
 
240
- # yield partial; keep Continue hidden
241
  yield accumulated_text, gr.update(visible=False)
242
-
243
  time.sleep(SLEEP_BETWEEN_CHUNKS)
244
 
245
- # Final: attempt to write merged transcript into Firebase under /transcribe
246
  write_ok = False
247
  try:
248
  write_ok = send_to_firebase_merge(accumulated_text)
@@ -250,7 +243,6 @@ def transcribe_file(filepath: str):
250
  print("Error while sending to Firebase:", e)
251
 
252
  if write_ok:
253
- # Continue button only when Firebase write succeeded
254
  continue_html = """
255
  <div style="text-align:center; margin-top:20px;">
256
  <a href="https://nolist-hospitalcentral.hf.space"
@@ -270,7 +262,6 @@ def transcribe_file(filepath: str):
270
  """
271
  yield accumulated_text, gr.update(value=continue_html, visible=True)
272
  else:
273
- # Show error HTML and keep Continue hidden
274
  error_html = """
275
  <div style="text-align:center; margin-top:20px; color:#b91c1c;">
276
  <strong>Failed to save transcribe to Firebase.</strong><br/>
@@ -298,8 +289,7 @@ with gr.Blocks(title="Record Option") as demo:
298
  output_text = gr.Textbox(label="Transcription", lines=12)
299
  continue_block = gr.HTML(visible=False)
300
 
301
- # Auto-trigger transcribe when recording upload completes
302
  audio_input.change(fn=transcribe_file, inputs=[audio_input], outputs=[output_text, continue_block])
303
 
304
  if __name__ == "__main__":
305
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
11
  from transformers import pipeline
12
  from transformers.pipelines.audio_utils import ffmpeg_read
13
 
14
+ # --- Firebase config (gradio secrets or environment) ---
15
+ # On HF Spaces set secrets as: databaseURL and databaseSecret (or FIREBASE_API_KEY if using other flows)
16
+ def _get_secret_safe(name):
 
 
 
17
  try:
18
+ return gr.secrets.get(name)
19
  except Exception:
20
+ try:
21
+ # older gradio: callable or dict-like
22
+ return gr.secrets(name)
23
+ except Exception:
24
+ return None
25
 
26
+ FIREBASE_URL = _get_secret_safe("databaseURL") or os.environ.get("FIREBASE_DATABASE_URL") or os.environ.get("FIREBASE_URL")
27
+ FIREBASE_AUTH = _get_secret_safe("databaseSecret") or _get_secret_safe("apiKey") or os.environ.get("FIREBASE_API_KEY") or os.environ.get("FIREBASE_AUTH")
 
 
 
 
 
 
28
 
 
 
 
29
  if FIREBASE_URL:
30
  FIREBASE_URL = FIREBASE_URL.rstrip("/")
31
 
32
+ print("Firebase URL:", FIREBASE_URL)
33
+ print("Firebase Auth present:", bool(FIREBASE_AUTH))
34
 
35
  # requests (required for Firebase REST calls)
36
  try:
 
58
 
59
 
60
  def _merge_with_overlap(accumulated: str, chunk_text: str, max_overlap_words: int = 12) -> str:
 
 
 
61
  if not accumulated:
62
  return chunk_text
 
63
  acc_words = accumulated.strip().split()
64
  chunk_words = chunk_text.strip().split()
65
  if not chunk_words:
 
81
 
82
 
83
  # ------------------------------
84
+ # Firebase helpers (GET + PUT with merging)
85
  # ------------------------------
86
  def _firebase_headers():
 
87
  return {"Content-Type": "application/json"}
88
 
89
 
90
  def _firebase_get(path: str) -> Optional[object]:
 
 
 
 
91
  if not FIREBASE_URL or requests is None:
92
  print("_firebase_get: Firebase not configured or requests missing.")
93
  return None
94
  try:
95
  url = f"{FIREBASE_URL}/{path}.json"
96
  params = {"auth": FIREBASE_AUTH} if FIREBASE_AUTH else None
97
+ print(f"GET {url} params={params}")
98
+ resp = requests.get(url, params=params, timeout=8)
99
+ print("GET status:", resp.status_code)
100
+ try:
101
+ print("GET response:", resp.text)
102
+ resp.raise_for_status()
103
+ return resp.json()
104
+ except Exception as e:
105
+ print("GET parse/raise:", e)
106
+ return None
107
  except Exception as e:
108
  print("Firebase GET failed:", e)
109
  return None
 
111
 
112
  def _firebase_put(path: str, value: object) -> bool:
113
  """
114
+ Try PUT; if auth is present but fails, print response. If it fails and auth was provided, try unauthenticated attempt
115
+ (useful to detect whether your DB requires auth or not).
116
  """
117
  if not FIREBASE_URL or requests is None:
118
  print("Firebase PUT skipped: config or requests missing.")
119
  return False
120
+
121
+ url = f"{FIREBASE_URL}/{path}.json"
122
+ params = {"auth": FIREBASE_AUTH} if FIREBASE_AUTH else None
123
+
124
  try:
125
+ print(f"Attempting PUT {url} params={params} payload_len={len(value) if isinstance(value, str) else 'obj'}")
126
+ resp = requests.put(url, params=params, json=value, timeout=10)
127
+ print("PUT status:", resp.status_code)
128
+ print("PUT response:", resp.text)
129
+ if resp.ok:
130
+ return True
131
+ else:
132
+ # If auth was used and failed, try unauthenticated (to differentiate rules vs invalid auth)
133
+ if FIREBASE_AUTH:
134
+ print("PUT with auth failed. Trying unauthenticated PUT to help diagnosis...")
135
+ try:
136
+ resp2 = requests.put(url, json=value, timeout=10)
137
+ print("Unauth PUT status:", resp2.status_code)
138
+ print("Unauth PUT response:", resp2.text)
139
+ return resp2.ok
140
+ except Exception as e2:
141
+ print("Unauth PUT attempt failed:", e2)
142
+ return False
143
+ return False
144
  except Exception as e:
145
+ print("Firebase PUT failed (exception):", e)
146
  return False
147
 
148
 
149
  def send_to_firebase_merge(new_transcript: str) -> bool:
 
 
 
 
 
150
  if not FIREBASE_URL:
151
  print("send_to_firebase_merge: FIREBASE_URL not configured.")
152
  return False
 
155
  return False
156
 
157
  try:
158
+ existing = _firebase_get("transcribe")
 
159
  existing_text = ""
160
  if isinstance(existing, str):
161
  existing_text = existing
162
+ elif isinstance(existing, dict) and "text" in existing and isinstance(existing["text"], str):
163
+ # if previously stored as object like {"text": "..."}
164
+ existing_text = existing["text"]
165
  elif existing is None:
166
  existing_text = ""
167
  else:
 
168
  existing_text = str(existing)
169
 
 
170
  merged = _merge_with_overlap(existing_text, new_transcript)
171
+ # Save as object with timestamp to make debugging easier
172
+ payload = {
173
+ "text": merged,
174
+ "updated_at": datetime.utcnow().isoformat() + "Z",
175
+ "length": len(merged),
176
+ }
177
+ print("Prepared payload for /transcribe:", {k: ("<len>" if k=="text" else v) for k, v in payload.items()})
178
+
179
+ success = _firebase_put("transcribe", payload)
180
  if success:
181
  print("Successfully wrote merged transcribe to Firebase (/transcribe).")
182
  else:
183
+ print("Failed to write merged transcribe to Firebase (/transcribe).")
184
  return success
185
  except Exception as e:
186
  print("send_to_firebase_merge: exception:", e)
 
188
 
189
 
190
  # ------------------------------
191
+ # Transcription generator (same as before)
192
  # ------------------------------
193
  def transcribe_file(filepath: str):
 
 
 
 
 
194
  if not filepath:
195
  raise gr.Error("No audio recorded! Please record audio (max 59s recommended).")
196
 
197
  try:
198
  sr = pipe.feature_extractor.sampling_rate
199
 
 
200
  if isinstance(filepath, (bytes, bytearray)):
201
  audio_array = ffmpeg_read(filepath, sr)
202
  else:
 
203
  with open(filepath, "rb") as f:
204
  audio_array = ffmpeg_read(f.read(), sr)
205
 
 
217
  for i in range(num_chunks):
218
  start = i * chunk_samples
219
  end = min((i + 1) * chunk_samples, total_samples)
 
220
  if i > 0:
221
  start = max(0, start - overlap_samples)
222
  if i < num_chunks - 1:
 
232
 
233
  accumulated_text = _merge_with_overlap(accumulated_text, chunk_text)
234
 
 
235
  yield accumulated_text, gr.update(visible=False)
 
236
  time.sleep(SLEEP_BETWEEN_CHUNKS)
237
 
238
+ # Final write
239
  write_ok = False
240
  try:
241
  write_ok = send_to_firebase_merge(accumulated_text)
 
243
  print("Error while sending to Firebase:", e)
244
 
245
  if write_ok:
 
246
  continue_html = """
247
  <div style="text-align:center; margin-top:20px;">
248
  <a href="https://nolist-hospitalcentral.hf.space"
 
262
  """
263
  yield accumulated_text, gr.update(value=continue_html, visible=True)
264
  else:
 
265
  error_html = """
266
  <div style="text-align:center; margin-top:20px; color:#b91c1c;">
267
  <strong>Failed to save transcribe to Firebase.</strong><br/>
 
289
  output_text = gr.Textbox(label="Transcription", lines=12)
290
  continue_block = gr.HTML(visible=False)
291
 
 
292
  audio_input.change(fn=transcribe_file, inputs=[audio_input], outputs=[output_text, continue_block])
293
 
294
  if __name__ == "__main__":
295
+ demo.launch(server_name="0.0.0.0", server_port=7860)