@storyteller-platform/align 0.1.56 → 0.1.58

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 (118) hide show
  1. package/dist/align/align.cjs +38 -19
  2. package/dist/align/align.d.cts +12 -1
  3. package/dist/align/align.d.ts +12 -1
  4. package/dist/align/align.js +38 -19
  5. package/dist/align/ctc/Aligner.cjs +800 -0
  6. package/dist/align/ctc/Aligner.d.cts +56 -0
  7. package/dist/align/ctc/Aligner.d.ts +56 -0
  8. package/dist/align/ctc/Aligner.js +789 -0
  9. package/dist/align/ctc/align.cjs +189 -0
  10. package/dist/align/ctc/align.d.cts +14 -0
  11. package/dist/align/ctc/align.d.ts +14 -0
  12. package/dist/align/ctc/align.js +132 -0
  13. package/dist/{process/mime.cjs → align/ctc/emissions.cjs} +7 -20
  14. package/dist/align/ctc/emissions.d.cts +6 -0
  15. package/dist/align/ctc/emissions.d.ts +6 -0
  16. package/dist/align/ctc/emissions.js +7 -0
  17. package/dist/align/ctc/forcedAlign.cjs +273 -0
  18. package/dist/align/ctc/forcedAlign.d.cts +21 -0
  19. package/dist/align/ctc/forcedAlign.d.ts +21 -0
  20. package/dist/align/ctc/forcedAlign.js +248 -0
  21. package/dist/align/ctc/greedyDecode.cjs +61 -0
  22. package/dist/align/ctc/greedyDecode.d.cts +15 -0
  23. package/dist/align/ctc/greedyDecode.d.ts +15 -0
  24. package/dist/align/ctc/greedyDecode.js +37 -0
  25. package/dist/align/ctc/mediaOverlay.cjs +248 -0
  26. package/dist/align/ctc/mediaOverlay.d.cts +16 -0
  27. package/dist/align/ctc/mediaOverlay.d.ts +16 -0
  28. package/dist/align/ctc/mediaOverlay.js +224 -0
  29. package/dist/align/ctc/report.cjs +66 -0
  30. package/dist/align/ctc/report.d.cts +79 -0
  31. package/dist/align/ctc/report.d.ts +79 -0
  32. package/dist/align/ctc/report.js +42 -0
  33. package/dist/align/ctc/search.cjs +437 -0
  34. package/dist/align/ctc/search.d.cts +46 -0
  35. package/dist/align/ctc/search.d.ts +46 -0
  36. package/dist/align/ctc/search.js +412 -0
  37. package/dist/align/getSentenceRanges.cjs +9 -3
  38. package/dist/align/getSentenceRanges.d.cts +1 -0
  39. package/dist/align/getSentenceRanges.d.ts +1 -0
  40. package/dist/align/getSentenceRanges.js +9 -3
  41. package/dist/align/parse.cjs +17 -4
  42. package/dist/align/parse.d.cts +8 -4
  43. package/dist/align/parse.d.ts +8 -4
  44. package/dist/align/parse.js +19 -4
  45. package/dist/align/search.cjs +76 -23
  46. package/dist/align/search.d.cts +8 -4
  47. package/dist/align/search.d.ts +8 -4
  48. package/dist/align/search.js +76 -24
  49. package/dist/align/slugify.cjs +3 -2
  50. package/dist/align/slugify.d.cts +1 -1
  51. package/dist/align/slugify.d.ts +1 -1
  52. package/dist/align/slugify.js +3 -2
  53. package/dist/cli/bin.cjs +204 -114
  54. package/dist/cli/bin.js +194 -103
  55. package/dist/common/ffmpeg.cjs +10 -5
  56. package/dist/common/ffmpeg.js +10 -5
  57. package/dist/common/logging.cjs +33 -1
  58. package/dist/common/logging.js +33 -1
  59. package/dist/emit/emit.cjs +105 -0
  60. package/dist/emit/emit.d.cts +15 -0
  61. package/dist/emit/emit.d.ts +15 -0
  62. package/dist/emit/emit.js +86 -0
  63. package/dist/emit/fs.cjs +227 -0
  64. package/dist/emit/fs.d.cts +22 -0
  65. package/dist/emit/fs.d.ts +22 -0
  66. package/dist/emit/fs.js +162 -0
  67. package/dist/emit/parse.cjs +68 -0
  68. package/dist/emit/parse.d.cts +26 -0
  69. package/dist/emit/parse.d.ts +26 -0
  70. package/dist/emit/parse.js +58 -0
  71. package/dist/index.d.cts +1 -1
  72. package/dist/index.d.ts +1 -1
  73. package/dist/markup/model.cjs +0 -16
  74. package/dist/markup/model.d.cts +1 -1
  75. package/dist/markup/model.d.ts +1 -1
  76. package/dist/markup/model.js +0 -12
  77. package/dist/markup/parseDom.cjs +13 -14
  78. package/dist/markup/parseDom.d.cts +2 -2
  79. package/dist/markup/parseDom.d.ts +2 -2
  80. package/dist/markup/parseDom.js +13 -18
  81. package/dist/markup/resolvedPos.d.cts +1 -1
  82. package/dist/markup/resolvedPos.d.ts +1 -1
  83. package/dist/markup/segmentation.cjs +3 -1
  84. package/dist/markup/segmentation.js +3 -1
  85. package/dist/markup/serializeDom.cjs +1 -1
  86. package/dist/markup/serializeDom.d.cts +1 -1
  87. package/dist/markup/serializeDom.d.ts +1 -1
  88. package/dist/markup/serializeDom.js +1 -1
  89. package/dist/markup/transform.cjs +5 -2
  90. package/dist/markup/transform.d.cts +1 -1
  91. package/dist/markup/transform.d.ts +1 -1
  92. package/dist/markup/transform.js +5 -4
  93. package/dist/{model-CZ2mMHJO.d.cts → model-CeOMDDgt.d.cts} +3 -11
  94. package/dist/{model-CZ2mMHJO.d.ts → model-CeOMDDgt.d.ts} +3 -11
  95. package/dist/process/processAudiobook.cjs +12 -23
  96. package/dist/process/processAudiobook.js +14 -30
  97. package/dist/process/ranges.cjs +2 -2
  98. package/dist/process/ranges.js +2 -2
  99. package/dist/readium/guidedNavigation.cjs +8 -8
  100. package/dist/readium/guidedNavigation.js +8 -8
  101. package/dist/readium/manifest.cjs +5 -4
  102. package/dist/readium/manifest.js +5 -4
  103. package/dist/snapshot/ctc/snapshot.cjs +261 -0
  104. package/dist/snapshot/ctc/snapshot.d.cts +6 -0
  105. package/dist/snapshot/ctc/snapshot.d.ts +6 -0
  106. package/dist/snapshot/ctc/snapshot.js +200 -0
  107. package/dist/snapshot/parse.cjs +29 -16
  108. package/dist/snapshot/parse.d.cts +14 -10
  109. package/dist/snapshot/parse.d.ts +14 -10
  110. package/dist/snapshot/parse.js +32 -17
  111. package/dist/transcribe/parse.d.cts +6 -6
  112. package/dist/transcribe/parse.d.ts +6 -6
  113. package/dist/transcribe/transcribe.cjs +4 -2
  114. package/dist/transcribe/transcribe.js +4 -2
  115. package/package.json +6 -4
  116. package/dist/process/mime.d.cts +0 -3
  117. package/dist/process/mime.d.ts +0 -3
  118. package/dist/process/mime.js +0 -24
