mythaitts commited on
Commit
169bd94
Β·
verified Β·
1 Parent(s): 414b060

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +39 -109
app.py CHANGED
@@ -13,16 +13,14 @@ import whisperx
13
  # ─────────────────────────────────────────────
14
  # Config
15
  # ─────────────────────────────────────────────
16
- DEVICE = "cpu" # change to "cuda" if you have GPU
17
- COMPUTE_TYPE = "int8" # "float16" on GPU, "int8" good on CPU
18
- WHISPER_MODEL_SIZE = "tiny" # "tiny", "base", "small", "medium", "large-v3" ...
19
  SAMPLE_RATE = 16000
20
- MODEL_LANGUAGE = None # None = multilingual / auto-detect
21
 
22
  # Global (lazy loaded)
23
  asr_model = None
24
- align_model = None
25
- align_metadata = None
26
 
27
  def load_whisperx_model():
28
  global asr_model
@@ -32,117 +30,54 @@ def load_whisperx_model():
32
  WHISPER_MODEL_SIZE,
33
  device=DEVICE,
34
  compute_type=COMPUTE_TYPE,
35
- language=MODEL_LANGUAGE, # None = auto
36
  download_root=os.path.expanduser("~/.cache/whisperx")
37
  )
38
  print("βœ… WhisperX transcription model loaded.")
39
  return asr_model
40
 
41
- def load_align_model(lang_code: str):
42
- global align_model, align_metadata
43
- if align_model is None or align_metadata is None:
44
- print(f"⏳ Loading alignment model for language '{lang_code}' …")
45
- align_model, align_metadata = whisperx.load_align_model(
46
- language_code=lang_code,
47
- device=DEVICE
48
- )
49
- print("βœ… Alignment model loaded.")
50
- return align_model, align_metadata
51
-
52
  # ─────────────────────────────────────────────
53
- # Core transcription + alignment
54
  # ─────────────────────────────────────────────
55
  def transcribe_audio(
56
  audio_path: str,
57
  language: str = "auto",
58
  task: str = "transcribe",
59
- timestamps: str = "segment" # "none", "segment", "word"
60
  ) -> dict:
61
- global asr_model
62
-
63
  print(f"Processing audio: {audio_path}")
64
-
65
- # Load audio (whisperx helper handles many formats)
66
  audio = whisperx.load_audio(audio_path)
67
-
68
- # Load transcription model
69
  model = load_whisperx_model()
70
-
71
  # Transcribe
72
  transcribe_options = {
73
  "language": None if language == "auto" else language,
74
  "task": task,
75
- "batch_size": 8 if DEVICE == "cpu" else 16, # tune for your RAM
76
  "chunk_size": 30,
77
  }
78
-
79
  result = model.transcribe(audio, **transcribe_options)
80
-
81
  full_text = result["text"].strip()
82
  detected_lang = result.get("language", "unknown")
83
-
84
- chunks_out = []
85
-
86
- if timestamps != "none" and "segments" in result:
87
- # Load alignment model (only once per language)
88
- align_model_, meta = load_align_model(detected_lang)
89
-
90
- # Align β†’ this gives word-level timestamps
91
- aligned_result = whisperx.align(
92
- result["segments"],
93
- align_model_,
94
- meta,
95
- audio,
96
- DEVICE,
97
- return_char_alignments=False # word level
98
- )
99
-
100
- # Format output like your original
101
- for segment in aligned_result["segments"]:
102
- if "words" in segment:
103
- for word_info in segment["words"]:
104
- chunks_out.append({
105
- "text": word_info["word"].strip(),
106
- "timestamp": [
107
- round(word_info.get("start", None), 3),
108
- round(word_info.get("end", None), 3)
109
- ]
110
- })
111
- else:
112
- # fallback to segment level if words missing
113
- chunks_out.append({
114
- "text": segment["text"].strip(),
115
- "timestamp": [
116
- round(segment.get("start", None), 3),
117
- round(segment.get("end", None), 3)
118
- ]
119
- })
120
-
121
  return {
122
  "text": full_text,
123
  "language": detected_lang,
124
- "chunks": chunks_out,
125
  "warning": None
126
  }
