Subtitle JavaScript library and command line tool with no dependencies.
This is a rewrite of the original subsrt package in TypeScript and using ESM syntax.
Table of Contents
Install the module
npm install -g subsrt
Command line
subsrt --help
subsrt convert sample.sub sample.srt
Using as Node.js library
import subsrt from "subsrt";
// MicroDVD (.sub) content
const sub = "{14975}{104000}Hi, my name is...";
// Convert to SubRip (.srt) content
const srt = subsrt.convert(sub, { format: "srt", fps: 25 });
- MicroDVD SUB (.sub)
- SubRip (.srt)
- SubViewer (.sbv)
- WebVTT (.vtt)
- SubStation Alpha (.ssa and .ass)
- SAMI (.smi) aka Synchronized Accessible Media Interchange
- LRC (.lrc) aka LyRiCs
- JSON (.json)
Usage:
subsrt [command] [options]
Commands:
list List supported formats
parse [src] [json] Parse a subtitle file
build [json] [dst] Create a subtitle file from captions
detect [src] Detect subtitle file format, if supported
resync [src] [dst] Resync FPS or shift time (+/- offset)
convert [src] [dst] Converts a subtitle format
Options:
--help Print this message
--eol [chars] End of line chars, e.g. \r\n
--fps [fps] Frames per second for .sub format
--offset [time] Resync time shift offset in ms
--format [ext] Subtitle format to convert/build/parse
--verbose Enable detailed logging
--version Print version number
Examples:
subsrt parse sample.sbv
subsrt parse sample.srt output.json
subsrt parse sample.sub --fps 30
subsrt build input.json output.vtt
subsrt build input.json --format sbv
subsrt detect unknown.txt
subsrt convert sample.srt sample.vtt
subsrt convert --offset -250 sample.srt sample.ssa
subsrt resync --offset +3000 input.srt output.srt
subsrt resync --fps 25-30 input.sub output.sub
The Node.js library supports converting, parsing and building subtitle file formats. Subtitles can also be resynced by shifting time offset, extending the duration or changing FPS.
import subsrt from "subsrt";
const list = subsrt.list();
console.log(list.join(", "));
// vtt, lrc, smi, ssa, ass, sub, srt, sbv, json
Format name is used in conversion options, e.g. { format: "srt" }
Use subsrt.format.name
to access functions directly
import subsrt from "subsrt";
const handler = subsrt.format.srt;
// handler = { name: 'srt', helper: [object], parse: [function], build: [function] }
To implement a new subtitle format handler do the following
import subsrt from "subsrt";
subsrt.format.my = {
// "my" is the format name
name: "my",
parse: (content, options) => {
const captions = [];
// ...
return captions;
},
build: (captions, options) => {
const content = "";
// ...
return content;
},
detect: (content) => {
if (content.indexOf("my") > 0) {
return true; // Recognized
}
},
};
Recognizes format by content
import subsrt from "subsrt";
let content = "";
content += "5" + "\r\n";
content += "00:00:16,700 --> 00:00:21,480" + "\r\n";
content += "Okay, so we have all the ingredients laid out here" + "\r\n";
const format = subsrt.detect(content);
// format = "srt"
Parse a subtitle file
import { readFileSync } from "fs";
import subsrt from "subsrt";
// Read a .srt file
const content = readFileSync("sample.srt", "utf8");
// Parse the content
const options = { verbose: true };
const captions = subsrt.parse(content, options);
// Output to console
console.log(captions);
Example of output
[
{
"type": "caption", // "caption" or "meta"
"index": 1, // Caption id, usually a sequential number
"start": 599, // Time to show caption in milliseconds
"end": 4160, // Time to hide caption in milliseconds
"duration": 3561, // Calculated caption duration
"content": ">> ALICE: Hi, my name is Alice Miller and this is John Brown", // Formatted content
"text": "Hi, my name is Alice Miller and this is John Brown" // Plain text content
},
{
"type": "caption",
"index": 2,
"start": 4160,
"end": 6770,
"duration": 2610,
"content": ">> JOHN: and we're the owners of Miller Bakery.",
"text": "and we're the owners of Miller Bakery."
}
// ...
]
List of options
format
: explicitly select a parser, values:sub
,srt
,sbv
,vtt
,lrc
,smi
,ssa
,ass
,json
, default is undefined to auto detectverbose
: set to true for extra messages, console only, default:false
eol
: end of line character(s), default:\r\n
fps
: frames per second,sub
format onlypreserveSpaces
: keep white space lines,smi
format only
Build a subtitle file
import { writeFileSync } from "fs";
import subsrt from "subsrt";
// Sample captions
const captions = [
{
start: 599, // Time to show caption in milliseconds
end: 4160, // Time to hide caption in milliseconds
text: "Hi, my name is Alice Miller and this is John Brown", // Plain text content
},
{
start: 4160,
end: 6770,
text: "and we're the owners of Miller Bakery.",
},
];
// Build the WebVTT content
const options = { format: "vtt" };
const content = subsrt.build(captions, options);
// Write content to .vtt file
writeFileSync("generated.vtt", content);
List of options
format
: required, output subtitle format, values:sub
,srt
,sbv
,vtt
,lrc
,smi
,ssa
,ass
,json
, default:srt
verbose
: set to true for extra messages, console only, default:false
fps
: frames per second,sub
format onlycloseTags
: set to true to close tags,smi
format only
Using a single action to convert from one to another subtitle format
import { readFileSync, writeFileSync } from "fs";
import subsrt from "subsrt";
// Read a .srt file
const srt = readFileSync("sample.srt", "utf8");
// Convert .srt to .sbv
const sbv = subsrt.convert(srt, { format: "sbv" });
// Write content to .sbv file
writeFileSync("converted.sbv", sbv);
List of options
format
: required, output subtitle format, values:sub
,srt
,sbv
,vtt
,lrc
,smi
,ssa
,ass
,json
, default:srt
verbose
: set to true for extra messages, console only, default:false
eol
: end of line character(s), default:\r\n
fps
: frames per second,sub
format onlyresync
: resync options, see below
An example to make an extra 3-second delay
import { readFileSync } from "fs";
import subsrt from "subsrt";
// Read a .srt file
const content = readFileSync("sample.srt", "utf8");
const captions = subsrt.parse(content);
// Returns updated captions
const resynced = subsrt.resync(captions, { offset: 3000 });
Use minus sign to display captions earlier
const resynced = subsrt.resync(captions, { offset: -3000 });
The .sub format has captions saved in frame units. To shift from 25 FPS to 30 FPS do the following
import { readFileSync } from "fs";
import subsrt from "subsrt";
// Read a .sub file
const content = readFileSync("sample.sub", "utf8");
const captions = subsrt.parse(content, { fps: 25 }); // The .sub file content is saved in 25 FPS units
// Convert to 30 FPS, make sure to set 'frame' to true to convert frames instead of time
const resynced = subsrt.resync(content, { ratio: 30 / 25, frame: true });
Extend caption duration by 500 ms
// Argument 'a' is an array with two elements: [ start, end ]
// Return shifted [ start, end ] values
const resynced = subsrt.resync(content, (a) => [a[0], a[1] + 500]);
Download the source code from the GitHub repository.
Install required packages if any
pnpm install
Run the unit tests
pnpm test
Distributed under the MIT License. See LICENSE.txt for more information.