package/dist/cli/bin.js CHANGED
@@ -5,10 +5,10 @@ import {
5
5
  } from "../chunk-BIEQXUOY.js";
6
6
  import { randomUUID } from "node:crypto";
7
7
  import { rmSync } from "node:fs";
8
- import * as os from "node:os";
9
- import { basename, join } from "node:path";
8
+ import { basename } from "node:path";
10
9
  import {
11
10
  constant,
11
+ flag,
12
12
  group,
13
13
  integer,
14
14
  merge,
@@ -23,8 +23,10 @@ import { run } from "@optique/run";
23
23
  import { path } from "@optique/run/valueparser";
24
24
  import { Presets, SingleBar } from "cli-progress";
25
25
  import { Epub } from "@storyteller-platform/epub";
26
+ import { scratchPath } from "@storyteller-platform/fs";
26
27
  import packageJson from "../../package.json" with { type: "json" };
27
28
  import { align } from "../align/align.js";
29
+ import { ctcAlign } from "../align/ctc/align.js";
28
30
  import { alignCommand, alignParser } from "../align/parse.js";
29
31
  import { createLogger } from "../common/logging.js";
30
32
  import {
@@ -34,10 +36,13 @@ import {
34
36
  loggingParser,
35
37
  removeNcxParser
36
38
  } from "../common/parse.js";
39
+ import { emit } from "../emit/emit.js";
40
+ import { emitCommand, emitParser } from "../emit/parse.js";
37
41
  import { markup } from "../markup/markup.js";
38
42
  import { markupCommand } from "../markup/parse.js";
39
43
  import { processCommand, processParser } from "../process/parse.js";
40
44
  import { processAudiobook } from "../process/processAudiobook.js";
45
+ import { snapshotCtcAlignment } from "../snapshot/ctc/snapshot.js";
41
46
  import { snapshotCommand } from "../snapshot/parse.js";
42
47
  import { snapshotAlignment } from "../snapshot/snapshot.js";
43
48
  import { transcribeCommand, transcribeParser } from "../transcribe/parse.js";
@@ -50,9 +55,6 @@ const pipelineCommand = merge(
50
55
  processedAudio: optional(
51
56
  option("--processed-audio", path({ type: "directory" }))
52
57
  ),
53
- transcriptions: optional(
54
- option("--transcriptions", path({ type: "directory" }))
55
- ),
56
58
  markedup: optional(
57
59
  option("--markedup", path({ type: "file", extensions: [".epub"] }))
58
60
  ),
@@ -67,7 +69,25 @@ const pipelineCommand = merge(
67
69
  output: option("--output", path({ type: "file", extensions: [".epub"] }))
68
70
  }),
69
71
  processParser,
70
- group("Transcription", transcribeParser),
72
+ or(
73
+ merge(
74
+ object({
75
+ ctc: flag("--ctc", {
76
+ description: message`Use CTC emissions instead of transcriptions for alignment`
77
+ }),
78
+ emissions: optional(option("--emissions", path({ type: "directory" })))
79
+ }),
80
+ emitParser
81
+ ),
82
+ merge(
83
+ group("Transcription", transcribeParser),
84
+ object({
85
+ transcriptions: optional(
86
+ option("--transcriptions", path({ type: "directory" }))
87
+ )
88
+ })
89
+ )
90
+ ),
71
91
  autoUpgradeParser,
72
92
  removeNcxParser,
73
93
  granularityParser,
@@ -78,6 +98,7 @@ const pipelineCommand = merge(
78
98
  const parser = or(
79
99
  processCommand,
80
100
  transcribeCommand,
101
+ emitCommand,
81
102
  upgradeEpubCommand,
82
103
  markupCommand,
83
104
  alignCommand,
@@ -87,6 +108,13 @@ const parser = or(
87
108
  async function main() {
88
109
  var _stack2 = [];
89
110
  try {
111
+ process.on("SIGINT", () => {
112
+ try {
113
+ progressBar.stop();
114
+ } finally {
115
+ process.exit(130);
116
+ }
117
+ });
90
118
  const parsed = run(parser, {
91
119
  showChoices: true,
92
120
  showDefault: true,
@@ -110,6 +138,17 @@ async function main() {
110
138
  progressBar.start(100, 0);
111
139
  }
112
140
  }
141
+ function updateProgressBar(progress) {
142
+ if (parsed.action === "upgrade-epub") return;
143
+ if (parsed.noProgress || parsed.logLevel !== "silent") {
144
+ return;
145
+ }
146
+ const floored = Math.floor(progress * 100);
147
+ if (floored === Math.floor(progressBar.getProgress() * 100)) {
148
+ return;
149
+ }
150
+ progressBar.update(floored);
151
+ }
113
152
  resetProgressBar();
114
153
  process.on("SIGINT", () => {
115
154
  controller.abort();
@@ -131,11 +170,7 @@ async function main() {
131
170
  parallelism: parsed.parallelism,
132
171
  signal: controller.signal,
133
172
  logger,
134
- ...!parsed.noProgress && parsed.logLevel === "silent" && {
135
- onProgress: (progress) => {
136
- progressBar.update(Math.floor(progress * 100));
137
- }
138
- }
173
+ onProgress: updateProgressBar
139
174
  });
140
175
  if (parsed.time) {
141
176
  timing.print("Process audiobook");
@@ -151,11 +186,7 @@ async function main() {
151
186
  ...parsed,
152
187
  signal: controller.signal,
153
188
  logger,
154
- ...!parsed.noProgress && parsed.logLevel === "silent" && {
155
- onProgress: (progress) => {
156
- progressBar.update(Math.floor(progress * 100));
157
- }
158
- }
189
+ onProgress: updateProgressBar
159
190
  }
160
191
  );
161
192
  if (parsed.time) {
@@ -163,6 +194,13 @@ async function main() {
163
194
  }
164
195
  break;
165
196
  }
197
+ case "emit": {
198
+ await emit(parsed.input, parsed.output, {
199
+ ...parsed,
200
+ onProgress: updateProgressBar
201
+ });
202
+ break;
203
+ }
166
204
  case "upgrade-epub": {
167
205
  await upgradeEpub(parsed.input, parsed.output, {
168
206
  removeNcx: parsed.removeNcx
@@ -173,8 +211,7 @@ async function main() {
173
211
  let input = parsed.input;
174
212
  if (parsed.autoupgrade) {
175
213
  logger.info("Upgrading EPUB 2 to EPUB 3");
176
- input = join(
177
- os.tmpdir(),
214
+ input = scratchPath(
178
215
  `stalign-autoupgrade-${randomUUID()}`,
179
216
  basename(parsed.input)
180
217
  );
@@ -184,11 +221,7 @@ async function main() {
184
221
  primaryLocale: parsed.language ?? new Intl.Locale("en-US"),
185
222
  granularity: parsed.granularity,
186
223
  logger,
187
- ...!parsed.noProgress && parsed.logLevel === "silent" && {
188
- onProgress: (progress) => {
189
- progressBar.update(Math.floor(progress * 100));
190
- }
191
- }
224
+ onProgress: updateProgressBar
192
225
  });
193
226
  if (parsed.time) {
194
227
  timing.print("Mark up EPUB");
@@ -199,34 +232,50 @@ async function main() {
199
232
  let input = parsed.epub;
200
233
  if (parsed.autoupgrade) {
201
234
  logger.info("Upgrading EPUB 2 to EPUB 3");
202
- input = join(
203
- os.tmpdir(),
235
+ input = scratchPath(
204
236
  `stalign-autoupgrade-${randomUUID()}`,
205
237
  basename(parsed.epub)
206
238
  );
207
239
  await upgradeEpub(parsed.epub, input, { removeNcx: parsed.removeNcx });
208
240
  }
209
- const timing = await align(
210
- input,
211
- parsed.output,
212
- parsed.transcriptions,
213
- parsed.audiobook,
214
- {
215
- granularity: parsed.granularity,
216
- textRef: parsed.textRef,
217
- outFormat: parsed.outFormat,
218
- primaryLocale: parsed.language,
219
- reportsPath: parsed.reports,
220
- logger,
221
- ...!parsed.noProgress && parsed.logLevel === "silent" && {
222
- onProgress: (progress) => {
223
- progressBar.update(Math.floor(progress * 100));
224
- }
241
+ if ("ctc" in parsed) {
242
+ const timing = await ctcAlign(
243
+ input,
244
+ parsed.output,
245
+ parsed.emissions,
246
+ parsed.audiobook,
247
+ {
248
+ granularity: parsed.granularity,
249
+ textRef: parsed.textRef,
250
+ outFormat: parsed.outFormat,
251
+ primaryLocale: parsed.language,
252
+ reportsPath: parsed.reports,
253
+ logger,
254
+ onProgress: updateProgressBar
225
255
  }
256
+ );
257
+ if (parsed.time) {
258
+ timing.print("Align EPUB and audiobook");
259
+ }
260
+ } else {
261
+ const timing = await align(
262
+ input,
263
+ parsed.output,
264
+ parsed.transcriptions,
265
+ parsed.audiobook,
266
+ {
267
+ granularity: parsed.granularity,
268
+ textRef: parsed.textRef,
269
+ outFormat: parsed.outFormat,
270
+ primaryLocale: parsed.language,
271
+ reportsPath: parsed.reports,
272
+ logger,
273
+ onProgress: updateProgressBar
274
+ }
275
+ );
276
+ if (parsed.time) {
277
+ timing.print("Align EPUB and audiobook");
226
278
  }
227
- );
228
- if (parsed.time) {
229
- timing.print("Align EPUB and audiobook");
230
279
  }
231
280
  break;
232
281
  }
@@ -236,8 +285,7 @@ async function main() {
236
285
  let input = parsed.epub;
237
286
  if (parsed.autoupgrade) {
238
287
  logger.info("Upgrading EPUB 2 to EPUB 3");
239
- input = join(
240
- os.tmpdir(),
288
+ input = scratchPath(
241
289
  `stalign-autoupgrade-${randomUUID()}`,
242
290
  basename(parsed.epub)
243
291
  );
@@ -246,7 +294,7 @@ async function main() {
246
294
  const epub = __using(_stack, await Epub.from(input));
247
295
  const primaryLocale = parsed.language ?? await epub.getLanguage() ?? new Intl.Locale("en-US");
248
296
  epub.discardAndClose();
249
- const processedAudio = parsed.processedAudio ?? join(os.tmpdir(), `stalign-processed-${randomUUID()}`);
297
+ const processedAudio = parsed.processedAudio ?? scratchPath(`stalign-processed-${randomUUID()}`);
250
298
  if (!parsed.processedAudio) {
251
299
  stack.defer(() => {
252
300
  rmSync(processedAudio, { recursive: true, force: true });
@@ -263,11 +311,7 @@ async function main() {
263
311
  maxLength: parsed.maxLength / 60,
264
312
  parallelism: parsed.parallelTranscodes,
265
313
  signal: controller.signal,
266
- ...!parsed.noProgress && parsed.logLevel === "silent" && {
267
- onProgress: (progress) => {
268
- progressBar.update(Math.floor(progress * 100));
269
- }
270
- }
314
+ onProgress: updateProgressBar
271
315
  }
272
316
  );
273
317
  resetProgressBar();
@@ -279,36 +323,56 @@ async function main() {
279
323
  }
280
324
  logger.info("Transcribing...");
281
325
  startProgressBar();
282
- const transcriptions = parsed.transcriptions ?? join(os.tmpdir(), `stalign-transcriptions-${randomUUID()}`);
283
- if (!parsed.transcriptions) {
284
- stack.defer(() => {
285
- rmSync(transcriptions, { recursive: true, force: true });
286
- });
287
- }
288
- const transcribeTiming = await transcribe(
289
- processedAudio,
290
- transcriptions,
291
- primaryLocale,
292
- {
326
+ let emissions;
327
+ let transcriptions;
328
+ if ("ctc" in parsed) {
329
+ emissions = parsed.emissions ?? scratchPath(`stalign-emissions-${randomUUID()}`);
330
+ if (!parsed.emissions) {
331
+ stack.defer(() => {
332
+ rmSync(emissions, { recursive: true, force: true });
333
+ });
334
+ }
335
+ const transcribeTiming = await emit(processedAudio, emissions, {
293
336
  ...parsed,
294
- parallelism: parsed.parallelTranscribes,
295
337
  signal: controller.signal,
296
338
  logger,
297
- ...!parsed.noProgress && parsed.logLevel === "silent" && {
298
- onProgress: (progress) => {
299
- progressBar.update(Math.floor(progress * 100));
300
- }
339
+ onProgress: updateProgressBar
340
+ });
341
+ resetProgressBar();
342
+ logger.info(
343
+ `Generating emissions for audiobook complete, emissions saved to ${emissions}.`
344
+ );
345
+ if (parsed.time) {
346
+ transcribeTiming.print();
347
+ }
348
+ } else {
349
+ transcriptions = parsed.transcriptions ?? scratchPath(`stalign-transcriptions-${randomUUID()}`);
350
+ if (!parsed.transcriptions) {
351
+ stack.defer(() => {
352
+ rmSync(transcriptions, { recursive: true, force: true });
353
+ });
354
+ }
355
+ const transcribeTiming = await transcribe(
356
+ processedAudio,
357
+ transcriptions,
358
+ primaryLocale,
359
+ {
360
+ ...parsed,
361
+ parallelism: parsed.parallelTranscribes,
362
+ signal: controller.signal,
363
+ logger,
364
+ onProgress: updateProgressBar
301
365
  }
366
+ );
367
+ resetProgressBar();
368
+ logger.info(
369
+ `Transcribing audiobook complete, transcriptions saved to ${transcriptions}.`
370
+ );
371
+ if (parsed.time) {
372
+ transcribeTiming.print();
302
373
  }
303
- );
304
- resetProgressBar();
305
- logger.info(
306
- `Transcribing audiobook complete, transcriptions saved to ${transcriptions}.`
307
- );
308
- if (parsed.time) {
309
- transcribeTiming.print();
310
374
  }
311
- const markedup = parsed.textRef === "id-fragment" ? parsed.markedup ?? join(os.tmpdir(), `stalign-markedup-${randomUUID()}.epub`) : parsed.epub;
375
+ const markedup = parsed.textRef === "id-fragment" ? parsed.markedup ?? scratchPath(`stalign-markedup-${randomUUID()}.epub`) : parsed.epub;
312
376
  if (parsed.textRef === "id-fragment") {
313
377
  logger.info("Marking up EPUB...");
314
378
  startProgressBar();
@@ -321,11 +385,7 @@ async function main() {
321
385
  granularity: parsed.granularity,
322
386
  primaryLocale,
323
387
  logger,
324
- ...!parsed.noProgress && parsed.logLevel === "silent" && {
325
- onProgress: (progress) => {
326
- progressBar.update(Math.floor(progress * 100));
327
- }
328
- }
388
+ onProgress: updateProgressBar
329
389
  });
330
390
  resetProgressBar();
331
391
  logger.info(`Markup complete, marked up EPUB saved to ${markedup}.`);
@@ -337,29 +397,52 @@ async function main() {
337
397
  }
338
398
  logger.info("Aligning EPUB with audiobook...");
339
399
  startProgressBar();
340
- const alignTiming = await align(
341
- markedup,
342
- parsed.output,
343
- transcriptions,
344
- processedAudio,
345
- {
346
- granularity: parsed.granularity,
347
- textRef: parsed.textRef,
348
- outFormat: parsed.outFormat,
349
- primaryLocale,
350
- reportsPath: parsed.reports,
351
- logger,
352
- ...!parsed.noProgress && parsed.logLevel === "silent" && {
353
- onProgress: (progress) => {
354
- progressBar.update(Math.floor(progress * 100));
355
- }
400
+ if ("ctc" in parsed) {
401
+ const timing = await ctcAlign(
402
+ markedup,
403
+ parsed.output,
404
+ emissions,
405
+ processedAudio,
406
+ {
407
+ granularity: parsed.granularity,
408
+ textRef: parsed.textRef,
409
+ outFormat: parsed.outFormat,
410
+ primaryLocale: parsed.language,
411
+ reportsPath: parsed.reports,
412
+ logger,
413
+ onProgress: updateProgressBar
356
414
  }
415
+ );
416
+ resetProgressBar();
417
+ logger.info(
418
+ `Alignment complete, aligned EPUB saved to ${parsed.output}`
419
+ );
420
+ if (parsed.time) {
421
+ timing.print("Align EPUB and audiobook");
422
+ }
423
+ } else {
424
+ const alignTiming = await align(
425
+ markedup,
426
+ parsed.output,
427
+ transcriptions,
428
+ processedAudio,
429
+ {
430
+ granularity: parsed.granularity,
431
+ textRef: parsed.textRef,
432
+ outFormat: parsed.outFormat,
433
+ primaryLocale,
434
+ reportsPath: parsed.reports,
435
+ logger,
436
+ onProgress: updateProgressBar
437
+ }
438
+ );
439
+ resetProgressBar();
440
+ logger.info(
441
+ `Alignment complete, aligned EPUB saved to ${parsed.output}`
442
+ );
443
+ if (parsed.time) {
444
+ alignTiming.print();
357
445
  }
358
- );
359
- resetProgressBar();
360
- logger.info(`Alignment complete, aligned EPUB saved to ${parsed.output}`);
361
- if (parsed.time) {
362
- alignTiming.print();
363
446
  }
364
447
  break;
365
448
  } catch (_) {
@@ -369,7 +452,15 @@ async function main() {
369
452
  }
370
453
  }
371
454
  case "snapshot": {
372
- await snapshotAlignment(parsed.epub, parsed.transcriptions, parsed.output);
455
+ if ("ctc" in parsed) {
456
+ await snapshotCtcAlignment(parsed.epub, parsed.emissions, parsed.output);
457
+ } else {
458
+ await snapshotAlignment(
459
+ parsed.epub,
460
+ parsed.transcriptions,
461
+ parsed.output
462
+ );
463
+ }
373
464
  }
374
465
  }
375
466
  } catch (_2) {
@@ -42,7 +42,8 @@ var import_promises = require("node:fs/promises");
42
42
  var import_node_path = require("node:path");
43
43
  var import_node_util = require("node:util");
44
44
  var import_memoize = __toESM(require("memoize"), 1);
45
- var import_mime = require("../process/mime.cjs");
45
+ var import_audiobook = require("@storyteller-platform/audiobook");
46
+ var import_media_types = require("@storyteller-platform/media-types");
46
47
  var import_shell = require("./shell.cjs");
47
48
  const execPromise = (0, import_node_util.promisify)(import_node_child_process.exec);
48
49
  async function execCmd(command, logger, signal) {
@@ -190,12 +191,15 @@ function commonFfmpegArguments(options) {
190
191
  const args = ["-vn"];
191
192
  if (codec) {
192
193
  args.push("-c:a", codec);
193
- if (codec === "libopus") {
194
+ if (codec === import_audiobook.AUDIO_ENCODERS.opus) {
194
195
  args.push("-b:a", bitrate && /^\d+[kK]$/i.test(bitrate) ? bitrate : "32K");
195
- } else if (codec === "libmp3lame" && bitrate) {
196
+ } else if (codec === import_audiobook.AUDIO_ENCODERS.mp3 && bitrate) {
196
197
  args.push("-b:a", bitrate);
197
198
  }
198
- } else if ((0, import_mime.areSameType)(sourceExtension, destExtension) || destExtension == ".mp4") {
199
+ } else if ((0, import_audiobook.canCopyCodec)(
200
+ import_media_types.MediaType.fromExtension(sourceExtension),
201
+ import_media_types.MediaType.fromExtension(destExtension)
202
+ )) {
199
203
  args.push("-c:a", "copy");
200
204
  }
201
205
  args.push("-map", "0:a");
@@ -238,7 +242,8 @@ async function splitFile(input, output, start, end, encoding, signal, logger) {
238
242
  return true;
239
243
  }
240
244
  async function transcodeFile(input, output, encoding, signal, logger) {
241
- if (!encoding?.codec && (0, import_mime.areSameType)(input, output)) {
245
+ const container = import_media_types.MediaType.fromPath(input);
246
+ if (!encoding?.codec && container !== null && container === import_media_types.MediaType.fromPath(output)) {
242
247
  logger?.info(
243
248
  `Input and output container and codec are the same, copying ${input} to output directory`
244
249
  );
@@ -4,7 +4,8 @@ import { copyFile } from "node:fs/promises";
4
4
  import { extname } from "node:path";
5
5
  import { promisify } from "node:util";
6
6
  import memoize from "memoize";
7
- import { areSameType } from "../process/mime.js";
7
+ import { AUDIO_ENCODERS, canCopyCodec } from "@storyteller-platform/audiobook";
8
+ import { MediaType } from "@storyteller-platform/media-types";
8
9
  import { quotePath } from "./shell.js";
9
10
  const execPromise = promisify(exec);
10
11
  async function execCmd(command, logger, signal) {
@@ -152,12 +153,15 @@ function commonFfmpegArguments(options) {
152
153
  const args = ["-vn"];
153
154
  if (codec) {
154
155
  args.push("-c:a", codec);
155
- if (codec === "libopus") {
156
+ if (codec === AUDIO_ENCODERS.opus) {
156
157
  args.push("-b:a", bitrate && /^\d+[kK]$/i.test(bitrate) ? bitrate : "32K");
157
- } else if (codec === "libmp3lame" && bitrate) {
158
+ } else if (codec === AUDIO_ENCODERS.mp3 && bitrate) {
158
159
  args.push("-b:a", bitrate);
159
160
  }
160
- } else if (areSameType(sourceExtension, destExtension) || destExtension == ".mp4") {
161
+ } else if (canCopyCodec(
162
+ MediaType.fromExtension(sourceExtension),
163
+ MediaType.fromExtension(destExtension)
164
+ )) {
161
165
  args.push("-c:a", "copy");
162
166
  }
163
167
  args.push("-map", "0:a");
@@ -200,7 +204,8 @@ async function splitFile(input, output, start, end, encoding, signal, logger) {
200
204
  return true;
201
205
  }
202
206
  async function transcodeFile(input, output, encoding, signal, logger) {
203
- if (!encoding?.codec && areSameType(input, output)) {
207
+ const container = MediaType.fromPath(input);
208
+ if (!encoding?.codec && container !== null && container === MediaType.fromPath(output)) {
204
209
  logger?.info(
205
210
  `Input and output container and codec are the same, copying ${input} to output directory`
206
211
  );
@@ -31,12 +31,44 @@ __export(logging_exports, {
31
31
  createLogger: () => createLogger
32
32
  });
33
33
  module.exports = __toCommonJS(logging_exports);
34
+ var import_node_fs = require("node:fs");
35
+ var import_node_path = require("node:path");
34
36
  var import_pino = __toESM(require("pino"), 1);
35
37
  var import_pino_pretty = __toESM(require("pino-pretty"), 1);
38
+ function todayDateString() {
39
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
40
+ }
41
+ function getLogDir() {
42
+ const dataDir = process.env["STORYTELLER_DATA_DIR"];
43
+ if (!dataDir) return null;
44
+ return (0, import_node_path.join)(dataDir, "logs");
45
+ }
36
46
  function createLogger(level = "info") {
47
+ const prettyStream = (0, import_pino_pretty.default)({
48
+ ignore: "pid,hostname",
49
+ translateTime: "SYS:standard"
50
+ });
51
+ const logDir = getLogDir();
52
+ if (!logDir) {
53
+ return (0, import_pino.default)({ level }, prettyStream);
54
+ }
55
+ try {
56
+ (0, import_node_fs.mkdirSync)(logDir, { recursive: true });
57
+ } catch {
58
+ return (0, import_pino.default)({ level }, prettyStream);
59
+ }
60
+ const filePath = (0, import_node_path.join)(logDir, `storyteller-${todayDateString()}.log`);
61
+ const fileStream = import_pino.default.destination({
62
+ dest: filePath,
63
+ mkdir: true,
64
+ sync: false
65
+ });
37
66
  return (0, import_pino.default)(
38
67
  { level },
39
- (0, import_pino_pretty.default)({ ignore: "pid,hostname", translateTime: "SYS:standard" })
68
+ import_pino.default.multistream([
69
+ { stream: prettyStream, level },
70
+ { stream: fileStream, level }
71
+ ])
40
72
  );
41
73
  }
42
74
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,10 +1,42 @@
1
1
  import "../chunk-BIEQXUOY.js";
2
+ import { mkdirSync } from "node:fs";
3
+ import { join } from "node:path";
2
4
  import pino from "pino";
3
5
  import PinoPretty from "pino-pretty";
6
+ function todayDateString() {
7
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
8
+ }
9
+ function getLogDir() {
10
+ const dataDir = process.env["STORYTELLER_DATA_DIR"];
11
+ if (!dataDir) return null;
12
+ return join(dataDir, "logs");
13
+ }
4
14
  function createLogger(level = "info") {
15
+ const prettyStream = PinoPretty({
16
+ ignore: "pid,hostname",
17
+ translateTime: "SYS:standard"
18
+ });
19
+ const logDir = getLogDir();
20
+ if (!logDir) {
21
+ return pino({ level }, prettyStream);
22
+ }
23
+ try {
24
+ mkdirSync(logDir, { recursive: true });
25
+ } catch {
26
+ return pino({ level }, prettyStream);
27
+ }
28
+ const filePath = join(logDir, `storyteller-${todayDateString()}.log`);
29
+ const fileStream = pino.destination({
30
+ dest: filePath,
31
+ mkdir: true,
32
+ sync: false
33
+ });
5
34
  return pino(
6
35
  { level },
7
- PinoPretty({ ignore: "pid,hostname", translateTime: "SYS:standard" })
36
+ pino.multistream([
37
+ { stream: prettyStream, level },
38
+ { stream: fileStream, level }
39
+ ])
8
40
  );
9
41
  }
10
42
  export {