127
 
128
- def _chunks_to_display(chunks: list) -> str:
129
- if not chunks:
130
- return "(no timestamped chunks)"
131
- lines = []
132
- for c in chunks:
133
- ts = c.get("timestamp", [None, None])
134
- s = f"{ts[0]:.2f}s" if ts[0] is not None else "?"
135
- e = f"{ts[1]:.2f}s" if ts[1] is not None else "?"
136
- lines.append(f"[{s} β†’ {e}] {c['text']}")
137
- return "\n".join(lines)
138
-
139
  # ─────────────────────────────────────────────
140
  # FastAPI app
141
  # ─────────────────────────────────────────────
142
  app = FastAPI(
143
- title="MythAI STT β€” WhisperX Edition",
144
- description="Speech-to-Text with word-level timestamps via WhisperX + faster-whisper",
145
- version="3.0-whisperx",
146
  )
147
 
148
  app.add_middleware(
@@ -155,9 +90,9 @@ app.add_middleware(
155
  @app.get("/")
156
  async def root():
157
  return {
158
- "message": "MythAI STT (WhisperX) is active",
159
  "model": f"faster-whisper-{WHISPER_MODEL_SIZE}",
160
- "timestamps": "word-level via alignment",
161
  "ui": "/ui",
162
  }
163
 
@@ -166,7 +101,6 @@ async def health():
166
  return {
167
  "status": "ok",
168
  "model_loaded": asr_model is not None,
169
- "align_loaded": align_model is not None,
170
  }
171
 
172
  @app.post("/transcribe")
@@ -174,7 +108,6 @@ async def transcribe_endpoint(
174
  file: UploadFile = File(...),
175
  language: str = Query("auto"),
176
  task: str = Query("transcribe", pattern="^(transcribe|translate)$"),
177
- timestamps: str = Query("word", pattern="^(none|segment|word)$"),
178
  ):
179
  try:
180
  suffix = os.path.splitext(file.filename or "audio")[1] or ".wav"
@@ -186,18 +119,19 @@ async def transcribe_endpoint(
186
  tmp_path,
187
  language=language,
188
  task=task,
189
- timestamps=timestamps,
190
  )
 
191
  os.unlink(tmp_path)
192
  return JSONResponse(content=result)
 
193
  except Exception as e:
194
  print(f"API error: {e}")
195
  return JSONResponse(status_code=500, content={"error": str(e)})
196
 
197
  # ─────────────────────────────────────────────
198
- # Gradio UI
199
  # ─────────────────────────────────────────────
200
- def gradio_transcribe(audio_input, language: str, task: str, timestamps: str):
201
  try:
202
  if audio_input is None:
203
  return "⚠️ Upload or record audio first.", ""
@@ -222,7 +156,6 @@ def gradio_transcribe(audio_input, language: str, task: str, timestamps: str):
222
  tmp_path,
223
  language if language != "Auto-detect" else "auto",
224
  task.lower(),
225
- timestamps.lower(),
226
  )
227
  finally:
228
  if cleanup:
@@ -231,9 +164,7 @@ def gradio_transcribe(audio_input, language: str, task: str, timestamps: str):
231
  lang_note = f"**Detected language:** {result['language']}\n\n"
232
  transcript = lang_note + result["text"]
233
 
234
- segments_txt = _chunks_to_display(result["chunks"])
235
-
236
- return transcript, segments_txt
237
 
238
  except Exception as e:
239
  msg = f"❌ Error: {str(e)}"
@@ -241,16 +172,17 @@ def gradio_transcribe(audio_input, language: str, task: str, timestamps: str):
241
  return msg, ""
242
 
243
  # UI setup
244
- LANG_OPTIONS = ["Auto-detect", "en", "es", "fr", "de", "it", "ja", "zh", "ru", "ko", "pt"] # add more if needed
245
 
246
  with gr.Blocks(
247
- title="MythAI STT β€” WhisperX",
248
  theme=gr.themes.Soft(primary_hue="violet", secondary_hue="purple"),
249
  ) as demo:
250
  gr.Markdown("""
251
- # 🎀 MythAI STT β€” WhisperX Edition
252
- Fast transcription + **word-level timestamps** using faster-whisper + forced alignment
253
- (much more accurate than native Whisper timestamps)
 
254
  """)
255
 
256
  with gr.Row():
@@ -259,23 +191,21 @@ with gr.Blocks(
259
  with gr.Column():
260
  language_dd = gr.Dropdown(LANG_OPTIONS, value="Auto-detect", label="Language")
261
  task_radio = gr.Radio(["transcribe", "translate"], value="transcribe", label="Task")
262
- timestamps_radio = gr.Radio(["none", "segment", "word"], value="word", label="Timestamps")
263
-
264
  submit_btn = gr.Button("Transcribe", variant="primary")
265
 
266
  transcript_out = gr.Markdown(label="Transcript")
267
- segments_out = gr.Textbox(label="Word / Segment Timestamps", lines=12, interactive=False)
268
 
269
  submit_btn.click(
270
  gradio_transcribe,
271
- inputs=[audio_input, language_dd, task_radio, timestamps_radio],
272
- outputs=[transcript_out, segments_out]
273
  )
274
 
275
  gr.Markdown("""
276
  ---
277
- **API Endpoints**
278
- β€’ POST /transcribe (file, language, task, timestamps)
279
  β€’ GET /health
280
  """)
281
 
@@ -285,8 +215,8 @@ app = gr.mount_gradio_app(app, demo, path="/ui")
285
  if __name__ == "__main__":
286
  import uvicorn
287
  print("\n" + "="*60)
288
- print("MythAI STT (WhisperX) starting …")
289
- print("UI: http://0.0.0.0:7860/ui")
290
- print("API docs: http://0.0.0.0:7860/docs")
291
  print("="*60 + "\n")
292
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
13
  # ─────────────────────────────────────────────
14
  # Config
15
  # ─────────────────────────────────────────────
16
+ DEVICE = "cpu" # change to "cuda" if you have GPU
17
+ COMPUTE_TYPE = "int8" # "float16" on GPU, "int8" is good on CPU
18
+ WHISPER_MODEL_SIZE = "tiny" # "tiny", "base", "small", "medium", "large-v3" ...
19
  SAMPLE_RATE = 16000
20
+ MODEL_LANGUAGE = None # None = multilingual / auto-detect
21
 
22
  # Global (lazy loaded)
23
  asr_model = None
 
 
24
 
25
  def load_whisperx_model():
26
  global asr_model
 
30
  WHISPER_MODEL_SIZE,
31
  device=DEVICE,
32
  compute_type=COMPUTE_TYPE,
33
+ language=MODEL_LANGUAGE, # None = auto
34
  download_root=os.path.expanduser("~/.cache/whisperx")
35
  )
36
  print("βœ… WhisperX transcription model loaded.")
37
  return asr_model
38
 
 
 
 
 
 
 
 
 
 
 
 
39
  # ─────────────────────────────────────────────
40
+ # Core transcription (text only β€” no timestamps)
41
  # ─────────────────────────────────────────────
42
  def transcribe_audio(
43
  audio_path: str,
44
  language: str = "auto",
45
  task: str = "transcribe",
 
46
  ) -> dict:
 
 
47
  print(f"Processing audio: {audio_path}")
48
+
49
+ # Load audio
50
  audio = whisperx.load_audio(audio_path)
51
+
52
+ # Load model
53
  model = load_whisperx_model()
54
+
55
  # Transcribe
56
  transcribe_options = {
57
  "language": None if language == "auto" else language,
58
  "task": task,
59
+ "batch_size": 8 if DEVICE == "cpu" else 16,
60
  "chunk_size": 30,
61
  }
62
+
63
  result = model.transcribe(audio, **transcribe_options)
64
+
65
  full_text = result["text"].strip()
66
  detected_lang = result.get("language", "unknown")
67
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  return {
69
  "text": full_text,
70
  "language": detected_lang,
 
71
  "warning": None
72
  }
73
 
 
 
 
 
 
 
 
 
 
 
 
74
  # ─────────────────────────────────────────────
75
  # FastAPI app
76
  # ─────────────────────────────────────────────
77
  app = FastAPI(
78
+ title="MythAI STT β€” WhisperX Simple",
79
+ description="Basic Speech-to-Text using WhisperX / faster-whisper (text only)",
80
+ version="3.0-simple",
81
  )
82
 
83
  app.add_middleware(
 
90
  @app.get("/")
91
  async def root():
92
  return {
93
+ "message": "MythAI STT (simple text-only) is active",
94
  "model": f"faster-whisper-{WHISPER_MODEL_SIZE}",
95
+ "timestamps": "none",
96
  "ui": "/ui",
97
  }
98
 
 
101
  return {
102
  "status": "ok",
103
  "model_loaded": asr_model is not None,
 
104
  }
105
 
106
  @app.post("/transcribe")
 
108
  file: UploadFile = File(...),
109
  language: str = Query("auto"),
110
  task: str = Query("transcribe", pattern="^(transcribe|translate)$"),
 
111
  ):
112
  try:
113
  suffix = os.path.splitext(file.filename or "audio")[1] or ".wav"
 
119
  tmp_path,
120
  language=language,
121
  task=task,
 
122
  )
123
+
124
  os.unlink(tmp_path)
125
  return JSONResponse(content=result)
126
+
127
  except Exception as e:
128
  print(f"API error: {e}")
129
  return JSONResponse(status_code=500, content={"error": str(e)})
130
 
131
  # ─────────────────────────────────────────────
132
+ # Gradio UI β€” simplified (no timestamp options)
133
  # ─────────────────────────────────────────────
134
+ def gradio_transcribe(audio_input, language: str, task: str):
135
  try:
136
  if audio_input is None:
137
  return "⚠️ Upload or record audio first.", ""
 
156
  tmp_path,
157
  language if language != "Auto-detect" else "auto",
158
  task.lower(),
 
159
  )
160
  finally:
161
  if cleanup:
 
164
  lang_note = f"**Detected language:** {result['language']}\n\n"
165
  transcript = lang_note + result["text"]
166
 
167
+ return transcript, ""
 
 
168
 
169
  except Exception as e:
170
  msg = f"❌ Error: {str(e)}"
 
172
  return msg, ""
173
 
174
  # UI setup
175
+ LANG_OPTIONS = ["Auto-detect", "en", "es", "fr", "de", "it", "ja", "zh", "ru", "ko", "pt"]
176
 
177
  with gr.Blocks(
178
+ title="MythAI STT β€” Simple",
179
  theme=gr.themes.Soft(primary_hue="violet", secondary_hue="purple"),
180
  ) as demo:
