Skip to main content
Home

Parse SRT files, segments and generates HLS playlists for them with Typescript support.

This package works with Node.js, Deno, Bun
This package works with Node.js
This package works with Deno
This package works with Bun
JSR Score
70%
Published
a year ago (3.0.4)

Subrip (.srt) compiler/parser/segmenter with Typescript support

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

CircleCI npm version npm downloads

This is a sister package to osk/node-webvtt and forked from goatandsheep/node-srt.

Usage

For a Subrip file:

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

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

3
00:01:00.000 --> 00:01:01.000
Foo

4
00:01:50.000 --> 00:01:51.000
Bar

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

import { parse, compile, hlsSegmentPlaylist, hlsSegment } from 'node-srt-ts';

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

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

Parsing

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:

{
   "valid":true,
   "cues":[
      {
         "identifier":"",
         "start":0,
         "end":1,
         "text":"Hello world!",
         "styles":""
      },
      {
         "identifier":"",
         "start":30,
         "end":31,
         "text":"This is a subtitle",
         "styles":"align:start line:0%"
      },
      {
         "identifier":"",
         "start":60,
         "end":61,
         "text":"Foo",
         "styles":""
      },
      {
         "identifier":"",
         "start":110,
         "end":111,
         "text":"Bar",
         "styles":""
      }
   ]
}

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

  • 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.

Metadata

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:

{
   "valid":true,
   "meta":{
      "Kind": "captions",
      "Language": "en"
   },
   "cues":[
      {
         "identifier":"",
         "start":0,
         "end":1,
         "text":"Hello world!",
         "styles":""
      }
   ]
}

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

Compiling

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);

/*
1
00:02:15.001 --> 00:02:20.000
Hello world
*/

Segmenting

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:

#EXTM3U
#EXT-X-TARGETDURATION:41
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:10.00000,
0.srt
#EXTINF:30.00000,
1.srt
#EXTINF:30.00000,
2.srt
#EXTINF:41.00000,
3.srt
#EXT-X-ENDLIST

HLS Segments

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

[
   {
      "filename":"0.srt",
      "content":"X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n1\n00:00:00.000 --> 00:00:01.000\nHello world!\n"
   },
   {
      "filename":"1.srt",
      "content":"X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n2\n00:00:30.000 --> 00:00:31.000 align:start line:0%\nThis is a subtitle\n"
   },
   {
      "filename":"2.srt",
      "content":"X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n3\n00:01:00.000 --> 00:01:01.000\nFoo\n"
   },
   {
      "filename":"3.srt",
      "content":"X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n4\n00:01:50.000 --> 00:01:51.000\nBar\n"
   }
]

Development

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

pnpm install
pnpm test

New Ticket: Report package

Please provide a reason for reporting this package. We will review your report and take appropriate action.

Please review the JSR usage policy before submitting a report.

Add Package

deno add jsr:@kms0219kms/node-srt-ts

Import symbol

import * as node_srt_ts from "@kms0219kms/node-srt-ts";
or

Import directly with a jsr specifier

import * as node_srt_ts from "jsr:@kms0219kms/node-srt-ts";

Add Package

pnpm i jsr:@kms0219kms/node-srt-ts
or (using pnpm 10.8 or older)
pnpm dlx jsr add @kms0219kms/node-srt-ts

Import symbol

import * as node_srt_ts from "@kms0219kms/node-srt-ts";

Add Package

yarn add jsr:@kms0219kms/node-srt-ts
or (using Yarn 4.8 or older)
yarn dlx jsr add @kms0219kms/node-srt-ts

Import symbol

import * as node_srt_ts from "@kms0219kms/node-srt-ts";

Add Package

vlt install jsr:@kms0219kms/node-srt-ts

Import symbol

import * as node_srt_ts from "@kms0219kms/node-srt-ts";

Add Package

npx jsr add @kms0219kms/node-srt-ts

Import symbol

import * as node_srt_ts from "@kms0219kms/node-srt-ts";

Add Package

bunx jsr add @kms0219kms/node-srt-ts

Import symbol

import * as node_srt_ts from "@kms0219kms/node-srt-ts";