| import ffmpeg |
| import os |
| from faster_whisper import WhisperModel |
|
|
|
|
| def extract_audio(video_path: str) -> str: |
| """Extract mono 16kHz WAV audio from a video file.""" |
| audio_path = os.path.splitext(video_path)[0] + "_audio.wav" |
| ( |
| ffmpeg |
| .input(video_path) |
| .output(audio_path, acodec="pcm_s16le", ac=1, ar="16000") |
| .overwrite_output() |
| .run(quiet=True) |
| ) |
| return audio_path |
|
|
|
|
| def transcribe(video_path: str, model_size: str = "base") -> dict: |
| """ |
| Transcribe video audio using faster-whisper. |
| Returns a dict with text, segments (with word timestamps), and language. |
| """ |
| audio_path = extract_audio(video_path) |
|
|
| |
| model = WhisperModel(model_size, device="cpu", compute_type="int8") |
|
|
| segments_iter, info = model.transcribe( |
| audio_path, |
| word_timestamps=True, |
| vad_filter=True, |
| ) |
|
|
| |
| segments = [] |
| full_text = [] |
|
|
| for seg in segments_iter: |
| words = [] |
| if seg.words: |
| for w in seg.words: |
| words.append({ |
| "word": w.word, |
| "start": w.start, |
| "end": w.end, |
| "probability": w.probability, |
| }) |
| segments.append({ |
| "id": seg.id, |
| "start": seg.start, |
| "end": seg.end, |
| "text": seg.text.strip(), |
| "words": words, |
| }) |
| full_text.append(seg.text.strip()) |
|
|
| |
| try: |
| os.remove(audio_path) |
| except Exception: |
| pass |
|
|
| return { |
| "text": " ".join(full_text), |
| "segments": segments, |
| "language": info.language, |
| } |
|
|