181
  gr.Markdown("""
182
+ # 🎀 MythAI STT β€” Simple Edition
183
+ Fast plain-text transcription using **faster-whisper**
184
+
185
+ (no timestamps / alignment β€” just the text)
186
  """)
187
 
188
  with gr.Row():
 
191
  with gr.Column():
192
  language_dd = gr.Dropdown(LANG_OPTIONS, value="Auto-detect", label="Language")
193
  task_radio = gr.Radio(["transcribe", "translate"], value="transcribe", label="Task")
 
 
194
  submit_btn = gr.Button("Transcribe", variant="primary")
195
 
196
  transcript_out = gr.Markdown(label="Transcript")
197
+ gr.Markdown("No word/segment timestamps in this version.")
198
 
199
  submit_btn.click(
200
  gradio_transcribe,
201
+ inputs=[audio_input, language_dd, task_radio],
202
+ outputs=[transcript_out, gr.State()] # dummy second output to keep layout
203
  )
204
 
205
  gr.Markdown("""
206
  ---
207
+ **API Endpoints**
208
+ β€’ POST /transcribe (file, language, task)
209
  β€’ GET /health
210
  """)
211
 
 
215
  if __name__ == "__main__":
216
  import uvicorn
217
  print("\n" + "="*60)
218
+ print("MythAI STT (simple text-only) starting …")
219
+ print("UI: http://0.0.0.0:7860/ui")
220
+ print("Docs: http://0.0.0.0:7860/docs")
221
  print("="*60 + "\n")
222
  uvicorn.run(app, host="0.0.0.0", port=7860)