Subtitler
A Rust library for parsing, manipulating, and generating subtitles in multiple formats.

- SRT, WebVTT, ASS/SSA, MicroDVD, and SubViewer format support
- 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
- Utility operations: sort, merge, split, validate, framerate transform
- Async I/O powered by
tokio
- Serialize/Deserialize via
serde
Installation
cargo add subtitler
Or as a CLI tool:
cargo install subtitler
Quick Start
Parse (any format, auto-detect)
let data = std::fs::read("subtitle.srt")?;
let file = subtitler::parse_bytes(&data)?;
println!("{} subtitles, format: {:?}", file.subtitles().len(), file.format());
The high-level entry points require the SubtitleFormat trait to be in scope
for methods like subtitles() / validate():
use subtitler::SubtitleFormat;
Parse a specific format (low-level)
use subtitler::srt;
let content = "1\n00:00:01,000 --> 00:00:03,500\nHello, world!\n\n";
let subtitles = srt::parse_content(content)?;
println!("{:?}", subtitles);
use subtitler::vtt;
let content = "WEBVTT\n\n1\n00:00:01.000 --> 00:00:03.500\nHello, world!\n\n";
let subtitles = vtt::parse_content(content)?;
use subtitler::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 = ass::parse_content(content)?;
println!("{}", file.to_string());
Convert between formats
use subtitler::SubtitleFormat;
use subtitler::model::Format;
let file = subtitler::parse_file("input.srt").await?;
let vtt_str = file.to_string_with_format(&Format::Vtt);
std::fs::write("output.vtt", vtt_str)?;
Generate subtitle files
use subtitler::model::Subtitle;
use subtitler::srt;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let subtitles = vec![
Subtitle::new(1000, 3500, "Hello!"),
Subtitle::new(4000, 6500, "World!"),
];
srt::generate(&subtitles, "output.srt").await?;
Ok(())
}
Detect format
use subtitler::detect_format;
use subtitler::model::Format;
let data = std::fs::read("unknown.sub")?;
match detect_format(&data) {
Some(Format::Srt) => println!("SRT detected"),
Some(Format::Vtt) => println!("WebVTT detected"),
Some(Format::Ass) => println!("ASS detected"),
Some(Format::Ssa) => println!("SSA detected"),
Some(Format::MicroDvd) => println!("MicroDVD detected"),
Some(Format::SubViewer) => println!("SubViewer detected"),
None => println!("Unknown format"),
}
Feature flags
All formats are enabled by default. To trim compile size, disable the formats
you don't need:
[dependencies]
subtitler = { version = "1.0", default-features = false, features = ["srt", "vtt"] }
| Flag |
Format |
srt |
SubRip (.srt) |
vtt |
WebVTT (.vtt) |
ass |
Advanced SubStation Alpha (.ass) |
ssa |
SubStation Alpha (.ssa) |
microdvd |
MicroDVD (.sub) |
subviewer |
SubViewer |
ttml |
TTML/IMSC (.ttml, .xml) |
sbv |
YouTube SBV (.sbv) |
lrc |
Lyrics LRC (.lrc) |
http |
parse_url() via reqwest |
See MIGRATION.md for upgrading from 0.1.x.
API Reference
Data Model
pub struct Subtitle {
pub index: Option<usize>, pub start: u64, pub end: u64, pub text: String, pub settings: Option<String>, pub text_parts: Vec<TextPart>,
pub style: Option<String>, pub actor: Option<String>, pub layer: Option<i32>, pub margin_l: Option<i32>, pub margin_r: Option<i32>, pub margin_v: Option<i32>, pub effect: Option<String>, pub is_comment: bool, }
pub struct TextPart {
pub text: String,
pub bold: bool,
pub italic: bool,
pub underline: bool,
pub color: Option<String>,
pub voice: Option<String>,
}
pub struct AssStyle {
pub name: String,
pub fontname: String,
pub fontsize: u32,
pub primary_color: String,
pub secondary_color: String,
}
pub enum SubtitleFile {
Srt(Vec<Subtitle>),
Vtt { header: Option<String>, subtitles: Vec<Subtitle> },
Ass { info: HashMap<String, String>, styles: Vec<AssStyle>, subtitles: Vec<Subtitle> },
}
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)
pub fn detect_format(data: &[u8]) -> Option<SubtitleFormat>
Auto-detects SRT (by index+timestamp pattern), WebVTT (by WEBVTT header), or ASS/SSA (by [Script Info] section).
CLI Usage
Parse subtitles
subtitler parse movie.srt
subtitler parse movie.vtt --json
subtitler parse https://example.com/subtitles.srt
cat movie.srt | subtitler parse -
subtitler parse data.txt --format srt
Convert between formats
subtitler convert input.srt output.vtt
subtitler convert input.srt output.ass --from srt --to ass
subtitler convert input.srt output.vtt --shift -500
subtitler convert input.srt -
Validate subtitles
subtitler validate movie.srt
subtitler validate movie.srt --max-chars 42 --max-gap 5000 --max-cps 25
subtitler validate movie.srt --basic
subtitler validate movie.srt --json
Edit & transform
subtitler edit input.srt --output output.srt --sort
subtitler edit input.srt --output output.srt --shift 500
subtitler edit input.srt --output output.srt --shift=-200
subtitler edit input.srt --output output.srt --merge 300
subtitler edit input.srt --output output.srt --split 42
subtitler edit input.srt --output output.vtt --sort --shift -300 --merge 100
subtitler edit input.srt --output output.srt --transform-fps 23.976 25.0
File info & statistics
subtitler info movie.srt
Detect format
subtitler detect unknown.sub
Features
| Feature |
Default |
Description |
http |
Yes |
Enable parse_url() via reqwest |
Examples
See the examples directory for more usage patterns:
parse-srt-file — Parse SRT from file
parse-srt-content — Parse SRT from inline content
parse-srt-http — Parse SRT from URL
create-srt-file — Generate SRT file
parse-vtt-file — Parse VTT from file
parse-vtt-content — Parse VTT from inline content
parse-vtt-http — Parse VTT from URL
create-vtt-file — Generate VTT file
parse-ass-content — Parse ASS from inline content
format-convert — Convert between SRT/VTT/ASS formats
utility-ops — Sort, validate, merge, and split operations
frame-conversion — Frame-based timecode conversion
License
Apache 2.0