Skip to content

Commit

Permalink
🚀 feat: v3.0.4 release - Fixed goatandsheep#20 issue
Browse files Browse the repository at this point in the history
  • Loading branch information
kms0219kms committed Aug 24, 2024
1 parent 10c0133 commit 86e00c6
Show file tree
Hide file tree
Showing 14 changed files with 761 additions and 785 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@

## [3.0.2] - 2024-08-24
- Add missing exported type definitions

## [3.0.4] - 2024-08-25
- Fixed [goatandsheep/node-srt#20 issue](https://github.com/goatandsheep/node-srt/issues/20), a bug that occurred when parsing srt files written in comma-separated millisecond time format.
67 changes: 16 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ This is a sister package to [osk/node-webvtt](https://github.com/osk/node-webvtt
For a Subrip file:

```text
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
```
Expand Down Expand Up @@ -95,44 +99,13 @@ For the above example we'd get:

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.

```javascript
const input = `WEBVTT
MALFORMEDCUE -->
This text is from a malformed cue. It should not be processed.
1
00:00.000 --> 00:00.001
test`;

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 } ]
}
*/
```

### Metadata

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

```text
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:

```javascript
Expand Down Expand Up @@ -224,13 +197,13 @@ Creates a subtitle playlist. For the above:
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:10.00000,
0.vtt
0.srt
#EXTINF:30.00000,
1.vtt
1.srt
#EXTINF:30.00000,
2.vtt
2.srt
#EXTINF:41.00000,
3.vtt
3.srt
#EXT-X-ENDLIST
```

Expand All @@ -241,20 +214,20 @@ Creates a list of HLS segments for the subtitles, returning an array of them wit
```json
[
{
"filename":"0.vtt",
"content":"WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:00:00.000 --> 00:00:01.000\nHello world!\n"
"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.vtt",
"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"
"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.vtt",
"content":"WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:01:00.000 --> 00:01:01.000\nFoo\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.vtt",
"content":"WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:01:50.000 --> 00:01:51.000\nBar\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"
}
]
```
Expand All @@ -267,11 +240,3 @@ This has been written with TDD so we've got a good coverage of the features.
pnpm install
pnpm test
```

## References

* Anne van Kesteren's [WebVTT validator](https://github.com/annevk/webvtt)
* [Live validator](https://quuz.org/webvtt/)
* [WebVTT Ruby parser and segmenter](https://github.com/opencoconut/webvtt-ruby)
* `mediasubtitlesegmenter` from Apple
* [WebVTT: The Web Video Text Tracks Format](https://w3c.github.io/webvtt/)
2 changes: 1 addition & 1 deletion jsr.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kms0219kms/node-srt-ts",
"version": "3.0.2",
"version": "3.0.4",
"description": "Parse SRT files, segments and generates HLS playlists for them with Typescript support.",
"exports": "./src/index.ts",
"publish": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "node-srt-ts",
"version": "3.0.2",
"version": "3.0.4",
"description": "Parse SRT files, segments and generates HLS playlists for them with Typescript support.",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export {hlsSegment, hlsSegmentPlaylist} from './lib/hls';

export {CompilerError, ParserError} from './types/exceptions';

export type {ICue} from './types/cue';
export type {ICue, IParsedCue} from './types/cue';
export type {IHlsSegment} from './types/hls';
export type {ISegment} from './types/segmenter';
export type {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function convertTimestamp(time: number): string {
const seconds = calculateSeconds(time).toString().padStart(2, '0');
const milliseconds = calculateMs(time).toString().padStart(3, '0');

return `${hours}:${minutes}:${seconds}.${milliseconds}`;
return `${hours}:${minutes}:${seconds},${milliseconds}`;
}

function calculateHours(time: number): number {
Expand Down
17 changes: 10 additions & 7 deletions src/lib/parser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

import {ICue} from '../types/cue';
import {IParsedCue} from '../types/cue';
import {ParserError} from '../types/exceptions/parser';
import {IParsedCueObject, IParsedObject} from '../types/parser';

Expand Down Expand Up @@ -48,7 +48,7 @@ function parseCues(cues: string[], strict = true): IParsedCueObject {
return null;
}
})
.filter(x => !!x) as ICue[];
.filter(x => !!x)!;

return {
cues: parsedCues,
Expand All @@ -65,7 +65,7 @@ function parseCues(cues: string[], strict = true): IParsedCueObject {
* @returns {object} cue Cue object with start, end, text and styles.
* Null if it's a note
*/
function parseCue(cue: string, i: number, strict: boolean): ICue | null {
function parseCue(cue: string, i: number, strict: boolean): IParsedCue | null {
let identifier = '';

let start = 0;
Expand Down Expand Up @@ -127,8 +127,9 @@ function parseCue(cue: string, i: number, strict: boolean): ICue | null {
);
}

// TODO better style validation
styles = times[1].replace(TIMESTAMP_REGEXP, '').trim();
// TODO: better style validation
// TODO: Find better way (rather than replace comma to dot)
styles = times[1].replace(',', '.').replace(TIMESTAMP_REGEXP, '').trim();

lines.shift();

Expand All @@ -139,11 +140,13 @@ function parseCue(cue: string, i: number, strict: boolean): ICue | null {
}

function validTimestamp(timestamp: string): boolean {
return TIMESTAMP_REGEXP.test(timestamp);
// TODO: Find better way (rather than replace comma to dot)
return TIMESTAMP_REGEXP.test(timestamp.replace(',', '.'));
}

function parseTimestamp(timestamp: string): number {
const matches = timestamp.match(TIMESTAMP_REGEXP);
// TODO: Find better way (rather than replace comma to dot)
const matches = timestamp.replace(',', '.').match(TIMESTAMP_REGEXP);

let secs = parseFloat(matches?.[1] || '0') * 60 * 60; // hours
secs += parseFloat(matches?.[2] || '0') * 60; // mins
Expand Down
5 changes: 5 additions & 0 deletions src/types/cue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ export interface ICue {
text: string;
styles: string;
}

export interface IParsedCue extends ICue {
start: number;
end: number;
}
6 changes: 3 additions & 3 deletions src/types/parser.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import {ICue} from './cue';
import {IParsedCue} from './cue';
import {ParserError} from './exceptions/parser';

export interface IBaseParsedObject {
valid: boolean;
cues: ICue[];
cues: IParsedCue[];
}

export interface IParsedObject extends IBaseParsedObject {
Expand All @@ -14,6 +14,6 @@ export interface IParsedObject extends IBaseParsedObject {
}

export interface IParsedCueObject {
cues: ICue[];
cues: IParsedCue[];
errors: (ParserError | any)[];
}
26 changes: 13 additions & 13 deletions test/compiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,17 +197,17 @@ describe('SRT compiler', () => {
valid: true,
};
const output = `1
00:02:15.001 --> 00:02:20.000
00:02:15,001 --> 00:02:20,000
Ta en kopp varmt te.
Det är inte varmt.
2
00:02:20.000 --> 00:02:25.000
00:02:20,000 --> 00:02:25,000
Har en kopp te.
Det smakar som te.
3
00:02:25.000 --> 00:02:30.000
00:02:25,000 --> 00:02:30,000
Ta en kopp
`;

Expand Down Expand Up @@ -242,17 +242,17 @@ Ta en kopp
valid: true,
};
const output = `1
00:19:59.529 --> 00:19:59.539
00:19:59,529 --> 00:19:59,539
Ta en kopp varmt te.
Det är inte varmt.
2
00:19:59.539 --> 00:19:59.549
00:19:59,539 --> 00:19:59,549
Har en kopp te.
Det smakar som te.
3
00:19:59.549 --> 00:19:59.558
00:19:59,549 --> 00:19:59,558
Ta en kopp
`;

Expand All @@ -273,7 +273,7 @@ Ta en kopp
valid: true,
};
const output = `1
00:02:15.999 --> 00:02:20.000
00:02:15,999 --> 00:02:20,000
Ta en kopp varmt te.
Det är inte varmt.
`;
Expand All @@ -286,9 +286,9 @@ Det är inte varmt.
compile({
cues: [
{
end: '1',
end: 1,
identifier: '',
start: '0',
start: 0,
styles: '',
text: 'Hello world!',
},
Expand All @@ -300,17 +300,17 @@ Det är inte varmt.

it('should be reversible', () => {
const input = `1
00:02:15.001 --> 00:02:20.000
00:02:15,001 --> 00:02:20,000
Ta en kopp varmt te.
Det är inte varmt.
2
00:02:20.000 --> 00:02:25.000
00:02:20,000 --> 00:02:25,000
Har en kopp te.
Det smakar som te.
3
00:02:25.000 --> 00:02:30.000
00:02:25,000 --> 00:02:30,000
Ta en kopp
`;
expect(compile(parse(input))).toBe(input);
Expand Down Expand Up @@ -364,7 +364,7 @@ Ta en kopp
valid: true,
};
const output = `1
00:02:15.001 --> 00:02:20.000 align:start line:0%
00:02:15,001 --> 00:02:20,000 align:start line:0%
Hello world
`;

Expand Down
2 changes: 1 addition & 1 deletion test/data/acid.srt
Original file line number Diff line number Diff line change
Expand Up @@ -5385,4 +5385,4 @@ T:100%

1344
00:07:09.800 --> 00:07:09.900 D:horizontal L:100% S:100% T:100%
100%
100%
2 changes: 1 addition & 1 deletion test/data/malformed.srt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
1096
01:45:13.056 --> 01:45:14.390
01:45:13,056 --> 01:45:14,390



Expand Down
Loading

0 comments on commit 86e00c6

Please sign in to comment.