Node WebVTT Parser, Compiler, and Segmenter
node-webvtt is a JavaScript library designed for comprehensive handling of WebVTT (Web Video Text Tracks) files. It provides functionalities for parsing WebVTT input into a structured JavaScript object, compiling such objects back into WebVTT format, and segmenting WebVTT content. A key differentiator is its integrated support for HLS (HTTP Live Streaming), enabling the generation of HLS playlists and segments directly from WebVTT data, which is crucial for delivering timed text tracks alongside adaptive video streams. The library is currently at version 1.9.4 (as of the last recorded release in early 2022) and maintains an active release cadence, primarily focusing on bug fixes, dependency updates, and minor feature enhancements. It offers both strict and non-strict parsing options, allowing developers to control error handling behavior when processing potentially malformed VTT files, and also supports parsing of WebVTT metadata.
Common errors
-
ParserError: Invalid cue timestamp (cue #X)
cause A cue in the WebVTT input has a malformed or invalid timestamp format (e.g., missing milliseconds, incorrect separators, start time greater than or equal to end time) and the parser is in strict mode.fixReview and correct the timestamp format in the WebVTT file. Alternatively, call `webvtt.parse(input, { strict: false })` to allow parsing with errors and inspect the `result.errors` array. -
ParserError: Header is incorrect
cause The WebVTT input string does not begin with 'WEBVTT' followed by a newline, or has other header-related issues, and the parser is in strict mode.fixEnsure the WebVTT file strictly adheres to the WebVTT specification, starting with 'WEBVTT' on the first line. For files with potential header variations, consider using `{ strict: false }`. -
TypeError: Cannot read properties of undefined (reading 'hlsSegmentPlaylist')
cause This typically occurs if `webvtt.hls` is `undefined`, often due to incorrect import (`import { hls } from 'node-webvtt';` instead of `import webvtt from 'node-webvtt';`) or using an outdated version of the library that did not yet include HLS segmentation features.fixEnsure you are using `import webvtt from 'node-webvtt';` (ESM) or `const webvtt = require('node-webvtt');` (CJS) and accessing `hls` as a property of the default export: `webvtt.hls.hlsSegmentPlaylist(...)`. Verify your `node-webvtt` version supports HLS features (added in v1.2).
Warnings
- gotcha The `parse` function operates in strict mode by default. If the WebVTT input is malformed (e.g., incorrect header, invalid cue timestamps), it will throw a `ParserError` exception, requiring developers to wrap calls in `try/catch` blocks.
- breaking The README indicates that the parser will not categorize cues starting and ending at the same time as an error when `strict` is `false`. However, changing this behavior to be `true` (i.e., treating `start === end` as an error) would constitute a breaking change in a 1.x version. Users relying on this lenient behavior should be aware of potential future changes.
- gotcha Support for parsing metadata from WebVTT files (e.g., `Kind: captions`) was added in version 1.3. Older versions will not correctly parse or return metadata, even if the `{ meta: true }` option is provided.
- gotcha The `webvtt.hls.hlsSegment` function's `startOffset` parameter (for MPEG TS timestamp mapping) defaults to `900000`. This default might not align with specific HLS stream requirements or expected subtitle timings if not explicitly set.
Install
-
npm install node-webvtt -
yarn add node-webvtt -
pnpm add node-webvtt
Imports
- webvtt
const webvtt = require('node-webvtt');import webvtt from 'node-webvtt';
- webvtt.parse
import { parse } from 'node-webvtt';import webvtt from 'node-webvtt'; const parsed = webvtt.parse(input, { strict: true }); - webvtt.hls.hlsSegmentPlaylist
import { hlsSegmentPlaylist } from 'node-webvtt';import webvtt from 'node-webvtt'; const playlist = webvtt.hls.hlsSegmentPlaylist(input, segmentDuration);
Quickstart
import webvtt from 'node-webvtt';
const webvttInput = `WEBVTT
Kind: captions
Language: en
00:00:00.000 --> 00:00:01.000
Hello world!
00:00:02.500 --> 00:00:04.000 align:start line:0%
This is a subtitle example.
00:00:05.000 --> 00:00:06.000
Foo bar.
`;
const segmentDuration = 2; // segments will be 2 seconds long
try {
// 1. Parse the WebVTT input with metadata option
const parsed = webvtt.parse(webvttInput, { strict: false, meta: true });
console.log('--- Parsed WebVTT (Non-strict with Meta) ---');
console.log(JSON.stringify(parsed, null, 2));
// 2. Compile the parsed object back to WebVTT string
// Note: For compilation, the input typically matches the parsed output structure.
const compiledInput = {
valid: true,
meta: parsed.meta,
cues: parsed.cues.filter(cue => cue.start < cue.end) // Filter out malformed cues if strict:false was used
};
const compiled = webvtt.compile(compiledInput);
console.log('\n--- Compiled WebVTT ---');
console.log(compiled);
// 3. Segment the WebVTT for HLS and generate a playlist
const segments = webvtt.hls.hlsSegment(webvttInput, segmentDuration);
console.log('\n--- HLS Segments ---');
segments.forEach((seg, i) => console.log(`Segment ${i}: ${seg.filename}\n${seg.content}`));
const playlist = webvtt.hls.hlsSegmentPlaylist(webvttInput, segmentDuration);
console.log('\n--- HLS Playlist (M3U8) ---');
console.log(playlist);
} catch (error) {
console.error('An error occurred during WebVTT processing:', error.message);
}