NickVerri commited on
Commit
56d3b85
Β·
verified Β·
1 Parent(s): 97aba95

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +28 -25
app.py CHANGED
@@ -1,9 +1,25 @@
1
  import os
2
  import numpy as np
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
- # --- NUMPY 2.0 PATCH ---
5
- # This must run before any other library imports to prevent crashes
6
- # with pyannote/whisperx which might expect the old 'np.NaN' attribute.
7
  if not hasattr(np, 'NaN'):
8
  np.NaN = np.nan
9
 
@@ -11,14 +27,12 @@ import streamlit as st
11
  import subprocess
12
  import json
13
  import requests
14
- import torch
15
  import whisperx
16
  import gc
17
  import pandas as pd
18
  from datetime import timedelta
19
 
20
  # --- Configuration & Tokens ---
21
- # Priority: Secret > Hardcoded
22
  HARDCODED_HF_TOKEN = "PASTE_YOUR_HF_TOKEN_HERE"
23
  HARDCODED_GEMINI_KEY = ""
24
 
@@ -100,8 +114,7 @@ with st.sidebar:
100
  fps = st.number_input("Timeline FPS", value=25)
101
 
102
  st.header("Model Settings")
103
- # T4 has 16GB VRAM, large-v2 works well
104
- model_size = st.selectbox("Whisper Model", ["large-v2", "medium"], index=0)
105
  num_speakers = st.number_input("Speakers (0=Auto)", min_value=0, value=0)
106
 
107
  st.divider()
@@ -114,7 +127,6 @@ with st.sidebar:
114
  uploaded_file = st.file_uploader("Upload Video/Audio Clip", type=["mp4", "m4a", "wav", "mp3", "mov"])
115
 
116
  if uploaded_file:
117
- # --- Step 1: Technical Processing ---
118
  if "transcript" not in st.session_state:
119
  if st.button("Step 1: Transcribe & Diarize"):
120
  if not ACTIVE_HF_TOKEN or "PASTE_YOUR_HF_TOKEN" in ACTIVE_HF_TOKEN:
@@ -124,12 +136,10 @@ if uploaded_file:
124
  with status_container.container():
125
  st.write("πŸ”„ **Processing Started...**")
126
 
127
- # Save local temp file
128
  with open("temp_input", "wb") as f:
129
  f.write(uploaded_file.getbuffer())
130
 
