NickVerri commited on
Commit
ef803ac
ยท
verified ยท
1 Parent(s): faf5bd6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -56
app.py CHANGED
@@ -5,25 +5,28 @@ import json
5
  import os
6
  import requests
7
  import torch
8
- import numpy
 
9
  from datetime import timedelta
10
  from pyannote.audio import Pipeline
11
- from huggingface_hub import login
12
-
13
- # --- Fix for OMP Error ---
14
- os.environ["OMP_NUM_THREADS"] = "1"
15
 
16
  # --- Safe Globals for PyTorch 2.6+ ---
17
  try:
18
  from pyannote.audio.core.task import Specifications, Problem, Resolution
 
 
19
 
20
  torch.serialization.add_safe_globals([
21
  torch.torch_version.TorchVersion,
22
  Specifications,
23
  Problem,
24
  Resolution,
25
- numpy.dtype,
 
 
26
  torch.nn.modules.container.ModuleList,
 
27
  ])
28
  except Exception as e:
29
  print(f"Safe Globals Warning: {e}")
@@ -125,96 +128,104 @@ if uploaded_file:
125
  if not ACTIVE_HF_TOKEN or "PASTE_YOUR_HF_TOKEN" in ACTIVE_HF_TOKEN:
126
  st.error("Please provide a valid Hugging Face Token.")
127
  else:
128
- status_container = st.empty()
129
- with status_container.container():
130
- st.write("๐Ÿ”„ **Processing Started...**")
131
-
132
- # Authenticate FIRST - this is critical
133
  login(token=ACTIVE_HF_TOKEN)
134
 
135
  with open("temp_input", "wb") as f:
136
  f.write(uploaded_file.getbuffer())
137
 
138
- st.write("๐ŸŽต **Extracting Audio (WAV)...**")
139
  subprocess.run([
140
  "ffmpeg", "-i", "temp_input",
141
  "-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1",
142
  "temp_audio.wav", "-y"
143
- ], capture_output=True)
144
 
145
- # 1. Diarization - CRITICAL FIX
146
  st.write("๐Ÿ—ฃ๏ธ **Running Speaker Diarization...**")
147
  diarization = None
148
- speaker_turns = []
149
-
150
  try:
151
- # Load pipeline WITHOUT any token parameter (relies on login())
152
- pipeline = Pipeline.from_pretrained("pyannote/speaker-diarization-3.1")
 
 
 
 
 
153
 
 
 
 
 
 
154
  if torch.cuda.is_available():
155
  st.write("๐Ÿš€ Using GPU for Diarization")
156
  pipeline.to(torch.device("cuda"))
157
-
158
- # Run diarization
159
- st.write("โณ Processing audio (this takes a moment)...")
160
- diarization = pipeline("temp_audio.wav")
161
-
162
- # Debug: Check what we got back
163
- st.write(f"DEBUG: Diarization type = {type(diarization)}")
164
- st.write(f"DEBUG: Has itertracks? {hasattr(diarization, 'itertracks')}")
165
-
166
- # Extract speaker turns
167
- if hasattr(diarization, 'itertracks'):
168
- for turn, _, speaker_id in diarization.itertracks(yield_label=True):
169
- speaker_turns.append({
170
- "start": turn.start,
171
- "end": turn.end,
172
- "speaker": speaker_id
173
- })
174
- st.write(f"โœ… Found {len(speaker_turns)} speaker segments")
175
 
176
- # Show first few turns for debugging
177
- if speaker_turns:
178
- st.write("Sample speaker turns:")
179
- for turn in speaker_turns[:3]:
180
- st.write(f" {turn['speaker']}: {turn['start']:.1f}s - {turn['end']:.1f}s")
 
181
  else:
182
- st.warning("โš ๏ธ Diarization object has no itertracks method")
183
 
 
 
 
 
 
 
 
184
  except Exception as e:
185
- st.error(f"Diarization Error: {str(e)}")
186
- import traceback
187
- st.code(traceback.format_exc())
188
 
189
  # 2. Whisper Transcription
