Subtitler
A Rust library for parsing, manipulating, and generating subtitles in multiple formats.
- 15 subtitle formats: SRT, WebVTT, ASS/SSA, MicroDVD, SubViewer, TTML/IMSC, SBV, LRC, SAMI, MPL2, SCC, EBU STL, DFXP, Whisper JSON
- Rich text extraction (bold, italic, underline, color, voice tags)
- Encoding detection and auto-decoding (UTF-8, UTF-16, BOM, chardetng fallback)
- Format detection, conversion, and validation
- Frame-based timecode support
- Streaming parsers for large files
- Utility operations: sort, merge, split, validate, framerate transform
- Async I/O powered by
tokio - Serialize/Deserialize via
serde
Installation
Or as a CLI tool:
Quick Start
Parse (any format, auto-detect)
// High-level entry points auto-detect the format.
let data = read?;
let file = parse_bytes?;
println!;
// Or from a file / URL (async, real I/O):
// let file = subtitler::parse_file("subtitle.srt").await?;
// let file = subtitler::parse_url("https://example.com/sub.vtt").await?;
The high-level entry points require the SubtitleFormat trait to be in scope
for methods like subtitles() / validate():
use SubtitleFormat; // brings trait methods into scope
Parse a specific format (low-level)
use srt;
// Parsing cores are sync (they never did real async I/O).
let content = "1\n00:00:01,000 --> 00:00:03,500\nHello, world!\n\n";
let subtitles = parse_content?;
println!;
use vtt;
let content = "WEBVTT\n\n1\n00:00:01.000 --> 00:00:03.500\nHello, world!\n\n";
let subtitles = parse_content?;
use ass;
let content = "[Script Info]\nScriptType: v4.00+\n\n[V4+ Styles]\nFormat: Name, Fontname, ...\nStyle: Default,Arial,20,...\n\n[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\nDialogue: 0,0:00:01.00,0:00:03.50,Default,,0,0,0,,Hello!\n";
let file = parse_content?;
println!;
Convert between formats
use SubtitleFormat;
use Format;
let file = parse_file.await?;
let vtt_str = file.to_string_with_format;
write?;
Generate subtitle files
use Subtitle;
use srt;
async
Detect format
use detect_format;
use Format;
let data = read?;
match detect_format
Feature flags
All formats are enabled by default. To trim compile size, disable the formats you don't need:
[]
= { = "2.0", = false, = ["srt", "vtt"] }
| Flag | Format | Notes |
|---|---|---|
srt |
SubRip (.srt) |
Most common format |
vtt |
WebVTT (.vtt) |
HTML5 standard |
ass |
Advanced SubStation Alpha (.ass) |
Advanced styling |
ssa |
SubStation Alpha (.ssa) |
Legacy ASS format |
microdvd |
MicroDVD (.sub) |
Frame-based |
subviewer |
SubViewer | DVD format |
ttml |
TTML/IMSC (.ttml, .xml) |
Broadcast standard |
sbv |
YouTube SBV (.sbv) |
YouTube format |
lrc |
Lyrics LRC (.lrc) |
Song lyrics |
sami |
SAMI (.smi) |
Microsoft format, popular in Asia |
mpl2 |
MPL2 (.mpl) |
Frame-based, popular in Eastern Europe |
scc |
SCC (.scc) |
CEA-608 closed captions, US broadcast |
ebu_stl |
EBU STL (.stl) |
European broadcast binary format |
http |
parse_url() via reqwest |
Network support |
See MIGRATION.md for upgrading from 0.1.x.
API Reference
Data Model
SRT Module (subtitler::srt)
| Function | Description |
|---|---|
parse_file(path) |
Parse SRT from file |
parse_bytes(data) |
Parse SRT from bytes |
parse_content(content) |
Parse SRT from string |
parse_url(url) |
Parse SRT from HTTP URL (requires http feature) |
generate(subtitles, path) |
Write SRT to file |
to_string(subtitles) |
Format subtitles as SRT string |
detect_format(data) |
Detect if data is SRT |
WebVTT Module (subtitler::vtt)
| Function | Description |
|---|---|
parse_file(path) |
Parse VTT from file |
parse_bytes(data) |
Parse VTT from bytes |
parse_content(content) |
Parse VTT from string |
parse_content_full(content) |
Parse VTT returning header + subtitles |
parse_url(url) |
Parse VTT from HTTP URL (requires http feature) |
generate(subtitles, path) |
Write VTT to file |
to_string(subtitles, header) |
Format subtitles as VTT string |
detect_format(data) |
Detect if data is VTT |
ASS Module (subtitler::ass)
| Function | Description |
|---|---|
parse_content(content) |
Parse ASS/SSA from string, returns SubtitleFile |
parse_file(path) |
Parse ASS/SSA from file (async) |
parse_bytes(data) |
Parse ASS/SSA from byte slice |
parse_url(url) |
Parse ASS/SSA from HTTP URL (requires http feature) |
to_string(info, styles, subtitles) |
Format as ASS string |
detect_format(data) |
Detect if data is ASS/SSA |
MicroDVD Module (subtitler::microdvd)
| Function | Description |
|---|---|
parse_content(content, fps) |
Parse MicroDVD frame-based content |
to_string(subtitles, fps) |
Format as MicroDVD string |
to_string_with_fps_header(subtitles, fps) |
Format with FPS declaration header |
detect_format(data) |
Detect if data is MicroDVD |
SubViewer Module (subtitler::subviewer)
| Function | Description |
|---|---|
parse_content(content) |
Parse SubViewer 1.0/2.0 content |
to_string(subtitles) |
Format as SubViewer 2.0 with headers |
detect_format(data) |
Detect if data is SubViewer |
Encoding Utilities (subtitler::encoding)
| Function | Description |
|---|---|
detect_encoding(data) |
Auto-detect character encoding (UTF-8/16/BOM/chardetng) |
decode_to_string(data) |
Decode bytes to string using detected encoding |
Timestamp Utilities (subtitler::utils)
| Function | Description |
|---|---|
parse_timestamp(ts) |
Parse "00:00:01,500" → 1500 ms |
parse_timestamps(ts) |
Parse "00:00:01,000 --> 00:00:03,500" → Timestamp |
format_timestamp(ms, fmt) |
Format ms to "00:00:01,000" (SRT) or "00:00:01.000" (VTT) |
pad_left(value, length) |
Zero-pad integer to fixed width |
Frame Utilities (subtitler::model)
| Function | Description |
|---|---|
ms_to_frames(ms, fps) |
Convert milliseconds to frame count |
frames_to_ms(frames, fps) |
Convert frame count to milliseconds |
SubtitleFile Methods (subtitler::model::SubtitleFile)
| Method | Description |
|---|---|
subtitles() |
Get reference to subtitle list |
subtitles_mut() |
Get mutable reference |
format() |
Get detected format enum |
shift_all(offset_ms) |
Shift all timestamps |
sort() |
Sort by start time |
validate() |
Check for timing issues |
validate_extended(max_chars, max_gap, max_cps) |
Extended validation |
merge_adjacent(max_gap_ms) |
Merge subtitles within gap threshold |
remove_overlaps() |
Fix overlapping subtitles by adjusting start times |
split_long(max_chars) |
Split long subtitles at word boundaries |
transform_framerate(in_fps, out_fps) |
Rescale timestamps for framerate change |
map(fn) |
Transform each subtitle (consuming) |
filter(fn) |
Filter subtitles (consuming) |
to_string() |
Format to appropriate format string |
Subtitle Methods (subtitler::model::Subtitle)
| Method | Description |
|---|---|
new(start, end, text) |
Create new subtitle |
shift(offset_ms) |
Shift this subtitle's timing |
duration_ms() |
Get duration in milliseconds |
chars_per_second() |
Calculate characters-per-second rate |
reading_speed_wpm() |
Calculate reading speed in words per minute |
strip_tags() |
Remove HTML/ASS formatting tags from subtitle text |
Validation Issues (subtitler::model::ValidationIssue)
| Variant | Description |
|---|---|
Overlap |
Two subtitles have overlapping time ranges |
NegativeDuration |
End time is before start time |
ZeroDuration |
Start and end times are equal |
DecreasingStartTime |
Start times are not monotonically increasing |
TooLongGap |
Gap between subtitles exceeds threshold |
TextTooLong |
Subtitle text exceeds character limit |
CpsTooHigh |
Characters-per-second exceeds threshold |
Format Detection (subtitler::detect_format)
Auto-detects SRT (by index+timestamp pattern), WebVTT (by WEBVTT header), or ASS/SSA (by [Script Info] section).
CLI Usage
Parse subtitles
# Parse SRT file and display contents
# Parse with JSON output
# Parse from URL
# Parse from stdin
|
# Force format
Convert between formats
# Auto-detect source, infer target from extension
# Explicit source and target
# Convert with time shift
# Pipe to stdout
Validate subtitles
# Basic timing validation
# Extended validation with custom thresholds
# Basic checks only (no text limits)
# JSON output
Edit & transform
# Sort by time
# Shift all timestamps (+500ms delay, -200ms advance)
# Merge adjacent subtitles (gap <= 300ms)
# Split long subtitles (max 42 chars per line)
# Multiple operations
# Framerate conversion
File info & statistics
# Output:
# File: movie.srt
# Format: srt
# Subtitles: 150
# Time range: 0ms -> 5400000ms
# Duration: 5400000ms (5400.0s)
# Avg duration: 2500ms
# Min duration: 500ms
# Max duration: 8000ms
# Total chars: 4500
# Max CPS: 22.5
# Timing issues: 0
Detect format
Features
| Feature | Default | Description |
|---|---|---|
http |
Yes | Enable parse_url() via reqwest |
Examples
See the examples directory for 23 usage examples:
SRT/WebVTT:
parse-srt-file— Parse SRT from fileparse-srt-content— Parse SRT from inline contentparse-srt-http— Parse SRT from URLcreate-srt-file— Generate SRT fileparse-vtt-file— Parse VTT from fileparse-vtt-content— Parse VTT from inline contentparse-vtt-http— Parse VTT from URLcreate-vtt-file— Generate VTT file
ASS/SSA:
parse-ass-content— Parse ASS from inline content
Format Conversion:
format-convert— Convert between SRT/VTT/ASS formatsconvert-all-formats— Convert between all 11 formats
Advanced Features:
stream-parse— Streaming parser for large filesquality-report— Generate quality reportnormalize-text— Text normalization and cleanupedit-operations— Sort, validate, merge, and split operationsframe-conversion— Frame-based timecode conversionvalidate-subtitle— Detect and validate subtitlesttml-lyrics-tutorial— TTML and LRC parsing tutorialtranslate-subtitle— Batch translation workflow
SAMI/MPL2 (v1.3.0):
parse-sami-content— Parse SAMI formatparse-mpl2-content— Parse MPL2 with frame conversioncreate-sami-file— Create multi-language SAMIcreate-mpl2-file— Create MPL2 with custom fps
License
Apache 2.0