131
- st.write("🎡 **Extracting Audio (WAV)...**")
132
- # WhisperX prefers 16k mono wav
133
  subprocess.run([
134
  "ffmpeg", "-i", "temp_input",
135
  "-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1",
@@ -138,38 +148,33 @@ if uploaded_file:
138
 
139
  try:
140
  device = "cuda" if torch.cuda.is_available() else "cpu"
141
- if device == "cpu":
142
- st.warning("⚠️ No GPU detected. WhisperX will be very slow.")
143
-
144
- st.write(f"πŸš€ **Loading WhisperX on {device}...**")
145
 
146
  # 1. Transcribe
147
- # Use float16 for GPU, int8 for CPU
148
  compute_type = "float16" if device == "cuda" else "int8"
149
-
150
  model = whisperx.load_model(model_size, device, compute_type=compute_type)
151
 
152
  st.write("πŸ“ **Transcribing...**")
153
  audio = whisperx.load_audio("temp_audio.wav")
154
  result = model.transcribe(audio, batch_size=16)
155
 
156
- # Cleanup VRAM
 
157
  gc.collect()
158
  torch.cuda.empty_cache()
159
- del model
160
 
161
- # 2. Align (Improves timestamp accuracy for diarization)
162
  st.write("⏱️ **Aligning Audio...**")
163
  model_a, metadata = whisperx.load_align_model(language_code=result["language"], device=device)
164
  result = whisperx.align(result["segments"], model_a, metadata, audio, device, return_char_alignments=False)
165
 
166
- # Cleanup VRAM
167
  gc.collect()
168
  torch.cuda.empty_cache()
169
- del model_a
170
 
171
  # 3. Diarize
172
  st.write("πŸ—£οΈ **Diarizing Speakers...**")
 
173
  diarize_model = whisperx.DiarizationPipeline(use_auth_token=ACTIVE_HF_TOKEN, device=device)
174
 
175
  diarize_kwargs = {}
@@ -178,13 +183,11 @@ if uploaded_file:
178
 
179
  diarize_segments = diarize_model(audio, **diarize_kwargs)
180
 
181
- # 4. Assign Speakers to Words
182
  st.write("πŸ”— **Merging Transcripts...**")
183
  final_result = whisperx.assign_word_speakers(diarize_segments, result)
184
 
185
- # Format for Gemini
186
  processed_segments = []
187
- # WhisperX structure is slightly different, it returns 'segments' list
188
  for segment in final_result["segments"]:
189
  processed_segments.append({
190
  "speaker": segment.get("speaker", "Unknown"),
 
1
  import os
2
  import numpy as np
3
+ import torch
4
+
5
+ # --- PYTORCH 2.6+ SECURITY & COMPATIBILITY PATCHES ---
6
+ # 1. Allow WhisperX/Pyannote globals for model loading
7
+ try:
8
+ from omegaconf.listconfig import ListConfig
9
+ from omegaconf.dictconfig import DictConfig
10
+ # Expanded safe globals to include classes often used in diarization checkpoints
11
+ torch.serialization.add_safe_globals([
12
+ ListConfig,
13
+ DictConfig,
14
+ torch.nn.modules.container.ModuleList,
15
+ np.dtype,
16
+ np._core.multiarray.scalar if hasattr(np, '_core') else np.core.multiarray.scalar
17
+ ])
18
+ except Exception as e:
19
+ # Use a generic print or pass to avoid startup crashes if classes are missing
20
+ print(f"Safe Globals Warning: {e}")
21
 
22
+ # 2. Fix NumPy 2.0+ attribute removal (required for older pyannote internals)
 
 
23
  if not hasattr(np, 'NaN'):
24
  np.NaN = np.nan
25
 
 
27
  import subprocess
28
  import json
29
  import requests
 
30
  import whisperx
31
  import gc
32
  import pandas as pd
33
  from datetime import timedelta
34
 
35
  # --- Configuration & Tokens ---
 
36
  HARDCODED_HF_TOKEN = "PASTE_YOUR_HF_TOKEN_HERE"
37
  HARDCODED_GEMINI_KEY = ""
38
 
 
114
  fps = st.number_input("Timeline FPS", value=25)
115
 
116
  st.header("Model Settings")
117
+ model_size = st.selectbox("Whisper Model", ["large-v2", "medium", "base"], index=0)
 
118
  num_speakers = st.number_input("Speakers (0=Auto)", min_value=0, value=0)
119
 
120
  st.divider()
 
127
  uploaded_file = st.file_uploader("Upload Video/Audio Clip", type=["mp4", "m4a", "wav", "mp3", "mov"])
128
 
129
  if uploaded_file:
 
130
  if "transcript" not in st.session_state:
131
  if st.button("Step 1: Transcribe & Diarize"):
132
  if not ACTIVE_HF_TOKEN or "PASTE_YOUR_HF_TOKEN" in ACTIVE_HF_TOKEN:
 
136
  with status_container.container():
137
  st.write("πŸ”„ **Processing Started...**")
138
 
 
139
  with open("temp_input", "wb") as f:
140
  f.write(uploaded_file.getbuffer())
141
 
142
+ st.write("🎡 **Extracting Audio...**")
 
143
  subprocess.run([
144
  "ffmpeg", "-i", "temp_input",
145
  "-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1",
 
148
 
149
  try:
150
  device = "cuda" if torch.cuda.is_available() else "cpu"
151
+ st.write(f"πŸš€ **Running WhisperX on {device}...**")
 
 
 
152
 
153
  # 1. Transcribe
 
154
  compute_type = "float16" if device == "cuda" else "int8"
 
155
  model = whisperx.load_model(model_size, device, compute_type=compute_type)
156
 
157
  st.write("πŸ“ **Transcribing...**")
158
  audio = whisperx.load_audio("temp_audio.wav")
159
  result = model.transcribe(audio, batch_size=16)
160
 
161
+ # Memory management
162
+ del model
163
  gc.collect()
164
  torch.cuda.empty_cache()
 
165
 
166
+ # 2. Align
167
  st.write("⏱️ **Aligning Audio...**")
168
  model_a, metadata = whisperx.load_align_model(language_code=result["language"], device=device)
169
  result = whisperx.align(result["segments"], model_a, metadata, audio, device, return_char_alignments=False)
170
 
171
+ del model_a
172
  gc.collect()
173
  torch.cuda.empty_cache()
 
174
 
175
  # 3. Diarize
176
  st.write("πŸ—£οΈ **Diarizing Speakers...**")
177
+ # Pass token for gated diarization models
178
  diarize_model = whisperx.DiarizationPipeline(use_auth_token=ACTIVE_HF_TOKEN, device=device)
179
 
180
  diarize_kwargs = {}
 
183
 
184
  diarize_segments = diarize_model(audio, **diarize_kwargs)
185
 
186
+ # 4. Final Merge
187
  st.write("πŸ”— **Merging Transcripts...**")
188
  final_result = whisperx.assign_word_speakers(diarize_segments, result)
189
 
 
190
  processed_segments = []
 
191
  for segment in final_result["segments"]:
192
  processed_segments.append({
193
  "speaker": segment.get("speaker", "Unknown"),