Khedhar commited on
Commit
3328a14
Β·
1 Parent(s): 4454058

Add m4a support and audio conversion

Browse files
Files changed (4) hide show
  1. app.py +63 -5
  2. pyproject.toml +1 -0
  3. requirements.txt +1 -0
  4. uv.lock +11 -0
app.py CHANGED
@@ -309,13 +309,29 @@ if 'download_approved' not in st.session_state:
309
  # Show download confirmation ONLY if Local mode AND model not cached AND not yet approved
310
  if not use_cloud and not model_status["is_cached"] and asr_model not in st.session_state.download_approved:
311
  with st.container():
 
 
 
 
 
 
 
312
  st.markdown("---")
313
  st.markdown(f"### ⬇️ Download Required")
 
 
 
 
 
 
 
 
 
314
  st.info(f"""**{asr_model}** is not cached locally.
315
 
316
  πŸ“¦ Size: **{model_status['required_gb']}GB**
317
  πŸ’Ύ Free space: **{model_status['free_gb']}GB**
318
- πŸ“‚ Cache location: `C:\\Users\\{os.environ.get('USERNAME', 'User')}\\.cache\\huggingface\\hub\\`
319
 
320
  πŸ’‘ **Tip:** Switch to Cloud Mode to avoid downloading!
321
  """)
@@ -378,7 +394,7 @@ with col1:
378
  st.warning("Update Streamlit to use `st.audio_input`.")
379
 
380
  with tab2:
381
- audio_val_up = st.file_uploader("Upload Audio", type=['wav', 'mp3'], label_visibility="collapsed")
382
  if audio_val_up:
383
  audio_data = audio_val_up
384
 
@@ -387,16 +403,58 @@ with col1:
387
  # Process Audio
388
  if audio_data:
389
  st.success("βœ… Audio captured!")
390
- st.audio(audio_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
 
392
  if st.button("πŸš€ Process Order", type="primary", use_container_width=True):
393
  transcription_text = ""
394
 
 
 
 
395
  if use_cloud:
396
  # CLOUD MODE - Use HuggingFace Inference API (no download)
397
  with st.spinner("☁️ Transcribing via Cloud..."):
398
  try:
399
- transcription_text = transcribe_cloud(audio_data, asr_model, hf_token)
400
  st.toast("βœ… Cloud Transcription Complete!")
401
  except Exception as e:
402
  st.error(f"❌ Cloud API failed: {e}")
@@ -413,7 +471,7 @@ with col1:
413
  st.stop()
414
 
415
  with st.spinner("🎧 Transcribing Locally..."):
416
- processed_audio = preprocessor.process(audio_data)
417
  result = asr(processed_audio)
418
  transcription_text = result["text"].replace("</s>", "").strip()
419
  st.toast("βœ… Local Transcription Complete!")
 
309
  # Show download confirmation ONLY if Local mode AND model not cached AND not yet approved
310
  if not use_cloud and not model_status["is_cached"] and asr_model not in st.session_state.download_approved:
311
  with st.container():
312
+ # Create a placeholder for status updates
313
+ status_placeholder = st.empty()
314
+
315
+ # Display current status in placeholder
316
+ if status_html:
317
+ status_placeholder.markdown(f'<div style="display: flex; justify-content: flex-end; margin-bottom: 20px;">{status_html}</div>', unsafe_allow_html=True)
318
+
319
  st.markdown("---")
320
  st.markdown(f"### ⬇️ Download Required")
321
+
322
+ # Dynamic path display
323
+ import platform
324
+ user_home = Path.home()
325
+ if platform.system() == "Windows":
326
+ cache_path_str = f"C:\\Users\\{os.environ.get('USERNAME', 'User')}\\.cache\\huggingface\\hub\\"
327
+ else:
328
+ cache_path_str = f"/home/user/.cache/huggingface/hub/"
329
+
330
  st.info(f"""**{asr_model}** is not cached locally.
331
 
332
  πŸ“¦ Size: **{model_status['required_gb']}GB**
333
  πŸ’Ύ Free space: **{model_status['free_gb']}GB**
334
+ πŸ“‚ Cache location: `{cache_path_str}`
335
 
336
  πŸ’‘ **Tip:** Switch to Cloud Mode to avoid downloading!
337
  """)
 
394
  st.warning("Update Streamlit to use `st.audio_input`.")
395
 
396
  with tab2:
397
+ audio_val_up = st.file_uploader("Upload Audio", type=['wav', 'mp3', 'm4a', 'ogg'], label_visibility="collapsed")
398
  if audio_val_up:
399
  audio_data = audio_val_up
400
 
 
403
  # Process Audio
404
  if audio_data:
405
  st.success("βœ… Audio captured!")
406
+
407
+ # Audio Conversion Logic
408
+ # If it's not a WAV file or if it needs processing, we handle conversion here
409
+ converted_audio = None
410
+
411
+ try:
412
+ # Basic check: if it's already a wav and we trust it, we might skip,
413
+ # but converting ensures standardized 16kHz for ASR
414
+ import io
415
+ from pydub import AudioSegment
416
+
417
+ # Load audio from the uploaded file type
418
+ # pydub auto-detects format from file content usually, but we can hint if needed
419
+ audio_segment = AudioSegment.from_file(audio_data)
420
+
421
+ # Export to simplified WAV (16kHz, mono) which is best for ASR
422
+ audio_segment = audio_segment.set_frame_rate(16000).set_channels(1)
423
+
424
+ # Export to in-memory bytes
425
+ wav_buffer = io.BytesIO()
426
+ audio_segment.export(wav_buffer, format="wav")
427
+ wav_buffer.seek(0)
428
+
429
+ converted_audio = wav_buffer
430
+
431
+ # Show audio player (use converted for consistent playback, or original)
432
+ # st.audio(audio_data) # Play original
433
+
434
+ except ImportError:
435
+ st.error("❌ `pydub` not installed. Please install it to support audio conversion.")
436
+ converted_audio = audio_data # Fallback
437
+ except Exception as e:
438
+ # Likely ffmpeg missing or bad file
439
+ if "ffmpeg" in str(e).lower():
440
+ st.warning("⚠️ FFmpeg needed for non-WAV files. Attempting to process raw file...")
441
+ else:
442
+ st.warning(f"⚠️ Audio conversion warning: {e}")
443
+ converted_audio = audio_data # Fallback
444
+
445
+ st.audio(audio_data) # Playback original file for user
446
 
447
  if st.button("πŸš€ Process Order", type="primary", use_container_width=True):
448
  transcription_text = ""
449
 
450
+ # Use converted audio if available, else original
451
+ input_audio = converted_audio if converted_audio is not None else audio_data
452
+
453
  if use_cloud:
454
  # CLOUD MODE - Use HuggingFace Inference API (no download)
455
  with st.spinner("☁️ Transcribing via Cloud..."):
456
  try:
457
+ transcription_text = transcribe_cloud(input_audio, asr_model, hf_token)
458
  st.toast("βœ… Cloud Transcription Complete!")
459
  except Exception as e:
460
  st.error(f"❌ Cloud API failed: {e}")
 
471
  st.stop()
472
 
473
  with st.spinner("🎧 Transcribing Locally..."):
474
+ processed_audio = preprocessor.process(input_audio)
475
  result = asr(processed_audio)
476
  transcription_text = result["text"].replace("</s>", "").strip()
477
  st.toast("βœ… Local Transcription Complete!")
pyproject.toml CHANGED
@@ -22,6 +22,7 @@ dependencies = [
22
  "torch>=2.9.1",
23
  "transformers>=4.57.6",
24
  "webrtcvad>=2.0.10",
 
25
  ]
26
 
27
  [project.scripts]
 
22
  "torch>=2.9.1",
23
  "transformers>=4.57.6",
24
  "webrtcvad>=2.0.10",
25
+ "pydub>=0.25.1",
26
  ]
27
 
28
  [project.scripts]
requirements.txt CHANGED
@@ -12,3 +12,4 @@ torch>=2.9.1
12
  transformers>=4.57.6
13
  webrtcvad>=2.0.10
14
  huggingface_hub
 
 
12
  transformers>=4.57.6
13
  webrtcvad>=2.0.10
14
  huggingface_hub
15
+ pydub>=0.25.1
uv.lock CHANGED
@@ -1195,6 +1195,7 @@ dependencies = [
1195
  { name = "numpy" },
1196
  { name = "openpyxl" },
1197
  { name = "pandas" },
 
1198
  { name = "rapidfuzz" },
1199
  { name = "regex" },
1200
  { name = "soundfile" },
@@ -1212,6 +1213,7 @@ requires-dist = [
1212
  { name = "numpy", specifier = "<2" },
1213
  { name = "openpyxl", specifier = ">=3.1.5" },
1214
  { name = "pandas", specifier = ">=2.3.3" },
 
1215
  { name = "rapidfuzz", specifier = ">=3.14.3" },
1216
  { name = "regex", specifier = ">=2026.1.15" },
1217
  { name = "soundfile", specifier = ">=0.13.1" },
@@ -1393,6 +1395,15 @@ wheels = [
1393
  { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" },
1394
  ]
1395
 
 
 
 
 
 
 
 
 
 
1396
  [[package]]
1397
  name = "pyparsing"
1398
  version = "3.3.1"
 
1195
  { name = "numpy" },
1196
  { name = "openpyxl" },
1197
  { name = "pandas" },
1198
+ { name = "pydub" },
1199
  { name = "rapidfuzz" },
1200
  { name = "regex" },
1201
  { name = "soundfile" },
 
1213
  { name = "numpy", specifier = "<2" },
1214
  { name = "openpyxl", specifier = ">=3.1.5" },
1215
  { name = "pandas", specifier = ">=2.3.3" },
1216
+ { name = "pydub", specifier = ">=0.25.1" },
1217
  { name = "rapidfuzz", specifier = ">=3.14.3" },
1218
  { name = "regex", specifier = ">=2026.1.15" },
1219
  { name = "soundfile", specifier = ">=0.13.1" },
 
1395
  { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" },
1396
  ]
1397
 
1398
+ [[package]]
1399
+ name = "pydub"
1400
+ version = "0.25.1"
1401
+ source = { registry = "https://pypi.org/simple" }
1402
+ sdist = { url = "https://files.pythonhosted.org/packages/fe/9a/e6bca0eed82db26562c73b5076539a4a08d3cffd19c3cc5913a3e61145fd/pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f", size = 38326, upload-time = "2021-03-10T02:09:54.659Z" }
1403
+ wheels = [
1404
+ { url = "https://files.pythonhosted.org/packages/a6/53/d78dc063216e62fc55f6b2eebb447f6a4b0a59f55c8406376f76bf959b08/pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6", size = 32327, upload-time = "2021-03-10T02:09:53.503Z" },
1405
+ ]
1406
+
1407
  [[package]]
1408
  name = "pyparsing"
1409
  version = "3.3.1"