Video-to-Text Transcriber

Turn short videos into searchable text in minutes

Welcome to another official UVF IT build guide. Today, we are constructing a minimal clone of HiTranscript, a tool that converts video content into searchable transcripts.

We will use Python with the whisper library for speech recognition and ffmpeg for audio extraction. This stack is free, open-source, and runs entirely on your local machine without paid APIs.

Prerequisites & Gotchas

Local Transcription Pipeline
LOCAL ENVIRONMENT Extract Audio Audio Stream Generate Text Input Video .mp4 FFmpeg Audio Extractor Whisper Model Speech-to-Text Transcript .txt
1

Phase 1: Scaffold & Install

Create a project directory and a virtual environment to isolate dependencies.

Install openai-whisper (which includes PyTorch) and ffmpeg-python for easy media handling.

mkdir uvf-transcriber
cd uvf-transcriber
python3 -m venv venv
source venv/bin/activate
pip install openai-whisper ffmpeg-python
2

Phase 2: Core Logic (Audio Extraction)

Whisper requires audio input. We will write a helper function to extract audio from a video file using ffmpeg.

This step converts the video to a .wav file, which is the standard format for speech recognition.

transcriber.py

import ffmpeg
import os

def extract_audio(video_path, audio_path):
    """
    Extracts audio from a video file and saves it as a .wav file.
    """
    try:
        (ffmpeg
            .input(video_path)
            .output(audio_path, acodec='pcm_s16le', ac=1, ar='16k')
            .overwrite_output()
            .run(capture_stdout=True, capture_stderr=True))
        print(f"Audio extracted to: {audio_path}")
    except ffmpeg.Error as e:
        print(f"FFmpeg error: {e.stderr.decode()}")
        raise
3

Phase 3: Core Logic (Transcription)

Load the Whisper model. We use 'tiny' for speed, but you can change this to 'base' or 'small' for better accuracy.

The transcribe method returns a dictionary containing the full text and segmented timestamps.

transcriber.py

import whisper

def transcribe_audio(audio_path, model_name='tiny'):
    """
    Loads the Whisper model and transcribes the audio file.
    """
    print(f"Loading model '{model_name}'...")
    model = whisper.load_model(model_name)
    
    print("Transcribing...")
    result = model.transcribe(audio_path)
    
    return result['text']
4

Phase 4: Main Execution & CLI

We now combine our helper functions into a single, executable script. By using Python's argparse, we create a simple command-line interface that accepts a video file path as an argument.

This main block orchestrates the workflow: it extracts audio, runs the Whisper model, and saves the resulting text to a file named after the input video. This makes the tool reusable for any video file you throw at it.

transcriber.py

import argparse
import os
import whisper
from moviepy.editor import VideoFileClip

def extract_audio(video_path, audio_path):
    """Extract audio from video and save as .wav"""
    video = VideoFileClip(video_path)
    video.audio.write_audiofile(audio_path, codec='pcm_s16le')
    video.close()
    print(f"Audio extracted to: {audio_path}")

def transcribe_audio(audio_path):
    """Transcribe audio file using Whisper"""
    print("Loading Whisper model (tiny)...")
    model = whisper.load_model("tiny")
    print("Transcribing...")
    result = model.transcribe(audio_path)
    return result["text"]

def main():
    parser = argparse.ArgumentParser(description="Video to Text Transcriber")
    parser.add_argument("video_path", help="Path to the input video file")
    args = parser.parse_args()

    if not os.path.exists(args.video_path):
        print(f"Error: File '{args.video_path}' not found.")
        return

    base_name = os.path.splitext(os.path.basename(args.video_path))[0]
    audio_path = f"{base_name}_audio.wav"
    output_txt = f"{base_name}_transcript.txt"

    try:
        # Step 1: Extract Audio
        extract_audio(args.video_path, audio_path)

        # Step 2: Transcribe
        text = transcribe_audio(audio_path)

        # Step 3: Save Output
        with open(output_txt, "w", encoding="utf-8") as f:
            f.write(text)
        
        print(f"Success! Transcript saved to: {output_txt}")
        
        # Cleanup optional: remove audio file if desired
        # os.remove(audio_path)
        
    except Exception as e:
        print(f"An error occurred: {e}")

if __name__ == "__main__":
    main()
5

Phase 5: Run & Verify

To test your new tool, you need a sample video file. You can use any short .mp4 file from your computer or download a free stock video from sites like Pexels.

Run the script from your terminal, ensuring your virtual environment is active. The first run will download the Whisper model weights, which may take a moment, but subsequent runs will be much faster.

# Activate your virtual environment first (if not already active)
# On Mac/Linux:
source venv/bin/activate
# On Windows:
# venv\Scripts\activate

# Run the transcriber with your video file
python transcriber.py sample_video.mp4

# Check the output
ls -l sample_video_transcript.txt
cat sample_video_transcript.txt

Verifying the Stack

CheckCommand / Why it matters
Virtual environment activatedRun which python (Mac/Linux) or where python (Windows) to ensure it points to ./venv/bin/python.
FFmpeg installedRun ffmpeg -version in terminal. If command not found, install FFmpeg via Homebrew (brew install ffmpeg) or package manager.
Transcript generatedCheck for a .txt file in your directory containing readable text that matches the video audio.
No errors during model loadThe first run downloads the model. Subsequent runs should be faster. Ensure no PyTorch CUDA errors if running on CPU.

Next Steps

To extend this, try adding a simple Flask API to accept file uploads via a web form, or switch the model to 'base' for higher accuracy on noisy audio.