190
  st.write("๐Ÿ“ **Transcribing with Whisper...**")
191
  device = "cuda" if torch.cuda.is_available() else "cpu"
192
- model = whisper.load_model("base", device=device)
193
  result = model.transcribe("temp_audio.wav", word_timestamps=True)
194
 
195
  # 3. Alignment
196
  st.write("๐Ÿ”— **Aligning Speakers...**")
197
  final_segments = []
 
198
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  for segment in result['segments']:
200
  mid_time = (segment['start'] + segment['end']) / 2
201
  speaker = "Unknown"
202
 
203
  if speaker_turns:
204
- # Match speaker by midpoint
205
  for turn in speaker_turns:
206
  if turn["start"] <= mid_time <= turn["end"]:
207
  speaker = turn["speaker"]
208
  break
209
 
210
- # Fallback: find closest speaker within 1 second
211
  if speaker == "Unknown":
212
- best_dist = 1.0
213
  for turn in speaker_turns:
214
- dist = min(
215
- abs(turn["start"] - mid_time),
216
- abs(turn["end"] - mid_time)
217
- )
218
  if dist < best_dist:
219
  best_dist = dist
220
  speaker = turn["speaker"]
@@ -228,16 +239,15 @@ if uploaded_file:
228
  })
229
 
230
  st.session_state.transcript = final_segments
231
- st.success(f"โœ“ Complete! Transcribed {len(final_segments)} segments")
232
 
233
  if "transcript" in st.session_state:
234
  st.divider()
235
-
236
  with st.expander("Transcript Preview (Diarized)"):
237
  for seg in st.session_state.transcript[:20]:
238
  st.markdown(f"**{seg['speaker']}:** {seg['text']}")
239
 
240
- brief = st.text_area("Creative Brief", placeholder="e.g. Focus on the yeast story, remove the interviewer.")
241
 
242
  if st.button("Step 2: Create EDL"):
243
  if not ACTIVE_GEMINI_KEY:
 
5
  import os
6
  import requests
7
  import torch
8
+ import numpy as np
9
+ import librosa
10
  from datetime import timedelta
11
  from pyannote.audio import Pipeline
12
+ from huggingface_hub import login, hf_hub_download
 
 
 
13
 
14
  # --- Safe Globals for PyTorch 2.6+ ---
15
  try:
16
  from pyannote.audio.core.task import Specifications, Problem, Resolution
17
+ from pyannote.audio.core.model import Model
18
+ from pyannote.audio.pipelines.speaker_diarization import SpeakerDiarization
19
 
20
  torch.serialization.add_safe_globals([
21
  torch.torch_version.TorchVersion,
22
  Specifications,
23
  Problem,
24
  Resolution,
25
+ Model,
26
+ SpeakerDiarization,
27
+ np.dtype,
28
  torch.nn.modules.container.ModuleList,
29
+ np.core.multiarray.scalar
30
  ])
31
  except Exception as e:
32
  print(f"Safe Globals Warning: {e}")
 
128
  if not ACTIVE_HF_TOKEN or "PASTE_YOUR_HF_TOKEN" in ACTIVE_HF_TOKEN:
129
  st.error("Please provide a valid Hugging Face Token.")
130
  else:
131
+ with st.spinner("Processing... This may take a moment."):
132
+ # Authenticate Globally
 
 
 
133
  login(token=ACTIVE_HF_TOKEN)
134
 
135
  with open("temp_input", "wb") as f:
136
  f.write(uploaded_file.getbuffer())
137
 
138
+ # Convert to strict WAV using FFmpeg
139
  subprocess.run([
140
  "ffmpeg", "-i", "temp_input",
141
  "-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1",
142
  "temp_audio.wav", "-y"
143
+ ])
144
 
145
+ # 1. Diarization with Librosa (Robust Audio Loading)
146
  st.write("๐Ÿ—ฃ๏ธ **Running Speaker Diarization...**")
147
  diarization = None
 
 
148
  try:
149
+ # Load Config Manually
150
+ config_path = hf_hub_download(
151
+ repo_id="pyannote/speaker-diarization-3.1",
152
+ filename="config.yaml",
153
+ token=ACTIVE_HF_TOKEN
154
+ )
155
+ pipeline = Pipeline.from_pretrained(config_path)
156
 
157
+ # Load Audio with Librosa (More robust than torchaudio in some containers)
158
+ y, sr = librosa.load("temp_audio.wav", sr=16000)
159
+ waveform = torch.tensor(y).unsqueeze(0) # Add channel dim
160
+
161
+ # Move to GPU if available
162
  if torch.cuda.is_available():
163
  st.write("๐Ÿš€ Using GPU for Diarization")
164
  pipeline.to(torch.device("cuda"))
165
+ waveform = waveform.to(torch.device("cuda"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
+ # Run Pipeline on Tensor directly
168
+ diarization_output = pipeline({"waveform": waveform, "sample_rate": 16000})
169
+
170
+ # Handle Output Wrapper
171
+ if isinstance(diarization_output, tuple):
172
+ diarization = diarization_output[0]
173
  else:
174
+ diarization = diarization_output
175
 
176
+ # Extract Annotation
177
+ if not hasattr(diarization, "itertracks"):
178
+ if hasattr(diarization_output, "annotation"):
179
+ diarization = diarization_output.annotation
180
+ elif hasattr(diarization_output, "get"):
181
+ diarization = diarization_output.get("annotation", diarization_output)
182
+
183
  except Exception as e:
184
+ st.error(f"Diarization Error: {e}")
185
+ diarization = None
 
186
 
187
  # 2. Whisper Transcription
188
  st.write("๐Ÿ“ **Transcribing with Whisper...**")
189
  device = "cuda" if torch.cuda.is_available() else "cpu"
190
+ model = whisper.load_model("medium", device=device)
191
  result = model.transcribe("temp_audio.wav", word_timestamps=True)
192
 
193
  # 3. Alignment
194
  st.write("๐Ÿ”— **Aligning Speakers...**")
195
  final_segments = []
196
+ speaker_turns = []
197
 
198
+ if diarization:
199
+ try:
200
+ iterator = None
201
+ if hasattr(diarization, 'itertracks'):
202
+ iterator = diarization.itertracks(yield_label=True)
203
+
204
+ if iterator:
205
+ for turn, _, speaker_id in iterator:
206
+ speaker_turns.append({"start": turn.start, "end": turn.end, "speaker": speaker_id})
207
+ st.write(f"โœ… Found {len(speaker_turns)} speaker turns.")
208
+ else:
209
+ st.warning("โš ๏ธ Pipeline ran but returned no iterable tracks.")
210
+ except Exception as e:
211
+ st.error(f"Error iterating tracks: {e}")
212
+
213
  for segment in result['segments']:
214
  mid_time = (segment['start'] + segment['end']) / 2
215
  speaker = "Unknown"
216
 
217
  if speaker_turns:
218
+ # Match speaker
219
  for turn in speaker_turns:
220
  if turn["start"] <= mid_time <= turn["end"]:
221
  speaker = turn["speaker"]
222
  break
223
 
224
+ # Fallback distance matching
225
  if speaker == "Unknown":
226
+ best_dist = 1.0
227
  for turn in speaker_turns:
228
+ dist = min(abs(turn["start"] - mid_time), abs(turn["end"] - mid_time))
 
 
 
229
  if dist < best_dist:
230
  best_dist = dist
231
  speaker = turn["speaker"]
 
239
  })
240
 
241
  st.session_state.transcript = final_segments
242
+ st.success("Complete!")
243
 
244
  if "transcript" in st.session_state:
245
  st.divider()
 
246
  with st.expander("Transcript Preview (Diarized)"):
247
  for seg in st.session_state.transcript[:20]:
248
  st.markdown(f"**{seg['speaker']}:** {seg['text']}")
249
 
250
+ brief = st.text_area("Creative Brief", placeholder="e.g. Focus on the yeast story.")
251
 
252
  if st.button("Step 2: Create EDL"):
253
  if not ACTIVE_GEMINI_KEY: