ceh-vedant commited on
Commit
33136cd
·
verified ·
1 Parent(s): 42bdf92

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -57
app.py CHANGED
@@ -2,7 +2,9 @@ import sys
2
  import os
3
  import types
4
  import subprocess
 
5
 
 
6
  if 'audioop' not in sys.modules:
7
  sys.modules['audioop'] = types.ModuleType('audioop')
8
 
@@ -12,6 +14,82 @@ import matplotlib.pyplot as plt
12
  import matplotlib
13
  matplotlib.use('Agg')
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  model = None
16
 
17
  def load_model():
@@ -19,12 +97,19 @@ def load_model():
19
  if model is not None:
20
  return "✅ Already loaded!"
21
  try:
 
 
22
  from tribev2 import TribeModel
23
  model = TribeModel.from_pretrained("facebook/tribev2", cache_folder="/tmp/tribe_cache")
24
  return "✅ Model loaded!"
25
  except Exception as e:
 
 
26
  return f"❌ Error loading model: {str(e)}"
27
 
 
 
 
28
  REGIONS = [
29
  ("Visual cortex", 0.00, 0.15, "#378ADD"),
30
  ("Auditory cortex", 0.15, 0.30, "#D85A30"),
@@ -108,43 +193,9 @@ def generate_suggestions(scores, overall):
108
  status = "🟢 Strong" if overall >= 75 else "🟡 Good, needs polish" if overall >= 55 else "🔴 Needs work"
109
  return f"**Overall: {overall}/100 — {status}**\n\n" + "\n".join(tips)
110
 
111
- def transcribe_audio_to_text(audio_path):
112
- """Use whisperx directly via Python to transcribe audio to a timed text file."""
113
- import whisperx
114
- import json
115
-
116
- hf_token = os.environ.get("HF_TOKEN", "")
117
- device = "cpu"
118
-
119
- print("Loading whisper model...")
120
- wx_model = whisperx.load_model("base", device, compute_type="int8")
121
-
122
- print("Transcribing audio...")
123
- audio = whisperx.load_audio(audio_path)
124
- result = wx_model.transcribe(audio, batch_size=4)
125
-
126
- print("Aligning words...")
127
- model_a, metadata = whisperx.load_align_model(
128
- language_code=result["language"], device=device
129
- )
130
- result = whisperx.align(
131
- result["segments"], model_a, metadata, audio, device,
132
- return_char_alignments=False
133
- )
134
-
135
- # Build a simple timed transcript text file
136
- lines = []
137
- for seg in result["segments"]:
138
- lines.append(seg["text"].strip())
139
- transcript = " ".join(lines)
140
-
141
- text_path = "/tmp/transcript.txt"
142
- with open(text_path, "w") as f:
143
- f.write(transcript)
144
-
145
- print(f"Transcript: {transcript[:200]}...")
146
- return text_path
147
-
148
  def analyze(input_mode, script_text, audio_file, progress=gr.Progress()):
149
  if input_mode == "Text" and (not script_text or not script_text.strip()):
150
  return None, None, "⚠️ Please paste your script text first.", None
@@ -159,12 +210,22 @@ def analyze(input_mode, script_text, audio_file, progress=gr.Progress()):
159
 
160
  try:
161
  if input_mode == "Text":
162
- progress(0.2, desc="Preparing text input...")
163
- text_path = "/tmp/script.txt"
164
- with open(text_path, "w") as f:
165
- f.write(script_text.strip())
166
- progress(0.4, desc="Running TRIBE v2 on text...")
167
- df = model.get_events_dataframe(text_path=text_path)
 
 
 
 
 
 
 
 
 
 
168
 
169
  else:
170
  import shutil
@@ -173,19 +234,8 @@ def analyze(input_mode, script_text, audio_file, progress=gr.Progress()):
173
  audio_path = f"/tmp/input_audio{ext}"
174
  shutil.copy(audio_file, audio_path)
175
 
176
- progress(0.3, desc="Transcribing audio with WhisperX...")
177
- try:
178
- text_path = transcribe_audio_to_text(audio_path)
179
- progress(0.45, desc="Running TRIBE v2 on transcript...")
180
- df = model.get_events_dataframe(
181
- audio_path=audio_path,
182
- text_path=text_path
183
- )
184
- except Exception as wx_err:
185
- # Fallback: use audio path directly and let TRIBE handle it
186
- print(f"WhisperX error (falling back): {wx_err}")
187
- progress(0.45, desc="Running TRIBE v2 on audio directly...")
188
- df = model.get_events_dataframe(audio_path=audio_path)
189
 
190
  progress(0.6, desc="Predicting brain response...")
191
  preds, segments = model.predict(events=df)
@@ -208,6 +258,9 @@ def analyze(input_mode, script_text, audio_file, progress=gr.Progress()):
208
  print(full_error)
209
  return None, None, f"❌ Error:\n{str(e)}\n\nFull traceback:\n{full_error}", None
210
 
 
 
 
211
  css = "#title{text-align:center} #subtitle{text-align:center;color:#888;font-size:14px}"
212
 
213
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo"), css=css) as demo:
@@ -257,4 +310,4 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo"), css=css) as demo:
257
  gr.Markdown("---\n*Powered by [TRIBE v2](https://github.com/facebookresearch/tribev2) by Meta FAIR*")
258
 
259
  if __name__ == "__main__":
260
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
2
  import os
3
  import types
4
  import subprocess
5
+ import logging
6
 
7
+ # Shim for removed audioop module (Python 3.13+)
8
  if 'audioop' not in sys.modules:
9
  sys.modules['audioop'] = types.ModuleType('audioop')
10
 
 
14
  import matplotlib
15
  matplotlib.use('Agg')
16
 
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # ---------------------------------------------------------------------------
20
+ # Monkey-patch TRIBE's whisperx subprocess call to use whisperx as a Python
21
+ # library instead. TRIBE internally calls `uvx whisperx ...` via subprocess,
22
+ # which fails in HuggingFace Spaces. This patch replaces that with a direct
23
+ # Python call to the whisperx library.
24
+ # ---------------------------------------------------------------------------
25
+ def _patched_get_transcript_from_audio(wav_filename, language="english"):
26
+ """Replacement for ExtractWordsFromAudio._get_transcript_from_audio
27
+ that uses whisperx as a Python library instead of a subprocess."""
28
+ import whisperx
29
+ import torch
30
+ from pathlib import Path
31
+
32
+ language_codes = dict(
33
+ english="en", french="fr", spanish="es", dutch="nl", chinese="zh"
34
+ )
35
+ if language not in language_codes:
36
+ raise ValueError(f"Language {language} not supported")
37
+
38
+ device = "cuda" if torch.cuda.is_available() else "cpu"
39
+ compute_type = "float16" if device == "cuda" else "int8"
40
+ lang_code = language_codes[language]
41
+
42
+ logger.info("Loading whisperx model (patched)...")
43
+ wx_model = whisperx.load_model("base", device, compute_type=compute_type, language=lang_code)
44
+
45
+ logger.info(f"Transcribing {wav_filename}...")
46
+ audio = whisperx.load_audio(str(wav_filename))
47
+ result = wx_model.transcribe(audio, batch_size=4)
48
+
49
+ logger.info("Aligning words...")
50
+ model_a, metadata = whisperx.load_align_model(
51
+ language_code=lang_code, device=device
52
+ )
53
+ result = whisperx.align(
54
+ result["segments"], model_a, metadata, audio, device,
55
+ return_char_alignments=False
56
+ )
57
+
58
+ import pandas as pd
59
+ words = []
60
+ for i, segment in enumerate(result.get("segments", [])):
61
+ sentence = segment.get("text", "").replace('"', "")
62
+ for word in segment.get("words", []):
63
+ if "start" not in word:
64
+ continue
65
+ words.append({
66
+ "text": word["word"].replace('"', ""),
67
+ "start": word["start"],
68
+ "duration": word["end"] - word["start"],
69
+ "sequence_id": i,
70
+ "sentence": sentence,
71
+ })
72
+
73
+ return pd.DataFrame(words)
74
+
75
+
76
+ def apply_whisperx_patch():
77
+ """Apply the monkey-patch to TRIBE's ExtractWordsFromAudio class."""
78
+ try:
79
+ from tribev2.eventstransforms import ExtractWordsFromAudio
80
+ ExtractWordsFromAudio._get_transcript_from_audio = staticmethod(
81
+ _patched_get_transcript_from_audio
82
+ )
83
+ logger.info("Successfully patched ExtractWordsFromAudio to use whisperx Python library")
84
+ except Exception as e:
85
+ logger.warning(f"Could not patch ExtractWordsFromAudio: {e}")
86
+
87
+ # Apply the patch before loading the model
88
+ apply_whisperx_patch()
89
+
90
+ # ---------------------------------------------------------------------------
91
+ # Model loading
92
+ # ---------------------------------------------------------------------------
93
  model = None
94
 
95
  def load_model():
 
97
  if model is not None:
98
  return "✅ Already loaded!"
99
  try:
100
+ # Re-apply patch in case import order matters
101
+ apply_whisperx_patch()
102
  from tribev2 import TribeModel
103
  model = TribeModel.from_pretrained("facebook/tribev2", cache_folder="/tmp/tribe_cache")
104
  return "✅ Model loaded!"
105
  except Exception as e:
106
+ import traceback
107
+ traceback.print_exc()
108
  return f"❌ Error loading model: {str(e)}"
109
 
110
+ # ---------------------------------------------------------------------------
111
+ # Brain region definitions (approximate vertex ranges on fsaverage5)
112
+ # ---------------------------------------------------------------------------
113
  REGIONS = [
114
  ("Visual cortex", 0.00, 0.15, "#378ADD"),
115
  ("Auditory cortex", 0.15, 0.30, "#D85A30"),
 
193
  status = "🟢 Strong" if overall >= 75 else "🟡 Good, needs polish" if overall >= 55 else "🔴 Needs work"
194
  return f"**Overall: {overall}/100 — {status}**\n\n" + "\n".join(tips)
195
 
196
+ # ---------------------------------------------------------------------------
197
+ # Main analysis function
198
+ # ---------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  def analyze(input_mode, script_text, audio_file, progress=gr.Progress()):
200
  if input_mode == "Text" and (not script_text or not script_text.strip()):
201
  return None, None, "⚠️ Please paste your script text first.", None
 
210
 
211
  try:
212
  if input_mode == "Text":
213
+ progress(0.2, desc="Converting text to speech...")
214
+
215
+ # Convert text → audio with gTTS, then feed as audio_path.
216
+ # This avoids TRIBE's internal TextToEvents path which also
217
+ # calls whisperx via subprocess after doing the same gTTS step.
218
+ from gtts import gTTS
219
+ from langdetect import detect
220
+
221
+ text = script_text.strip()
222
+ lang = detect(text)
223
+ audio_path = "/tmp/script_audio.mp3"
224
+ tts = gTTS(text=text, lang=lang)
225
+ tts.save(audio_path)
226
+
227
+ progress(0.4, desc="Running TRIBE v2 on generated audio...")
228
+ df = model.get_events_dataframe(audio_path=audio_path)
229
 
230
  else:
231
  import shutil
 
234
  audio_path = f"/tmp/input_audio{ext}"
235
  shutil.copy(audio_file, audio_path)
236
 
237
+ progress(0.4, desc="Running TRIBE v2 on audio...")
238
+ df = model.get_events_dataframe(audio_path=audio_path)
 
 
 
 
 
 
 
 
 
 
 
239
 
240
  progress(0.6, desc="Predicting brain response...")
241
  preds, segments = model.predict(events=df)
 
258
  print(full_error)
259
  return None, None, f"❌ Error:\n{str(e)}\n\nFull traceback:\n{full_error}", None
260
 
261
+ # ---------------------------------------------------------------------------
262
+ # Gradio UI
263
+ # ---------------------------------------------------------------------------
264
  css = "#title{text-align:center} #subtitle{text-align:center;color:#888;font-size:14px}"
265
 
266
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo"), css=css) as demo:
 
310
  gr.Markdown("---\n*Powered by [TRIBE v2](https://github.com/facebookresearch/tribev2) by Meta FAIR*")
311
 
312
  if __name__ == "__main__":
313
+ demo.launch(server_name="0.0.0.0", server_port=7860)