Storing Video Transcripts in SQLite Using Python and SRT Files

Storing Video Transcripts in SQLite Using Python and SRT Files

Video recordings hold a lot of knowledge, but that knowledge is locked inside audio. Once you turn those recordings into text, something changes. You can search them. You can query them. You can pull out every moment someone mentioned a specific term, or find everything said between the ten-minute and twenty-minute marks. This guide walks through exactly that: taking an SRT subtitle file, parsing it with Python, and loading it into a SQLite database where the content becomes fully searchable.

What This Covers

This article shows you how to write a Python script that reads SRT subtitle files, extracts dialogue and timestamps, and inserts that data into a SQLite database. From there, you get a working schema and a set of SQL queries for searching transcripts by keyword or filtering content by time range. No heavy dependencies required. Everything runs on Python’s standard library and the SQLite module that ships with it.

How SRT Files Carry Transcript Data

SRT stands for SubRip Text. It is one of the most common subtitle formats around, and the structure is refreshingly simple. Each entry has three parts: a sequence number, a time range written as HH:MM:SS,mmm --> HH:MM:SS,mmm, and then one or more lines of dialogue text. A blank line separates each entry from the next.

Here is a small example of what an SRT file looks like:

1
00:00:05,000 --> 00:00:08,400
Welcome to the quarterly review.

2
00:00:08,500 --> 00:00:12,300
Today we are covering Q3 results.

3
00:00:12,400 --> 00:00:17,000
Revenue came in above expectations this quarter.

If you are working with a video recording that does not already have subtitles, you can generate an SRT file through a video to SRT converter before moving on with the steps below. Once you have that file, the rest of the process is straightforward Python.

Designing the SQLite Database Schema

Before writing any parsing code, it helps to think through what the database needs to store. Each subtitle entry maps naturally to a single database row. You need the sequence number, the start time, the end time, and the text. Storing start and end times as floating-point seconds makes range queries much easier later.

The Transcript Table Structure

A single table handles everything for most use cases. Here is what a clean schema looks like:

CREATE TABLE IF NOT EXISTS transcript (
    id INTEGER PRIMARY KEY,
    sequence INTEGER NOT NULL,
    start_time REAL NOT NULL,
    end_time REAL NOT NULL,
    text TEXT NOT NULL
);

The start_time and end_time columns hold seconds as decimal numbers. A timestamp of 00:02:35,500 becomes 155.5. This format makes numeric comparisons in SQL clean and predictable.

Schema Field Reference

Column Type Purpose
id INTEGER Auto-incrementing primary key
sequence INTEGER The subtitle block number from the SRT file
start_time REAL Start of the subtitle in seconds
end_time REAL End of the subtitle in seconds
text TEXT The spoken dialogue for that subtitle block

Parsing the SRT File with Python

Python’s standard library is all you need here. No third-party packages required. The parsing logic reads the file in blocks separated by blank lines, then extracts the sequence number, the time range, and the text from each block.

Here is a complete parser function:

import re

def parse_srt(filepath):
    with open(filepath, "r", encoding="utf-8") as f:
        content = f.read()

    blocks = content.strip().split("\n\n")
    entries = []

    for block in blocks:
        lines = block.strip().splitlines()
        if len(lines) < 3:
            continue

        sequence = int(lines[0].strip())

        time_match = re.match(
            r"(\d{2}):(\d{2}):(\d{2}),(\d{3})\s-->\s(\d{2}):(\d{2}):(\d{2}),(\d{3})",
            lines[1].strip()
        )
        if not time_match:
            continue

        def to_seconds(h, m, s, ms):
            return int(h) * 3600 + int(m) * 60 + int(s) + int(ms) / 1000

        start = to_seconds(*time_match.groups()[:4])
        end = to_seconds(*time_match.groups()[4:])
        text = " ".join(lines[2:]).strip()

        entries.append((sequence, start, end, text))

    return entries

The function splits the file on double newlines, which is the standard SRT block separator. A regular expression handles the timestamp line because the format is consistent enough to match reliably. Multi-line dialogue blocks get joined with a space so each subtitle entry becomes a single text field in the database.

Loading Transcript Entries into SQLite

With parsed entries ready, writing them to the database takes just a few lines. Python’s built-in sqlite3 module handles the connection and all insertion work without any external dependencies.

import sqlite3

def load_to_db(entries, db_path="transcripts.db"):
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()

    cursor.execute("""
        CREATE TABLE IF NOT EXISTS transcript (
            id INTEGER PRIMARY KEY,
            sequence INTEGER NOT NULL,
            start_time REAL NOT NULL,
            end_time REAL NOT NULL,
            text TEXT NOT NULL
        )
    """)

    cursor.executemany("""
        INSERT INTO transcript (sequence, start_time, end_time, text)
        VALUES (?, ?, ?, ?)
    """, entries)

    conn.commit()
    conn.close()
    print(f"Loaded {len(entries)} entries into {db_path}")

Using executemany batches all the inserts into a single transaction. For large SRT files, this is noticeably faster than inserting row by row. The database file gets created automatically if it does not already exist.

Putting the Script Together as a Command-Line Tool

The two functions above combine into a simple command-line script. Pass the SRT filename as an argument and the script handles everything from there.

import sys

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python load_transcript.py file.srt")
        sys.exit(1)

    srt_file = sys.argv[1]
    entries = parse_srt(srt_file)
    load_to_db(entries)

Run it like this from your terminal:

python load_transcript.py meeting_recording.srt

That loads the entire transcript into transcripts.db and prints a confirmation with the row count. From here, you can open the database file in any SQLite client and start running queries immediately.

Searching Your Transcript Data with SQL

Now the interesting part begins. With the data in SQLite, you can run queries against it directly. The two most common patterns are keyword search and time range filtering. SQLite's LIKE operator covers basic keyword matching without any additional setup. For more serious text search, SQLite ships with a built-in full-text search extension that handles tokenization, ranking, and phrase queries far more efficiently than pattern matching alone.

Searching by Keyword

A basic keyword search using LIKE looks like this:

SELECT sequence, start_time, end_time, text
FROM transcript
WHERE text LIKE '%revenue%'
ORDER BY start_time;

That returns every subtitle block containing the word "revenue", along with the timestamps showing exactly when it appeared in the recording. You get a clear picture of how often a topic came up and at what points in the conversation.

Filtering by Time Range

Time range queries are just numeric comparisons against start_time and end_time. To pull everything said in the first five minutes:

SELECT sequence, start_time, end_time, text
FROM transcript
WHERE start_time >= 0 AND end_time <= 300
ORDER BY start_time;

You can combine both filters too. Find every mention of a keyword within a specific portion of the recording:

SELECT sequence, start_time, end_time, text
FROM transcript
WHERE text LIKE '%budget%'
  AND start_time >= 600
  AND end_time <= 1200
ORDER BY start_time;

That query finds every mention of "budget" in the ten-to-twenty-minute window of the recording. Narrow searches like this are where storing timestamps as seconds pays off. The math stays simple and the queries stay readable.

Things Worth Considering Before You Scale This Up

The script above works well for a single file. A few things become important as your collection of transcripts grows:

  • Add a source column. Store the original filename or a video identifier alongside each row so you know which recording each line came from. Without this, a keyword search across multiple transcripts returns results with no way to trace them back.
  • Add an index on start_time. Time range queries get faster with CREATE INDEX idx_start ON transcript(start_time);. This matters once you have tens of thousands of rows.
  • Use FTS5 for serious text search. SQLite's FTS5 virtual tables handle stemming, phrase matching, and result ranking in ways that LIKE simply cannot.
  • Handle encoding carefully. SRT files from different tools sometimes use different text encodings. Opening with encoding="utf-8" works for most files, but you may need to catch UnicodeDecodeError and retry with a fallback encoding like latin-1.
  • Deduplicate before inserting. Running the script twice on the same file produces duplicate rows. A check on sequence number and source filename before inserting prevents that problem.

From Raw Video to a Searchable Text Archive

There is something genuinely useful about turning a passive video file into a structured, queryable database. What was once a recording you had to scrub through manually becomes something you can interrogate in milliseconds. You can ask it questions. You can pull the exact timestamp where a topic came up. You can compare multiple transcripts by running joins across recordings. You can build a minimal web interface on top of the database and give your whole team access to the archive through a text input.

The toolchain here is about as lightweight as Python gets. No external packages to install. No services to run. No configuration files to maintain. The database file you end up with is a single portable file you can copy anywhere and open with any SQLite client, from the command line to a GUI tool to a web app.

For teams that regularly record meetings, lectures, or interviews, this approach turns a growing library of video files into something that actually respects your time. Instead of scrubbing through audio trying to recall when a decision was made, you run a query and get timestamps back in under a second. That shift, from passive archive to active search tool, changes how useful those recordings can be.

Leave a Reply