@remotion/captions 4.0.529 → 4.0.531

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -142,12 +142,73 @@ function toMilliseconds(time) {
142
142
  }
143
143
  return parseInt(first, 10) * 3600000 + parseInt(second, 10) * 60000 + parseInt(seconds, 10) * 1000 + parseInt(millis, 10);
144
144
  }
145
- var parseSrt = ({ input }) => {
145
+ var parseSrt = ({
146
+ input,
147
+ strict = false
148
+ }) => {
146
149
  const inputLines = input.replace(/^\uFEFF/, "").replace(/\r\n/g, `
147
150
  `).replace(/\r/g, `
148
151
  `).split(`
149
152
  `);
150
153
  const captions = [];
154
+ if (strict) {
155
+ const timestamp = (token, cue, line) => {
156
+ const match = /^(\d+):(\d{2}):(\d{2})[,.](\d{3})$/.exec(token);
157
+ if (!match || Number(match[2]) > 59 || Number(match[3]) > 59) {
158
+ throw new Error(`SRT cue ${cue}, line ${line}: invalid timestamp "${token}". Use HH:MM:SS,mmm.`);
159
+ }
160
+ const value = Number(match[1]) * 3600000 + Number(match[2]) * 60000 + Number(match[3]) * 1000 + Number(match[4]);
161
+ if (!Number.isFinite(value)) {
162
+ throw new Error(`SRT cue ${cue}, line ${line}: timestamp is too large.`);
163
+ }
164
+ return value;
165
+ };
166
+ let i = 0;
167
+ while (i < inputLines.length) {
168
+ if (inputLines[i].trim() === "") {
169
+ i++;
170
+ continue;
171
+ }
172
+ const cue = captions.length + 1;
173
+ const headerLine = i + 1;
174
+ if (!/^\d+$/.test(inputLines[i].trim())) {
175
+ throw new Error(`SRT cue ${cue}, line ${headerLine}: expected a numeric cue index.`);
176
+ }
177
+ i++;
178
+ const timingLine = inputLines[i];
179
+ const timing = timingLine?.match(/^\s*(\S+)\s+-->\s+(\S+)(.*)$/);
180
+ const settings = timing?.[3].trim();
181
+ if (!timing || settings && !settings.split(/\s+/).every((setting) => /^(?:(?:X[12]|Y[12]):\d+|(?:align|position|line|size|vertical):\S+)$/.test(setting))) {
182
+ throw new Error(`SRT cue ${cue}, line ${i + 1}: expected start --> end timestamps.`);
183
+ }
184
+ const startMs = timestamp(timing[1], cue, i + 1);
185
+ const endMs = timestamp(timing[2], cue, i + 1);
186
+ if (endMs < startMs) {
187
+ throw new Error(`SRT cue ${cue}, line ${i + 1}: end must not be earlier than start.`);
188
+ }
189
+ i++;
190
+ const text = [];
191
+ while (i < inputLines.length && inputLines[i].trim() !== "") {
192
+ text.push(inputLines[i]);
193
+ i++;
194
+ }
195
+ if (text.length === 0) {
196
+ throw new Error(`SRT cue ${cue}, line ${i + 1}: expected cue text.`);
197
+ }
198
+ captions.push({
199
+ text: text.join(`
200
+ `).trimEnd(),
201
+ startMs,
202
+ endMs,
203
+ timestampMs: (startMs + endMs) / 2,
204
+ confidence: 1
205
+ });
206
+ }
207
+ if (captions.length === 0) {
208
+ throw new Error("SRT: No timed captions found. Provide numbered cues with timestamps and text.");
209
+ }
210
+ return { captions };
211
+ }
151
212
  for (let i = 0;i < inputLines.length; i++) {
152
213
  const line = inputLines[i];
153
214
  const nextLine = inputLines[i + 1];
@@ -1,8 +1,9 @@
1
1
  import type { Caption } from './caption';
2
2
  export type ParseSrtInput = {
3
3
  input: string;
4
+ strict?: boolean;
4
5
  };
5
6
  export type ParseSrtOutput = {
6
7
  captions: Caption[];
7
8
  };
8
- export declare const parseSrt: ({ input }: ParseSrtInput) => ParseSrtOutput;
9
+ export declare const parseSrt: ({ input, strict, }: ParseSrtInput) => ParseSrtOutput;
package/dist/parse-srt.js CHANGED
@@ -24,13 +24,77 @@ function toMilliseconds(time) {
24
24
  parseInt(seconds, 10) * 1000 +
25
25
  parseInt(millis, 10));
26
26
  }
27
- const parseSrt = ({ input }) => {
27
+ const parseSrt = ({ input, strict = false, }) => {
28
28
  const inputLines = input
29
29
  .replace(/^\uFEFF/, '')
30
30
  .replace(/\r\n/g, '\n')
31
31
  .replace(/\r/g, '\n')
32
32
  .split('\n');
33
33
  const captions = [];
34
+ if (strict) {
35
+ const timestamp = (token, cue, line) => {
36
+ const match = /^(\d+):(\d{2}):(\d{2})[,.](\d{3})$/.exec(token);
37
+ if (!match || Number(match[2]) > 59 || Number(match[3]) > 59) {
38
+ throw new Error(`SRT cue ${cue}, line ${line}: invalid timestamp "${token}". Use HH:MM:SS,mmm.`);
39
+ }
40
+ const value = Number(match[1]) * 3600000 +
41
+ Number(match[2]) * 60000 +
42
+ Number(match[3]) * 1000 +
43
+ Number(match[4]);
44
+ if (!Number.isFinite(value)) {
45
+ throw new Error(`SRT cue ${cue}, line ${line}: timestamp is too large.`);
46
+ }
47
+ return value;
48
+ };
49
+ let i = 0;
50
+ while (i < inputLines.length) {
51
+ if (inputLines[i].trim() === '') {
52
+ i++;
53
+ continue;
54
+ }
55
+ const cue = captions.length + 1;
56
+ const headerLine = i + 1;
57
+ if (!/^\d+$/.test(inputLines[i].trim())) {
58
+ throw new Error(`SRT cue ${cue}, line ${headerLine}: expected a numeric cue index.`);
59
+ }
60
+ i++;
61
+ const timingLine = inputLines[i];
62
+ const timing = timingLine === null || timingLine === void 0 ? void 0 : timingLine.match(/^\s*(\S+)\s+-->\s+(\S+)(.*)$/);
63
+ const settings = timing === null || timing === void 0 ? void 0 : timing[3].trim();
64
+ if (!timing ||
65
+ (settings &&
66
+ !settings
67
+ .split(/\s+/)
68
+ .every((setting) => /^(?:(?:X[12]|Y[12]):\d+|(?:align|position|line|size|vertical):\S+)$/.test(setting)))) {
69
+ throw new Error(`SRT cue ${cue}, line ${i + 1}: expected start --> end timestamps.`);
70
+ }
71
+ const startMs = timestamp(timing[1], cue, i + 1);
72
+ const endMs = timestamp(timing[2], cue, i + 1);
73
+ if (endMs < startMs) {
74
+ throw new Error(`SRT cue ${cue}, line ${i + 1}: end must not be earlier than start.`);
75
+ }
76
+ i++;
77
+ const text = [];
78
+ while (i < inputLines.length && inputLines[i].trim() !== '') {
79
+ text.push(inputLines[i]);
80
+ i++;
81
+ }
82
+ if (text.length === 0) {
83
+ throw new Error(`SRT cue ${cue}, line ${i + 1}: expected cue text.`);
84
+ }
85
+ captions.push({
86
+ text: text.join('\n').trimEnd(),
87
+ startMs,
88
+ endMs,
89
+ timestampMs: (startMs + endMs) / 2,
90
+ confidence: 1,
91
+ });
92
+ }
93
+ if (captions.length === 0) {
94
+ throw new Error('SRT: No timed captions found. Provide numbered cues with timestamps and text.');
95
+ }
96
+ return { captions };
97
+ }
34
98
  for (let i = 0; i < inputLines.length; i++) {
35
99
  const line = inputLines[i];
36
100
  const nextLine = inputLines[i + 1];
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/captions"
4
4
  },
5
5
  "name": "@remotion/captions",
6
- "version": "4.0.529",
6
+ "version": "4.0.531",
7
7
  "description": "Primitives for dealing with captions",
8
8
  "main": "dist/index.js",
9
9
  "types": "dist/index.d.ts",
@@ -23,7 +23,7 @@
23
23
  "dependencies": {},
24
24
  "peerDependencies": {},
25
25
  "devDependencies": {
26
- "@remotion/eslint-config-internal": "4.0.529",
26
+ "@remotion/eslint-config-internal": "4.0.531",
27
27
  "eslint": "9.19.0",
28
28
  "@typescript/native-preview": "7.0.0-dev.20260217.1"
29
29
  },