@design-edito/tools 0.5.6 → 0.5.7

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.
Files changed (79) hide show
  1. package/agnostic/colors/index.d.ts +2 -2
  2. package/agnostic/colors/index.js +2 -2
  3. package/agnostic/css/index.d.ts +1 -1
  4. package/agnostic/css/index.js +1 -1
  5. package/agnostic/errors/index.d.ts +1 -1
  6. package/agnostic/errors/index.js +1 -1
  7. package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +8 -8
  8. package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +8 -8
  9. package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +4 -4
  10. package/agnostic/html/hyper-json/smart-tags/isolated/index.js +4 -4
  11. package/agnostic/html/index.d.ts +1 -1
  12. package/agnostic/html/index.js +1 -1
  13. package/agnostic/index.d.ts +1 -0
  14. package/agnostic/index.js +1 -0
  15. package/agnostic/misc/index.d.ts +2 -2
  16. package/agnostic/misc/index.js +2 -2
  17. package/agnostic/numbers/index.d.ts +1 -1
  18. package/agnostic/numbers/index.js +1 -1
  19. package/agnostic/objects/index.d.ts +3 -3
  20. package/agnostic/objects/index.js +3 -3
  21. package/agnostic/random/index.d.ts +1 -1
  22. package/agnostic/random/index.js +1 -1
  23. package/agnostic/strings/index.d.ts +1 -1
  24. package/agnostic/strings/index.js +1 -1
  25. package/agnostic/subtitles/index.d.ts +2 -0
  26. package/agnostic/subtitles/index.js +2 -0
  27. package/agnostic/subtitles/parse-srt/index.d.ts +17 -0
  28. package/agnostic/subtitles/parse-srt/index.js +67 -0
  29. package/agnostic/subtitles/parse-srt/index.test.d.ts +1 -0
  30. package/agnostic/subtitles/parse-srt/index.test.js +94 -0
  31. package/agnostic/subtitles/parse-srt/types.d.ts +22 -0
  32. package/agnostic/subtitles/parse-srt/types.js +1 -0
  33. package/agnostic/subtitles/srt-timecode-to-ms/index.d.ts +14 -0
  34. package/agnostic/subtitles/srt-timecode-to-ms/index.js +36 -0
  35. package/agnostic/subtitles/srt-timecode-to-ms/index.test.d.ts +1 -0
  36. package/agnostic/subtitles/srt-timecode-to-ms/index.test.js +44 -0
  37. package/agnostic/time/dates/index.d.ts +1 -1
  38. package/agnostic/time/dates/index.js +1 -1
  39. package/components/ListLoader/index.controlled.d.ts +8 -3
  40. package/components/ListLoader/index.controlled.js +2 -2
  41. package/components/ListLoader/index.d.ts +1 -1
  42. package/components/ListLoader/index.js +2 -2
  43. package/components/Scrllgngn/index.d.ts +37 -7
  44. package/components/Scrllgngn/index.js +81 -24
  45. package/components/Scrllgngn/styles.module.css +30 -5
  46. package/components/Scrllgngn/utils.d.ts +87 -4
  47. package/components/Scrllgngn/utils.js +142 -8
  48. package/components/ScrollListener/index.d.ts +7 -3
  49. package/components/ScrollListener/index.js +2 -2
  50. package/components/Sequencer/index.d.ts +10 -1
  51. package/components/Sequencer/index.js +7 -3
  52. package/components/Subtitles/index.d.ts +53 -62
  53. package/components/Subtitles/index.js +90 -158
  54. package/components/Subtitles/utils.d.ts +60 -0
  55. package/components/Subtitles/utils.js +76 -0
  56. package/components/Video/index.controlled.d.ts +5 -0
  57. package/components/Video/index.controlled.js +22 -3
  58. package/components/Video/index.d.ts +47 -9
  59. package/components/Video/index.js +71 -20
  60. package/components/Video/utils.d.ts +14 -0
  61. package/components/Video/utils.js +18 -0
  62. package/components/utils/types.d.ts +32 -0
  63. package/index.d.ts +1 -1
  64. package/index.js +1 -1
  65. package/node/@aws-s3/storage/directory/index.d.ts +1 -1
  66. package/node/@aws-s3/storage/directory/index.js +1 -1
  67. package/node/@google-cloud/storage/file/index.d.ts +2 -2
  68. package/node/@google-cloud/storage/file/index.js +2 -2
  69. package/node/cloud-storage/operations/index.d.ts +1 -1
  70. package/node/cloud-storage/operations/index.js +1 -1
  71. package/node/files/index.d.ts +1 -1
  72. package/node/files/index.js +1 -1
  73. package/node/images/index.d.ts +1 -1
  74. package/node/images/index.js +1 -1
  75. package/node/index.d.ts +1 -1
  76. package/node/index.js +1 -1
  77. package/node/sftp/file/index.d.ts +1 -1
  78. package/node/sftp/file/index.js +1 -1
  79. package/package.json +26 -1
@@ -4,9 +4,9 @@ export * as convert from './convert/index.js'
4
4
  export * as distance from './distance/index.js'
5
5
  export * as grayscale from './grayscale/index.js'
6
6
  export * as invert from './invert/index.js'
7
- export * as luminance from './luminance/index.js'
8
7
  export * as lerp from './lerp/index.js'
8
+ export * as luminance from './luminance/index.js'
9
9
  export * as palette from './palette/index.js'
10
- export * as tidy from './tidy/index.js'
11
10
  export * as rotate from './rotate/index.js'
11
+ export * as tidy from './tidy/index.js'
12
12
  export * as typechecks from './typechecks/index.js'
@@ -4,9 +4,9 @@ export * as convert from './convert/index.js'
4
4
  export * as distance from './distance/index.js'
5
5
  export * as grayscale from './grayscale/index.js'
6
6
  export * as invert from './invert/index.js'
7
- export * as luminance from './luminance/index.js'
8
7
  export * as lerp from './lerp/index.js'
8
+ export * as luminance from './luminance/index.js'
9
9
  export * as palette from './palette/index.js'
10
- export * as tidy from './tidy/index.js'
11
10
  export * as rotate from './rotate/index.js'
11
+ export * as tidy from './tidy/index.js'
12
12
  export * as typechecks from './typechecks/index.js'
@@ -2,5 +2,5 @@ export * as bem from './bem/index.js'
2
2
  export * as clss from './clss/index.js'
3
3
  export * as generateNiceColor from './generate-nice-color/index.js'
4
4
  export * as isValidCssClassName from './is-valid-css-class-name/index.js'
5
- export * as scale from './scale/index.js'
6
5
  export * as stylesSet from './styles-set/index.js'
6
+ export * as scale from './scale/index.js'
@@ -2,5 +2,5 @@ export * as bem from './bem/index.js'
2
2
  export * as clss from './clss/index.js'
3
3
  export * as generateNiceColor from './generate-nice-color/index.js'
4
4
  export * as isValidCssClassName from './is-valid-css-class-name/index.js'
5
- export * as scale from './scale/index.js'
6
5
  export * as stylesSet from './styles-set/index.js'
6
+ export * as scale from './scale/index.js'
@@ -1,2 +1,2 @@
1
- export * as register from './register/index.js'
2
1
  export * as unknownToString from './unknown-to-string/index.js'
2
+ export * as register from './register/index.js'
@@ -1,2 +1,2 @@
1
- export * as register from './register/index.js'
2
1
  export * as unknownToString from './unknown-to-string/index.js'
2
+ export * as register from './register/index.js'
@@ -2,39 +2,39 @@ export * as add from './add/index.js'
2
2
  export * as addclass from './addclass/index.js'
3
3
  export * as and from './and/index.js'
4
4
  export * as append from './append/index.js'
5
- export * as clone from './clone/index.js'
6
5
  export * as at from './at/index.js'
6
+ export * as clone from './clone/index.js'
7
7
  export * as call from './call/index.js'
8
8
  export * as deleteproperties from './deleteproperties/index.js'
9
9
  export * as equals from './equals/index.js'
10
10
  export * as getattribute from './getattribute/index.js'
11
- export * as getproperties from './getproperties/index.js'
12
- export * as getproperty from './getproperty/index.js'
13
11
  export * as hjparse from './hjparse/index.js'
14
12
  export * as hjstringify from './hjstringify/index.js'
15
- export * as initialize from './initialize/index.js'
16
13
  export * as if from './if/index.js'
14
+ export * as initialize from './initialize/index.js'
15
+ export * as getproperties from './getproperties/index.js'
16
+ export * as length from './length/index.js'
17
17
  export * as join from './join/index.js'
18
18
  export * as map from './map/index.js'
19
- export * as length from './length/index.js'
20
19
  export * as negate from './negate/index.js'
21
20
  export * as notrailing from './notrailing/index.js'
22
21
  export * as pickrandom from './pickrandom/index.js'
22
+ export * as or from './or/index.js'
23
+ export * as getproperty from './getproperty/index.js'
23
24
  export * as populate from './populate/index.js'
24
25
  export * as print from './print/index.js'
25
- export * as or from './or/index.js'
26
26
  export * as pusheach from './pusheach/index.js'
27
+ export * as push from './push/index.js'
27
28
  export * as recordtoarray from './recordtoarray/index.js'
28
29
  export * as removeattribute from './removeattribute/index.js'
29
30
  export * as removeclass from './removeclass/index.js'
30
- export * as push from './push/index.js'
31
31
  export * as renameproperty from './renameproperty/index.js'
32
32
  export * as replace from './replace/index.js'
33
33
  export * as select from './select/index.js'
34
34
  export * as set from './set/index.js'
35
35
  export * as setattribute from './setattribute/index.js'
36
- export * as setproperty from './setproperty/index.js'
37
36
  export * as sorton from './sorton/index.js'
37
+ export * as setproperty from './setproperty/index.js'
38
38
  export * as split from './split/index.js'
39
39
  export * as spread from './spread/index.js'
40
40
  export * as toarray from './toarray/index.js'
@@ -2,39 +2,39 @@ export * as add from './add/index.js'
2
2
  export * as addclass from './addclass/index.js'
3
3
  export * as and from './and/index.js'
4
4
  export * as append from './append/index.js'
5
- export * as clone from './clone/index.js'
6
5
  export * as at from './at/index.js'
6
+ export * as clone from './clone/index.js'
7
7
  export * as call from './call/index.js'
8
8
  export * as deleteproperties from './deleteproperties/index.js'
9
9
  export * as equals from './equals/index.js'
10
10
  export * as getattribute from './getattribute/index.js'
11
- export * as getproperties from './getproperties/index.js'
12
- export * as getproperty from './getproperty/index.js'
13
11
  export * as hjparse from './hjparse/index.js'
14
12
  export * as hjstringify from './hjstringify/index.js'
15
- export * as initialize from './initialize/index.js'
16
13
  export * as if from './if/index.js'
14
+ export * as initialize from './initialize/index.js'
15
+ export * as getproperties from './getproperties/index.js'
16
+ export * as length from './length/index.js'
17
17
  export * as join from './join/index.js'
18
18
  export * as map from './map/index.js'
19
- export * as length from './length/index.js'
20
19
  export * as negate from './negate/index.js'
21
20
  export * as notrailing from './notrailing/index.js'
22
21
  export * as pickrandom from './pickrandom/index.js'
22
+ export * as or from './or/index.js'
23
+ export * as getproperty from './getproperty/index.js'
23
24
  export * as populate from './populate/index.js'
24
25
  export * as print from './print/index.js'
25
- export * as or from './or/index.js'
26
26
  export * as pusheach from './pusheach/index.js'
27
+ export * as push from './push/index.js'
27
28
  export * as recordtoarray from './recordtoarray/index.js'
28
29
  export * as removeattribute from './removeattribute/index.js'
29
30
  export * as removeclass from './removeclass/index.js'
30
- export * as push from './push/index.js'
31
31
  export * as renameproperty from './renameproperty/index.js'
32
32
  export * as replace from './replace/index.js'
33
33
  export * as select from './select/index.js'
34
34
  export * as set from './set/index.js'
35
35
  export * as setattribute from './setattribute/index.js'
36
- export * as setproperty from './setproperty/index.js'
37
36
  export * as sorton from './sorton/index.js'
37
+ export * as setproperty from './setproperty/index.js'
38
38
  export * as split from './split/index.js'
39
39
  export * as spread from './spread/index.js'
40
40
  export * as toarray from './toarray/index.js'
@@ -1,11 +1,11 @@
1
- export * as any from './any/index.js'
2
- export * as boolean from './boolean/index.js'
3
1
  export * as array from './array/index.js'
2
+ export * as boolean from './boolean/index.js'
3
+ export * as any from './any/index.js'
4
4
  export * as element from './element/index.js'
5
5
  export * as get from './get/index.js'
6
- export * as guess from './guess/index.js'
7
- export * as null from './null/index.js'
8
6
  export * as nodelist from './nodelist/index.js'
7
+ export * as null from './null/index.js'
8
+ export * as guess from './guess/index.js'
9
9
  export * as number from './number/index.js'
10
10
  export * as record from './record/index.js'
11
11
  export * as ref from './ref/index.js'
@@ -1,11 +1,11 @@
1
- export * as any from './any/index.js'
2
- export * as boolean from './boolean/index.js'
3
1
  export * as array from './array/index.js'
2
+ export * as boolean from './boolean/index.js'
3
+ export * as any from './any/index.js'
4
4
  export * as element from './element/index.js'
5
5
  export * as get from './get/index.js'
6
- export * as guess from './guess/index.js'
7
- export * as null from './null/index.js'
8
6
  export * as nodelist from './nodelist/index.js'
7
+ export * as null from './null/index.js'
8
+ export * as guess from './guess/index.js'
9
9
  export * as number from './number/index.js'
10
10
  export * as record from './record/index.js'
11
11
  export * as ref from './ref/index.js'
@@ -6,5 +6,5 @@ export * as insertNode from './insert-node/index.js'
6
6
  export * as placeholders from './placeholders/index.js'
7
7
  export * as replaceInElement from './replace-in-element/index.js'
8
8
  export * as selectorToElement from './selector-to-element/index.js'
9
- export * as watchSelection from './watch-selection/index.js'
10
9
  export * as stringToNodes from './string-to-nodes/index.js'
10
+ export * as watchSelection from './watch-selection/index.js'
@@ -6,5 +6,5 @@ export * as insertNode from './insert-node/index.js'
6
6
  export * as placeholders from './placeholders/index.js'
7
7
  export * as replaceInElement from './replace-in-element/index.js'
8
8
  export * as selectorToElement from './selector-to-element/index.js'
9
- export * as watchSelection from './watch-selection/index.js'
10
9
  export * as stringToNodes from './string-to-nodes/index.js'
10
+ export * as watchSelection from './watch-selection/index.js'
@@ -11,6 +11,7 @@ export * as optim from './optim/index.js'
11
11
  export * as random from './random/index.js'
12
12
  export * as regexps from './regexps/index.js'
13
13
  export * as sanitization from './sanitization/index.js'
14
+ export * as subtitles from './subtitles/index.js'
14
15
  export * as strings from './strings/index.js'
15
16
  export * as time from './time/index.js'
16
17
  export * as typescript from './typescript/index.js'
package/agnostic/index.js CHANGED
@@ -11,6 +11,7 @@ export * as optim from './optim/index.js'
11
11
  export * as random from './random/index.js'
12
12
  export * as regexps from './regexps/index.js'
13
13
  export * as sanitization from './sanitization/index.js'
14
+ export * as subtitles from './subtitles/index.js'
14
15
  export * as strings from './strings/index.js'
15
16
  export * as time from './time/index.js'
16
17
  export * as typescript from './typescript/index.js'
@@ -1,12 +1,12 @@
1
1
  export * as assert from './assert/index.js'
2
+ export * as cast from './cast/index.js'
2
3
  export * as connection from './connection/index.js'
3
4
  export * as crawler from './crawler/index.js'
4
5
  export * as crossenv from './crossenv/index.js'
5
6
  export * as dataSize from './data-size/index.js'
6
- export * as isConstructorFunction from './is-constructor-function/index.js'
7
7
  export * as isNullish from './is-nullish/index.js'
8
- export * as cast from './cast/index.js'
9
8
  export * as logs from './logs/index.js'
9
+ export * as isConstructorFunction from './is-constructor-function/index.js'
10
10
  export * as loremIpsum from './lorem-ipsum/index.js'
11
11
  export * as normalizeExtension from './normalize-extension/index.js'
12
12
  export * as outcome from './outcome/index.js'
@@ -1,12 +1,12 @@
1
1
  export * as assert from './assert/index.js'
2
+ export * as cast from './cast/index.js'
2
3
  export * as connection from './connection/index.js'
3
4
  export * as crawler from './crawler/index.js'
4
5
  export * as crossenv from './crossenv/index.js'
5
6
  export * as dataSize from './data-size/index.js'
6
- export * as isConstructorFunction from './is-constructor-function/index.js'
7
7
  export * as isNullish from './is-nullish/index.js'
8
- export * as cast from './cast/index.js'
9
8
  export * as logs from './logs/index.js'
9
+ export * as isConstructorFunction from './is-constructor-function/index.js'
10
10
  export * as loremIpsum from './lorem-ipsum/index.js'
11
11
  export * as normalizeExtension from './normalize-extension/index.js'
12
12
  export * as outcome from './outcome/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as absoluteModulo from './absolute-modulo/index.js'
2
- export * as clamp from './clamp/index.js'
3
2
  export * as approximateRational from './approximate-rational/index.js'
3
+ export * as clamp from './clamp/index.js'
4
4
  export * as geometricProgressions from './geometric-progressions/index.js'
5
5
  export * as interpolate from './interpolate/index.js'
6
6
  export * as round from './round/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as absoluteModulo from './absolute-modulo/index.js'
2
- export * as clamp from './clamp/index.js'
3
2
  export * as approximateRational from './approximate-rational/index.js'
3
+ export * as clamp from './clamp/index.js'
4
4
  export * as geometricProgressions from './geometric-progressions/index.js'
5
5
  export * as interpolate from './interpolate/index.js'
6
6
  export * as round from './round/index.js'
@@ -1,9 +1,9 @@
1
- export * as deepGetProperty from './deep-get-property/index.js'
2
1
  export * as enums from './enums/index.js'
3
- export * as flattenGetters from './flatten-getters/index.js'
4
- export * as isRecord from './is-record/index.js'
2
+ export * as deepGetProperty from './deep-get-property/index.js'
5
3
  export * as isObject from './is-object/index.js'
4
+ export * as isRecord from './is-record/index.js'
6
5
  export * as recordFormat from './record-format/index.js'
7
6
  export * as recordMap from './record-map/index.js'
7
+ export * as flattenGetters from './flatten-getters/index.js'
8
8
  export * as sortKeys from './sort-keys/index.js'
9
9
  export * as validation from './validation/index.js'
@@ -1,9 +1,9 @@
1
- export * as deepGetProperty from './deep-get-property/index.js'
2
1
  export * as enums from './enums/index.js'
3
- export * as flattenGetters from './flatten-getters/index.js'
4
- export * as isRecord from './is-record/index.js'
2
+ export * as deepGetProperty from './deep-get-property/index.js'
5
3
  export * as isObject from './is-object/index.js'
4
+ export * as isRecord from './is-record/index.js'
6
5
  export * as recordFormat from './record-format/index.js'
7
6
  export * as recordMap from './record-map/index.js'
7
+ export * as flattenGetters from './flatten-getters/index.js'
8
8
  export * as sortKeys from './sort-keys/index.js'
9
9
  export * as validation from './validation/index.js'
@@ -1,3 +1,3 @@
1
1
  export * as hexChar from './hex-char/index.js'
2
- export * as random from './random/index.js'
3
2
  export * as uuid from './uuid/index.js'
3
+ export * as random from './random/index.js'
@@ -1,3 +1,3 @@
1
1
  export * as hexChar from './hex-char/index.js'
2
- export * as random from './random/index.js'
3
2
  export * as uuid from './uuid/index.js'
3
+ export * as random from './random/index.js'
@@ -3,6 +3,6 @@ export * as matches from './matches/index.js'
3
3
  export * as normalizeIndent from './normalize-indent/index.js'
4
4
  export * as parseTable from './parse-table/index.js'
5
5
  export * as splitTrim from './split-trim/index.js'
6
+ export * as replaceAll from './replace-all/index.js'
6
7
  export * as toAlphanum from './to-alphanum/index.js'
7
8
  export * as trim from './trim/index.js'
8
- export * as replaceAll from './replace-all/index.js'
@@ -3,6 +3,6 @@ export * as matches from './matches/index.js'
3
3
  export * as normalizeIndent from './normalize-indent/index.js'
4
4
  export * as parseTable from './parse-table/index.js'
5
5
  export * as splitTrim from './split-trim/index.js'
6
+ export * as replaceAll from './replace-all/index.js'
6
7
  export * as toAlphanum from './to-alphanum/index.js'
7
8
  export * as trim from './trim/index.js'
8
- export * as replaceAll from './replace-all/index.js'
@@ -0,0 +1,2 @@
1
+ export * as parseSrt from './parse-srt/index.js'
2
+ export * as srtTimecodeToMs from './srt-timecode-to-ms/index.js'
@@ -0,0 +1,2 @@
1
+ export * as parseSrt from './parse-srt/index.js'
2
+ export * as srtTimecodeToMs from './srt-timecode-to-ms/index.js'
@@ -0,0 +1,17 @@
1
+ import type { SrtCue } from './types.js';
2
+ /**
3
+ * Parses an SRT subtitle file.
4
+ *
5
+ * Cues are delimited by blank lines, which is the format's only structural marker —
6
+ * reading line by line instead would leave the parser guessing, and a cue whose text
7
+ * is a bare number (`1914`) would read as the next cue's id.
8
+ *
9
+ * Line endings are normalised first, so a CRLF file — the format's de facto norm —
10
+ * parses like any other.
11
+ *
12
+ * @param rawSrt - The file's contents.
13
+ * @returns The cues that parsed, in file order. A block missing a usable timecode or
14
+ * any text is dropped rather than returned half-built, so the result never holds a
15
+ * cue a consumer has to check.
16
+ */
17
+ export declare function parseSrt(rawSrt: string): SrtCue[];
@@ -0,0 +1,67 @@
1
+ import { srtTimecodeToMs } from '../srt-timecode-to-ms/index.js';
2
+ /** Splits on the blank line that separates two cues, whatever the line endings were. */
3
+ const cueSeparatorRegexp = /\n[ \t]*\n/v;
4
+ const idRegexp = /^\d+$/v;
5
+ const cueArrow = '-->';
6
+ /** Reads `hh:mm:ss,mmm --> hh:mm:ss,mmm`, or nothing. */
7
+ function parseTimecodeLine(line) {
8
+ if (!line.includes(cueArrow))
9
+ return null;
10
+ const [rawStart = '', rawEnd = ''] = line.split(cueArrow);
11
+ const start = srtTimecodeToMs(rawStart);
12
+ const end = srtTimecodeToMs(rawEnd);
13
+ if (start === null || end === null)
14
+ return null;
15
+ return { start, end };
16
+ }
17
+ /** Turns one blank-line-delimited block into a cue, or drops it. */
18
+ function parseCue(block, fallbackId) {
19
+ const lines = block.split('\n');
20
+ const firstLine = lines[0];
21
+ if (firstLine === undefined)
22
+ return null;
23
+ // The id line is optional: some writers leave it out, and skipping it here is what
24
+ // lets the timecode line be found either way.
25
+ const hasIdLine = idRegexp.test(firstLine.trim());
26
+ const id = hasIdLine ? parseInt(firstLine.trim(), 10) : fallbackId;
27
+ const rest = hasIdLine ? lines.slice(1) : lines;
28
+ const timecodeLine = rest[0];
29
+ if (timecodeLine === undefined)
30
+ return null;
31
+ const timecode = parseTimecodeLine(timecodeLine);
32
+ if (timecode === null)
33
+ return null;
34
+ const content = rest.slice(1).join('\n').trim();
35
+ if (content === '')
36
+ return null;
37
+ return { id, ...timecode, content };
38
+ }
39
+ /**
40
+ * Parses an SRT subtitle file.
41
+ *
42
+ * Cues are delimited by blank lines, which is the format's only structural marker —
43
+ * reading line by line instead would leave the parser guessing, and a cue whose text
44
+ * is a bare number (`1914`) would read as the next cue's id.
45
+ *
46
+ * Line endings are normalised first, so a CRLF file — the format's de facto norm —
47
+ * parses like any other.
48
+ *
49
+ * @param rawSrt - The file's contents.
50
+ * @returns The cues that parsed, in file order. A block missing a usable timecode or
51
+ * any text is dropped rather than returned half-built, so the result never holds a
52
+ * cue a consumer has to check.
53
+ */
54
+ export function parseSrt(rawSrt) {
55
+ const cues = [];
56
+ const blocks = rawSrt
57
+ .replace(/\r\n?/gv, '\n')
58
+ .split(cueSeparatorRegexp);
59
+ for (const block of blocks) {
60
+ if (block.trim() === '')
61
+ continue;
62
+ const cue = parseCue(block, cues.length + 1);
63
+ if (cue !== null)
64
+ cues.push(cue);
65
+ }
66
+ return cues;
67
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,94 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseSrt } from './index.js';
3
+ const twoCues = `1
4
+ 00:00:01,000 --> 00:00:02,000
5
+ Bonjour
6
+
7
+ 2
8
+ 00:00:03,000 --> 00:00:04,000
9
+ Au revoir`;
10
+ describe('parseSrt', () => {
11
+ it('reads a cue whole', () => {
12
+ expect(parseSrt(twoCues)[0]).toMatchObject({
13
+ id: 1,
14
+ start: 1000,
15
+ end: 2000,
16
+ content: 'Bonjour'
17
+ });
18
+ });
19
+ it('keeps the file order', () => {
20
+ expect(parseSrt(twoCues).map(cue => cue.content)).toEqual(['Bonjour', 'Au revoir']);
21
+ });
22
+ describe('line endings', () => {
23
+ it('parses a CRLF file, the format de facto norm', () => {
24
+ expect(parseSrt(twoCues.replace(/\n/g, '\r\n'))).toHaveLength(2);
25
+ });
26
+ it('parses a lone-CR file', () => {
27
+ expect(parseSrt(twoCues.replace(/\n/g, '\r'))).toHaveLength(2);
28
+ });
29
+ });
30
+ describe('block delimiting', () => {
31
+ it('reads a bare number as text, not as the next cue id', () => {
32
+ const cues = parseSrt(`1
33
+ 00:00:01,000 --> 00:00:02,000
34
+ 1914
35
+
36
+ 2
37
+ 00:00:03,000 --> 00:00:04,000
38
+ La guerre`);
39
+ expect(cues).toHaveLength(2);
40
+ expect(cues[0]?.content).toBe('1914');
41
+ });
42
+ it('keeps the newlines inside a multi-line cue', () => {
43
+ const cues = parseSrt(`1
44
+ 00:00:01,000 --> 00:00:02,000
45
+ Première ligne
46
+ Deuxième ligne`);
47
+ expect(cues[0]?.content).toBe('Première ligne\nDeuxième ligne');
48
+ });
49
+ it('tolerates a separator line holding spaces', () => {
50
+ expect(parseSrt(twoCues.replace('\n\n', '\n \n'))).toHaveLength(2);
51
+ });
52
+ it('tolerates blank lines around the file', () => {
53
+ expect(parseSrt(`\n\n${twoCues}\n\n`)).toHaveLength(2);
54
+ });
55
+ });
56
+ describe('the id line', () => {
57
+ it('keeps the number written in the file, however it is numbered', () => {
58
+ const cues = parseSrt(`57
59
+ 00:00:01,000 --> 00:00:02,000
60
+ Bonjour`);
61
+ expect(cues[0]?.id).toBe(57);
62
+ });
63
+ it('numbers a cue by its position when the file leaves the id out', () => {
64
+ const cues = parseSrt(`00:00:01,000 --> 00:00:02,000
65
+ Bonjour
66
+
67
+ 00:00:03,000 --> 00:00:04,000
68
+ Au revoir`);
69
+ expect(cues.map(cue => cue.id)).toEqual([1, 2]);
70
+ });
71
+ });
72
+ describe('dropping what cannot be read', () => {
73
+ it('drops a block whose timecode is malformed', () => {
74
+ const cues = parseSrt(`1
75
+ 00:00:01,000 -> 00:00:02,000
76
+ Bonjour
77
+
78
+ 2
79
+ 00:00:03,000 --> 00:00:04,000
80
+ Au revoir`);
81
+ expect(cues.map(cue => cue.content)).toEqual(['Au revoir']);
82
+ });
83
+ it('drops a block with a timecode but no text', () => {
84
+ expect(parseSrt(`1
85
+ 00:00:01,000 --> 00:00:02,000`)).toEqual([]);
86
+ });
87
+ it('returns nothing at all for an empty file', () => {
88
+ expect(parseSrt('')).toEqual([]);
89
+ });
90
+ it('returns nothing for a file that is not SRT', () => {
91
+ expect(parseSrt('<!doctype html><title>404</title>')).toEqual([]);
92
+ });
93
+ });
94
+ });
@@ -0,0 +1,22 @@
1
+ /**
2
+ * One subtitle, complete.
3
+ *
4
+ * Every field is required: a cue missing its timecode or its text is not a cue the
5
+ * parser should be handing out, and a consumer shouldn't have to defend itself
6
+ * against the parser's own intermediate states.
7
+ *
8
+ * @property id - The cue number as written in the file. Kept because a consumer may
9
+ * want to show it, but it carries no meaning for the parser: a concatenated or
10
+ * hand-edited file numbers its cues however it likes. Order and identity come from
11
+ * the position in the returned array.
12
+ * @property start - Start position in milliseconds.
13
+ * @property end - End position in milliseconds.
14
+ * @property content - The cue's text, newlines preserved, surrounding whitespace
15
+ * trimmed.
16
+ */
17
+ export type SrtCue = {
18
+ id: number;
19
+ start: number;
20
+ end: number;
21
+ content: string;
22
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Converts an SRT timecode to milliseconds.
3
+ *
4
+ * @param timecode - A timecode in `hh:mm:ss,mmm` form. Surrounding whitespace is
5
+ * tolerated; anything else is not a timecode.
6
+ * @returns The position in milliseconds, or `null` when `timecode` doesn't parse —
7
+ * a caller reading a subtitle file needs to tell a malformed line from one that
8
+ * legitimately sits at zero.
9
+ *
10
+ * @example
11
+ * srtTimecodeToMs('00:00:01,500') // 1500
12
+ * srtTimecodeToMs('00:00:01,5') // 1500, not 1005
13
+ */
14
+ export declare function srtTimecodeToMs(timecode: string): number | null;
@@ -0,0 +1,36 @@
1
+ /** Matches `hh:mm:ss,mmm`, with any number of digits in each field. */
2
+ const timecodeRegexp = /^\s*(\d+):(\d+):(\d+),(\d+)\s*$/v;
3
+ /**
4
+ * Reads the fractional field as a **number of digits**, not as a value.
5
+ *
6
+ * SRT writes milliseconds as a decimal fraction of a second: `,5` is half a second,
7
+ * where reading it as an integer would call it 5ms. Padding to three digits gives
8
+ * the fraction its place value back, and truncating past them drops precision the
9
+ * format doesn't carry.
10
+ */
11
+ function fractionToMs(fraction) {
12
+ return parseInt(`${fraction}00`.slice(0, 3), 10);
13
+ }
14
+ /**
15
+ * Converts an SRT timecode to milliseconds.
16
+ *
17
+ * @param timecode - A timecode in `hh:mm:ss,mmm` form. Surrounding whitespace is
18
+ * tolerated; anything else is not a timecode.
19
+ * @returns The position in milliseconds, or `null` when `timecode` doesn't parse —
20
+ * a caller reading a subtitle file needs to tell a malformed line from one that
21
+ * legitimately sits at zero.
22
+ *
23
+ * @example
24
+ * srtTimecodeToMs('00:00:01,500') // 1500
25
+ * srtTimecodeToMs('00:00:01,5') // 1500, not 1005
26
+ */
27
+ export function srtTimecodeToMs(timecode) {
28
+ const match = timecode.match(timecodeRegexp);
29
+ if (match === null)
30
+ return null;
31
+ const [, hours = '0', minutes = '0', seconds = '0', fraction = '0'] = match;
32
+ return parseInt(hours, 10) * 60 * 60 * 1000
33
+ + parseInt(minutes, 10) * 60 * 1000
34
+ + parseInt(seconds, 10) * 1000
35
+ + fractionToMs(fraction);
36
+ }