2.0.1 • Public • Published

Subrip (.srt) compiler parser and segmenter

Compiles, parses Subrip files, segments and generates HLS playlists for them.


This is a sister package to osk/node-webvtt.


For a Subrip file:

00:00:00.000 --> 00:00:01.000
Hello world!

00:00:30.000 --> 00:00:31.000 align:start line:0%
This is a subtitle

00:01:00.000 --> 00:01:01.000

00:01:50.000 --> 00:01:51.000

We can parse, segment and create HLS playlists, and compile back to Subrip format:

const subrip = require('node-srt');

const segmentDuration = 10; // default to 10
const startOffset = 0; // Starting MPEG TS offset to be used in timestamp map, default 900000

const parsed = subrip.parse(input);
const compile = subrip.compile(input);
const segmented = subrip.parse(input, segmentDuration);
const playlist = subrip.hls.hlsSegmentPlaylist(input, segmentDuration);
const segments = subrip.hls.hlsSegment(input, segmentDuration, startOffset);


Parses the Subrip file and returns an object with valid === true if parsed correctly and an array of cues parsed.

Each cue can have:

  • identifier - Id, if any of the cue
  • start - Start time of cue in seconds
  • end - End time of cue in seconds
  • text - Text of the subtitle
  • styles - If any of the cue

If the Subrip file is invalid, the parser will throw a ParserError exception. So for safety, calls to parse should be in try catch.

For the above example we'd get:

         "text":"Hello world!",
         "text":"This is a subtitle",
         "styles":"align:start line:0%"

By default the parser is strict. It will throw errors if:

  • Header is incorrect, i.e. does not start with WEBVTT
  • If any cue is malformed in any way

Setting the option parameter of strict to false will allow files with malformed cues to be parsed. The resulting object will have valid === false and all errors in an errors array.

If strict is set to false, the parser will also not categorize it as an error if a cue starts and ends at the same time. This might be the correct behaviour but changing would introduce a breaking change in version 1.x.

const input = `WEBVTT

This text is from a malformed cue. It should not be processed.

00:00.000 --> 00:00.001

const result = parse(input, { strict: false });

result = {
  valid: false,
  strict: false,
  cues: [ { identifier: '1', start: 0, end: 0.001, text: 'test', styles: '' } ],
  errors: [ { Error: Invalid cue timestamp (cue #0) message: 'Invalid cue timestamp (cue #0)', error: undefined } ]


Some Subrip strings may also contain lines of metadata after the initial WEBVTT line, for example:

00:00:00.000 --> 00:00:01.000
Hello world!

By passing { meta: true } to the parse method, these metadata will be returned as an object called meta. For example, parsing the above example:

parse(subrip, { meta: true });

would return the following:

      "Kind": "captions",
      "Language": "en"
         "text":"Hello world!",

If no metadata is available, meta will be set to null in the result if the option is specified.


Compiles JSON from the above format back into a Subrip string. If a meta key is in the input, it will be compiled as well. The meta value must be an object and each key and value must be a string.

If the object is missing any attributes, the compiler will throw a CompilerError exception. So for safety, calls to compile should be in try catch.

const input = {
  meta: {
    Kind: 'captions',
    Language: 'en'
  cues: [{
    end: 140,
    identifier: '1',
    start: 135.001,
    text: 'Hello world',
    styles: ''
  valid: true

const result = compile(input);

00:02:15.001 --> 00:02:20.000
Hello world


Segments a subtitle according to how it should be segmented for HLS subtitles.

  • Does a one pass of the cues for segmenting, this might have been a good idea or bad, only time will tell
  • The One and Only Source of Truth is Apple's mediasubtitlesegmenter CLI

For the above example:

    { duration: 10, cues: [ [Object] ] },
    { duration: 30, cues: [ [Object] ] },
    { duration: 30, cues: [ [Object] ] },
    { duration: 41, cues: [ [Object] ] }

HLS playlist

Creates a subtitle playlist. For the above:


HLS Segments

Creates a list of HLS segments for the subtitles, returning an array of them with filename and content.

      "content":"WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:00:00.000 --> 00:00:01.000\nHello world!\n"
      "content":"WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:00:30.000 --> 00:00:31.000 align:start line:0%\nThis is a subtitle\n"
      "content":"WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:01:00.000 --> 00:01:01.000\nFoo\n"
      "content":"WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:01:50.000 --> 00:01:51.000\nBar\n"


For segmenting a Subrip file quickly, you can use the included CLI tool:

./subrip-segment.js -v --target-duration 10 -o ./subs subs.vtt
$ ./subrip-segment.js --help

  Usage: subrip-segment [options] <subrip file>


    -h, --help                        output usage information
    -V, --version                     output the version number
    -t, --target-duration [duration]  Target duration for each segment in secods, defaults to 10
    -o, --output-directory [dir]      Output directory for segments and playlist
    -v, --verbose                     Chatty output
    -s, --silent                      No output


This has been written with TDD so we've got a good coverage of the features.

npm install
npm test
mocha -w
<write failing test>
<write passing code>
<lather, rinse, repeat>


Package Sidebar


npm i node-srt

Weekly Downloads






Unpacked Size

27.1 kB

Total Files


Last publish


  • goatandsheep