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.
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
LIKEsimply 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 catchUnicodeDecodeErrorand retry with a fallback encoding likelatin-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.