@nxtedition/lib 22.0.12 → 22.0.13

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 (2) hide show
  1. package/ass.js +90 -26
  2. package/package.json +1 -1
package/ass.js CHANGED
@@ -1,16 +1,68 @@
1
+ // @ts-check
1
2
  import fp from 'lodash/fp.js'
2
3
 
3
4
  const BASE_WIDTH = 1920
4
5
  const BASE_HEIGHT = 1080
5
6
 
6
- export function encodeASS(events, { styles = {}, width = BASE_WIDTH, height = BASE_HEIGHT } = {}) {
7
+ /**
8
+ * @typedef {object} SubtitleEvent
9
+ * @property {number} start
10
+ * @property {number | null} [duration]
11
+ * @property {number | null} [end]
12
+ * @property {string | null} [style]
13
+ * @property {string | null} [text]
14
+ *
15
+ * @typedef {object} Styles
16
+ * @property {string} [name]
17
+ * @property {string} [fontname]
18
+ * @property {string} [fontsize]
19
+ * @property {string} [primaryColour]
20
+ * @property {string} [secondaryColour]
21
+ * @property {string} [outlineColour]
22
+ * @property {string} [backColour]
23
+ * @property {string} [bold]
24
+ * @property {string} [italic]
25
+ * @property {string} [underline]
26
+ * @property {string} [strikeOut]
27
+ * @property {string} [scaleX]
28
+ * @property {string} [scaleY]
29
+ * @property {string} [spacing]
30
+ * @property {string} [angle]
31
+ * @property {string} [borderStyle]
32
+ * @property {string} [outline]
33
+ * @property {string} [shadow]
34
+ * @property {string} [alignment]
35
+ * @property {string} [marginL]
36
+ * @property {string} [marginR]
37
+ * @property {string} [marginV]
38
+ * @property {string} [encoding]
39
+ *
40
+ * @typedef {object} Options
41
+ * @property {Styles} [styles]
42
+ * @property {number} [width]
43
+ * @property {number} [height]
44
+ * @property {boolean} [scaledBorderAndShadow] TODO: default to true when all client styles depend on it?
45
+ */
46
+
47
+ /**
48
+ * @param {SubtitleEvent[]} events
49
+ * @param {Options} [options]
50
+ * @returns {string}
51
+ */
52
+ export function encodeASS(
53
+ events,
54
+ { styles = {}, width = BASE_WIDTH, height = BASE_HEIGHT, scaledBorderAndShadow = false } = {},
55
+ ) {
7
56
  return [
8
- encASSHeader({ width, height }),
9
- encASSStyles({ styles, width, height }),
57
+ encASSHeader({ width, height, scaledBorderAndShadow }),
58
+ encASSStyles(styles),
10
59
  encASSEvents(events),
11
60
  ].join('\n')
12
61
  }
13
62
 
63
+ /**
64
+ * @param {SubtitleEvent[]} events
65
+ */
14
66
  function formatDialogues(events) {
15
67
  let s = ''
16
68
  for (const { start, duration, end = duration != null ? start + duration : null, text, style } of [
@@ -25,12 +77,18 @@ function formatDialogues(events) {
25
77
  return s
26
78
  }
27
79
 
80
+ /**
81
+ * @param {SubtitleEvent[]} events
82
+ */
28
83
  function encASSEvents(events) {
29
84
  return `[Events]
30
85
  Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
31
86
  ${formatDialogues(events)}`
32
87
  }
33
88
 
89
+ /**
90
+ * @param {Styles} styles
91
+ */
34
92
  const formatStyles = fp.pipe(
35
93
  fp.entries,
36
94
  fp.reduce((s, [id, style]) => {
@@ -87,40 +145,46 @@ const formatStyles = fp.pipe(
87
145
  }, ''),
88
146
  )
89
147
 
90
- function encASSStyles({ styles, width, height }) {
91
- const scaledStyles = {}
92
-
93
- for (const [id, style] of Object.entries(styles)) {
94
- scaledStyles[id] = { ...style }
95
- for (const key of ['fontsize', 'marginV', 'marginL', 'marginR']) {
96
- const scale = key === 'fontsize' ? height / BASE_HEIGHT : width / BASE_WIDTH
97
-
98
- if (typeof style[key] === 'string') {
99
- const scaled = Number(style[key]) * scale
100
- scaledStyles[id][key] = String(Math.round(scaled * 10) / 10)
101
- }
102
- }
103
- }
104
-
148
+ /**
149
+ * @param {Styles} styles
150
+ * @returns {string}
151
+ */
152
+ function encASSStyles(styles) {
105
153
  return `[V4+ Styles]
106
154
  Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
107
- ${formatStyles(scaledStyles)}`
155
+ ${formatStyles(styles)}`
108
156
  }
109
157
 
110
- function encASSHeader({ width, height }) {
158
+ /**
159
+ * @param {object} params
160
+ * @param {number} [params.width]
161
+ * @param {number} [params.height]
162
+ * @param {boolean} [params.scaledBorderAndShadow]
163
+ */
164
+ function encASSHeader({ width, height, scaledBorderAndShadow }) {
165
+ const scale = typeof width === 'number' ? BASE_WIDTH / width : 1
111
166
  // WrapStyle
112
167
  // 0: smart wrapping, lines are evenly broken
113
168
  // 1: end-of-line word wrapping, only \N breaks
114
169
  // 2: no word wrapping, \n \N both breaks
115
170
  // 3: same as 0, but lower line gets wider.
116
- return `[Script Info]
117
- ScriptType: v4.00+
118
- PlayResX: ${width || BASE_WIDTH}
119
- PlayResY: ${height || BASE_HEIGHT}
120
- WrapStyle: 1
121
- `
171
+ const header = [
172
+ '[Script Info]',
173
+ 'ScriptType: v4.00+',
174
+ `PlayResX: ${scale * (width || BASE_WIDTH)}`,
175
+ `PlayResY: ${scale * (height || BASE_HEIGHT)}`,
176
+ 'WrapStyle: 1',
177
+ ]
178
+ if (scaledBorderAndShadow) {
179
+ header.push('ScaledBorderAndShadow: Yes')
180
+ }
181
+ return `${header.join('\n')}\n`
122
182
  }
123
183
 
184
+ /**
185
+ * @param {number | null | undefined} seconds
186
+ * @returns {string}
187
+ */
124
188
  function formatASSTime(seconds) {
125
189
  if (seconds == null) {
126
190
  return ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "22.0.12",
3
+ "version": "22.0.13",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",