Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -79,7 +79,6 @@ except Exception as e:
|
|
| 79 |
if not hasattr(np, 'NaN'):
|
| 80 |
np.NaN = np.nan
|
| 81 |
|
| 82 |
-
# Import whisperx AFTER patching
|
| 83 |
import whisperx
|
| 84 |
|
| 85 |
# --- Configuration & Tokens ---
|
|
@@ -210,7 +209,6 @@ uploaded_file = st.file_uploader("Upload Video/Audio Clip", type=["mp4", "m4a",
|
|
| 210 |
|
| 211 |
if uploaded_file:
|
| 212 |
# --- Auto-Reset Logic for New Files ---
|
| 213 |
-
# If the user uploads a new file, we must clear the old transcript from memory
|
| 214 |
if "last_processed_file" not in st.session_state or st.session_state.last_processed_file != uploaded_file.name:
|
| 215 |
if "transcript" in st.session_state:
|
| 216 |
del st.session_state.transcript
|
|
@@ -221,55 +219,62 @@ if uploaded_file:
|
|
| 221 |
if not ACTIVE_HF_TOKEN or "PASTE_YOUR_HF_TOKEN" in ACTIVE_HF_TOKEN:
|
| 222 |
st.error("Please provide a valid Hugging Face Token in the Sidebar/Secrets.")
|
| 223 |
else:
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
progress_bar = st.progress(0)
|
| 228 |
|
| 229 |
try:
|
| 230 |
-
# Save File
|
|
|
|
| 231 |
with open("temp_input", "wb") as f:
|
| 232 |
f.write(uploaded_file.getbuffer())
|
| 233 |
|
| 234 |
-
st.write("🎵 **Phase 1/4: Extracting Audio...**")
|
| 235 |
-
progress_bar.progress(10)
|
| 236 |
subprocess.run([
|
| 237 |
"ffmpeg", "-i", "temp_input",
|
| 238 |
"-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1",
|
| 239 |
"temp_audio.wav", "-y"
|
| 240 |
])
|
|
|
|
| 241 |
|
|
|
|
| 242 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 243 |
if device == "cpu":
|
| 244 |
st.warning("⚠️ No GPU detected. This will be slow.")
|
| 245 |
|
| 246 |
-
#
|
| 247 |
-
|
| 248 |
-
progress_bar.progress(30)
|
| 249 |
|
| 250 |
compute_type = "float16" if device == "cuda" else "int8"
|
| 251 |
model = whisperx.load_model(model_size, device, compute_type=compute_type)
|
| 252 |
audio = whisperx.load_audio("temp_audio.wav")
|
|
|
|
|
|
|
|
|
|
| 253 |
result = model.transcribe(audio, batch_size=16, language=target_language)
|
| 254 |
|
| 255 |
-
# Cleanup
|
| 256 |
del model
|
| 257 |
gc.collect()
|
| 258 |
torch.cuda.empty_cache()
|
|
|
|
| 259 |
|
| 260 |
-
#
|
| 261 |
-
|
| 262 |
-
progress_bar.progress(60)
|
| 263 |
model_a, metadata = whisperx.load_align_model(language_code=result["language"], device=device)
|
| 264 |
result = whisperx.align(result["segments"], model_a, metadata, audio, device, return_char_alignments=False)
|
| 265 |
|
| 266 |
del model_a
|
| 267 |
gc.collect()
|
| 268 |
torch.cuda.empty_cache()
|
|
|
|
| 269 |
|
| 270 |
-
#
|
| 271 |
-
|
| 272 |
-
progress_bar.progress(80)
|
| 273 |
diarize_model = whisperx.DiarizationPipeline(use_auth_token=ACTIVE_HF_TOKEN, device=device)
|
| 274 |
|
| 275 |
diarize_kwargs = {}
|
|
@@ -278,8 +283,8 @@ if uploaded_file:
|
|
| 278 |
|
| 279 |
diarize_segments = diarize_model(audio, **diarize_kwargs)
|
| 280 |
|
| 281 |
-
#
|
| 282 |
-
|
| 283 |
final_result = whisperx.assign_word_speakers(diarize_segments, result)
|
| 284 |
|
| 285 |
processed_segments = []
|
|
@@ -292,16 +297,20 @@ if uploaded_file:
|
|
| 292 |
})
|
| 293 |
|
| 294 |
st.session_state.transcript = processed_segments
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
progress_bar.progress(100)
|
| 296 |
-
|
| 297 |
|
| 298 |
except Exception as e:
|
| 299 |
-
|
| 300 |
if os.path.exists("temp_input"): os.remove("temp_input")
|
| 301 |
if os.path.exists("temp_audio.wav"): os.remove("temp_audio.wav")
|
| 302 |
st.stop()
|
| 303 |
|
| 304 |
-
# --- Display Results if Transcript Exists ---
|
| 305 |
if "transcript" in st.session_state:
|
| 306 |
st.divider()
|
| 307 |
with st.expander("Transcript Preview", expanded=True):
|
|
|
|
| 79 |
if not hasattr(np, 'NaN'):
|
| 80 |
np.NaN = np.nan
|
| 81 |
|
|
|
|
| 82 |
import whisperx
|
| 83 |
|
| 84 |
# --- Configuration & Tokens ---
|
|
|
|
| 209 |
|
| 210 |
if uploaded_file:
|
| 211 |
# --- Auto-Reset Logic for New Files ---
|
|
|
|
| 212 |
if "last_processed_file" not in st.session_state or st.session_state.last_processed_file != uploaded_file.name:
|
| 213 |
if "transcript" in st.session_state:
|
| 214 |
del st.session_state.transcript
|
|
|
|
| 219 |
if not ACTIVE_HF_TOKEN or "PASTE_YOUR_HF_TOKEN" in ACTIVE_HF_TOKEN:
|
| 220 |
st.error("Please provide a valid Hugging Face Token in the Sidebar/Secrets.")
|
| 221 |
else:
|
| 222 |
+
# Create a dedicated container for the progress UI so it stays at the top
|
| 223 |
+
progress_container = st.container()
|
| 224 |
+
with progress_container:
|
| 225 |
+
st.info("🤖 **Junior Editor is processing your file...**")
|
| 226 |
+
|
| 227 |
+
# Create the visual elements
|
| 228 |
+
status_text = st.empty()
|
| 229 |
progress_bar = st.progress(0)
|
| 230 |
|
| 231 |
try:
|
| 232 |
+
# Phase 1: Save File & Extract Audio
|
| 233 |
+
status_text.markdown("**Phase 1/4: Extracting Audio...**")
|
| 234 |
with open("temp_input", "wb") as f:
|
| 235 |
f.write(uploaded_file.getbuffer())
|
| 236 |
|
|
|
|
|
|
|
| 237 |
subprocess.run([
|
| 238 |
"ffmpeg", "-i", "temp_input",
|
| 239 |
"-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1",
|
| 240 |
"temp_audio.wav", "-y"
|
| 241 |
])
|
| 242 |
+
progress_bar.progress(25)
|
| 243 |
|
| 244 |
+
# Setup Device
|
| 245 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 246 |
if device == "cpu":
|
| 247 |
st.warning("⚠️ No GPU detected. This will be slow.")
|
| 248 |
|
| 249 |
+
# Phase 2: Transcribe
|
| 250 |
+
status_text.markdown(f"**Phase 2/4: Transcribing (Whisper {model_size})... This is the longest step.**")
|
|
|
|
| 251 |
|
| 252 |
compute_type = "float16" if device == "cuda" else "int8"
|
| 253 |
model = whisperx.load_model(model_size, device, compute_type=compute_type)
|
| 254 |
audio = whisperx.load_audio("temp_audio.wav")
|
| 255 |
+
|
| 256 |
+
# This step takes the most time. It will sit at 25% until complete.
|
| 257 |
+
# This is safer than chunking manually which can cause transcription errors.
|
| 258 |
result = model.transcribe(audio, batch_size=16, language=target_language)
|
| 259 |
|
| 260 |
+
# Cleanup VRAM
|
| 261 |
del model
|
| 262 |
gc.collect()
|
| 263 |
torch.cuda.empty_cache()
|
| 264 |
+
progress_bar.progress(50)
|
| 265 |
|
| 266 |
+
# Phase 3: Align
|
| 267 |
+
status_text.markdown("**Phase 3/4: Aligning Text...**")
|
|
|
|
| 268 |
model_a, metadata = whisperx.load_align_model(language_code=result["language"], device=device)
|
| 269 |
result = whisperx.align(result["segments"], model_a, metadata, audio, device, return_char_alignments=False)
|
| 270 |
|
| 271 |
del model_a
|
| 272 |
gc.collect()
|
| 273 |
torch.cuda.empty_cache()
|
| 274 |
+
progress_bar.progress(75)
|
| 275 |
|
| 276 |
+
# Phase 4: Diarize
|
| 277 |
+
status_text.markdown("**Phase 4/4: Identifying Speakers...**")
|
|
|
|
| 278 |
diarize_model = whisperx.DiarizationPipeline(use_auth_token=ACTIVE_HF_TOKEN, device=device)
|
| 279 |
|
| 280 |
diarize_kwargs = {}
|
|
|
|
| 283 |
|
| 284 |
diarize_segments = diarize_model(audio, **diarize_kwargs)
|
| 285 |
|
| 286 |
+
# Final Merge
|
| 287 |
+
status_text.markdown("**Finalizing...**")
|
| 288 |
final_result = whisperx.assign_word_speakers(diarize_segments, result)
|
| 289 |
|
| 290 |
processed_segments = []
|
|
|
|
| 297 |
})
|
| 298 |
|
| 299 |
st.session_state.transcript = processed_segments
|
| 300 |
+
|
| 301 |
+
# --- CLEANUP ON SUCCESS ---
|
| 302 |
+
if os.path.exists("temp_input"): os.remove("temp_input")
|
| 303 |
+
if os.path.exists("temp_audio.wav"): os.remove("temp_audio.wav")
|
| 304 |
+
|
| 305 |
progress_bar.progress(100)
|
| 306 |
+
status_text.success(f"Done! Processed {len(processed_segments)} segments.")
|
| 307 |
|
| 308 |
except Exception as e:
|
| 309 |
+
status_text.error(f"Error during processing: {e}")
|
| 310 |
if os.path.exists("temp_input"): os.remove("temp_input")
|
| 311 |
if os.path.exists("temp_audio.wav"): os.remove("temp_audio.wav")
|
| 312 |
st.stop()
|
| 313 |
|
|
|
|
| 314 |
if "transcript" in st.session_state:
|
| 315 |
st.divider()
|
| 316 |
with st.expander("Transcript Preview", expanded=True):
|