How transcribe.py Parses VTT Caption Files in the bradautomates/claude-video Repository
transcribe.py converts WebVTT subtitle files into clean, timestamped transcripts using only Python standard library tools, parsing timestamp cues with regular expressions, stripping HTML tags, and deduplicating rolling YouTube subtitles to produce concise, readable output.
The bradautomates/claude-video repository provides a lightweight video processing toolkit that includes a standalone VTT parser. The transcribe.py script located at skills/watch/scripts/transcribe.py handles WebVTT caption parsing without external dependencies, making it portable across Agent-Skill hosts like Claude, Codex, and Cursor.
Reading and Splitting the VTT File
The parsing process begins with simple file I/O operations. The script reads the entire .vtt file as UTF-8 encoded text and immediately splits it into individual lines for sequential processing.
In skills/watch/scripts/transcribe.py at line 25, the implementation uses Path.read_text to ingest the file:
from pathlib import Path
# Read the entire VTT file
lines = Path("captions.vtt").read_text(encoding="utf-8").splitlines()
This approach eliminates the need for streaming parsers or complex file handlers, keeping the implementation lightweight and memory-efficient for typical subtitle files.
Extracting Cue Segments with Regex
The core parsing logic scans lines sequentially to identify cue segments—the individual subtitle entries containing timestamps and text.
Timestamp Detection with TS_RE
The script identifies timestamp lines using the TS_RE regular expression defined at lines 14-16. This pattern matches the WebVTT timestamp format HH:MM:SS.mmm --> HH:MM:SS.mmm:
TS_RE = re.compile(r"(\d{2}:\d{2}:\d{2}\.\d{3}) --> (\d{2}:\d{2}:\d{2}\.\d{3})")
When TS_RE.match(line) returns a match object, the parser extracts the start and end times as strings.
Converting Time to Seconds
The _to_seconds helper function (lines 20-22) converts timestamp strings into floating-point seconds for easier manipulation:
def _to_seconds(ts: str) -> float:
h, m, s = ts.split(":")
return int(h) * 3600 + int(m) * 60 + float(s)
This conversion allows the parser to store precise timing data while enabling arithmetic operations for duration calculations.
HTML Tag Stripping with TAG_RE
WebVTT files often contain HTML-style tags (such as <c> or <b>) for styling. The script removes these using the TAG_RE pattern while preserving the actual text content. Lines 40-46 demonstrate the cue text extraction loop:
if TS_RE.match(line):
# ... timestamp parsing logic ...
cue_lines = []
i += 1
while i < len(lines) and lines[i].strip():
cue_lines.append(TAG_RE.sub("", lines[i]).strip())
i += 1
cue_text = " ".join(cue_lines)
Each segment is stored as a dictionary with rounded timestamps:
{"start": round(start, 2), "end": round(end, 2), "text": cue_text}
Handling YouTube's Rolling Duplicates
YouTube's auto-generated subtitles frequently contain rolling duplicates—the same text appears in 2-3 consecutive cues as the caption scrolls across the screen. The _dedupe function (lines 55-66) eliminates this redundancy through intelligent merging:
- Exact duplicates: If the current segment's text matches the previous segment exactly, the script extends the previous segment's end time rather than creating a new entry.
- Continuations: If the current text starts with the previous text plus additional words (indicating a rolling caption), the script merges the texts and updates the end timestamp.
- New segments: Unique text triggers a new segment append.
This deduplication produces a concise transcript where each sentence appears only once, with accurate start and end times spanning the full duration of the spoken content.
Formatting the Final Transcript
The format_transcript function (lines 83-89) renders the deduplicated segments into human-readable output. It prefixes each cue with a [MM:SS] timestamp for easy reference:
def format_transcript(segments: list[dict]) -> str:
lines = []
for seg in segments:
start = seg["start"]
minutes = int(start // 60)
seconds = int(start % 60)
lines.append(f"[{minutes:02d}:{seconds:02d}] {seg['text']}")
return "\n".join(lines)
This produces output like:
[00:12] Hello, welcome to the video.
[00:15] Today we'll explore the implementation details.
Complete Usage Examples
The script functions both as a Python library and a standalone command-line tool.
Python API
from skills.watch.scripts.transcribe import parse_vtt, format_transcript
# Parse and format a complete transcript
transcript = format_transcript(parse_vtt("sample.vtt"))
print(transcript)
Command-Line Usage
# The script is executable and accepts a VTT file path
$ ./skills/watch/scripts/transcribe.py sample.vtt
[00:12] Hello, welcome to the video.
[00:15] Today we'll explore...
Filtering by Time Range
from skills.watch.scripts.transcribe import parse_vtt, filter_range, format_transcript
segments = parse_vtt("sample.vtt")
# Extract only the segment between 30 and 60 seconds
filtered = filter_range(segments, start_seconds=30, end_seconds=60)
print(format_transcript(filtered))
Summary
- transcribe.py parses VTT files using only Python standard library modules (
reandpathlib), requiring zero external dependencies. - The parser identifies cues using the
TS_REregex pattern, converts timestamps to seconds via_to_seconds, and strips HTML tags withTAG_RE. - The
_dedupefunction merges YouTube's rolling duplicate captions to produce clean, non-redundant output. - The script supports both programmatic use (via
parse_vttandformat_transcript) and command-line execution for quick transcript generation.
Frequently Asked Questions
What regex patterns does transcribe.py use to parse VTT files?
The script uses two primary regex patterns defined in skills/watch/scripts/transcribe.py: TS_RE matches WebVTT timestamp lines in the format HH:MM:SS.mmm --> HH:MM:SS.mmm, and TAG_RE removes HTML-style formatting tags from cue text. These patterns allow the parser to handle standard WebVTT syntax without requiring specialized caption parsing libraries.
How does transcribe.py handle duplicate or overlapping subtitles?
The _dedupe function detects YouTube's rolling caption behavior by comparing each segment's text with the previous segment. If text is identical or represents a continuation (starting with the previous text plus a space), the script merges the segments and extends the time range. This produces a single entry for each unique spoken phrase rather than 2-3 duplicate entries as the caption scrolls.
Can I use transcribe.py without installing dependencies?
Yes. The script uses only Python standard library modules (re and pathlib), making it completely dependency-free. You can copy skills/watch/scripts/transcribe.py to any environment with Python 3.x and use it immediately for VTT parsing, both as an imported module and as a command-line executable.
How does the script convert WebVTT timestamps to seconds?
The _to_seconds helper function splits the timestamp string by colons, then calculates total seconds using the formula hours * 3600 + minutes * 60 + seconds. This conversion occurs at the cue extraction stage, allowing the parser to store numeric timestamps for后续的 filtering and formatting operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →