@bendyline/squisq-cli 2.3.0 → 2.3.2
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.
- package/README.md +7 -5
- package/THIRD_PARTY_LICENSES.txt +4471 -0
- package/dist/{api-5SX73ZKT.js → api-A7FKMZY6.js} +1 -1
- package/dist/api.d.ts +6 -6
- package/dist/api.js +17 -8
- package/dist/{chunk-GYHQHKO5.js → chunk-ZEN42AFN.js} +19 -10
- package/dist/index.js +36 -21
- package/dist/squisq-player.global.js +575 -0
- package/package.json +7 -7
package/dist/api.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ interface Mp4FormatOptions {
|
|
|
23
23
|
orientation?: VideoOrientation;
|
|
24
24
|
width?: number;
|
|
25
25
|
height?: number;
|
|
26
|
-
captionStyle?: 'standard' | 'social';
|
|
26
|
+
captionStyle?: 'off' | 'standard' | 'social';
|
|
27
27
|
coverPreRoll?: number;
|
|
28
28
|
animationsEnabled?: boolean;
|
|
29
29
|
}
|
|
@@ -34,7 +34,7 @@ interface GifFormatOptions {
|
|
|
34
34
|
orientation?: VideoOrientation;
|
|
35
35
|
width?: number;
|
|
36
36
|
height?: number;
|
|
37
|
-
captionStyle?: 'standard' | 'social';
|
|
37
|
+
captionStyle?: 'off' | 'standard' | 'social';
|
|
38
38
|
coverPreRoll?: number;
|
|
39
39
|
animationsEnabled?: boolean;
|
|
40
40
|
loop?: number;
|
|
@@ -258,8 +258,8 @@ interface RenderDocToMp4Options {
|
|
|
258
258
|
width?: number;
|
|
259
259
|
/** Override video height in pixels. */
|
|
260
260
|
height?: number;
|
|
261
|
-
/** Caption
|
|
262
|
-
captionStyle?: 'standard' | 'social';
|
|
261
|
+
/** Caption mode to bake into the video (default: off). */
|
|
262
|
+
captionStyle?: 'off' | 'standard' | 'social';
|
|
263
263
|
/** Render layer animations and block transitions (default: true). */
|
|
264
264
|
animationsEnabled?: boolean;
|
|
265
265
|
/**
|
|
@@ -289,8 +289,8 @@ interface RenderDocToGifOptions {
|
|
|
289
289
|
width?: number;
|
|
290
290
|
/** Override output height (default: 540 landscape / 960 portrait). */
|
|
291
291
|
height?: number;
|
|
292
|
-
/** Caption
|
|
293
|
-
captionStyle?: 'standard' | 'social';
|
|
292
|
+
/** Caption mode to bake into the GIF (default: standard). */
|
|
293
|
+
captionStyle?: 'off' | 'standard' | 'social';
|
|
294
294
|
/** Seconds of cover-slide pre-roll (default: 0). */
|
|
295
295
|
coverPreRoll?: number;
|
|
296
296
|
/** Render layer animations and block transitions (default: false). */
|
package/dist/api.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-Z3BHD4AF.js";
|
|
9
9
|
|
|
10
10
|
// src/api.ts
|
|
11
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
11
12
|
import { resolveMediaSchedule } from "@bendyline/squisq/schemas";
|
|
12
13
|
import { flattenBlocks } from "@bendyline/squisq/doc";
|
|
13
14
|
import { ffmpegGifOutputArgs, generateRenderHtml } from "@bendyline/squisq-video";
|
|
@@ -96,7 +97,7 @@ async function buildMixedAudioTrack(doc, container, ffmpegPath, coverPreRoll, si
|
|
|
96
97
|
return mixTimelineClips(ffmpegPath, usable, signal);
|
|
97
98
|
}
|
|
98
99
|
async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
99
|
-
const { writeFile, readFile:
|
|
100
|
+
const { writeFile, readFile: readFile4, mkdir, rm: rm2 } = await import("fs/promises");
|
|
100
101
|
const { join: join3 } = await import("path");
|
|
101
102
|
const { tmpdir: tmpdir2 } = await import("os");
|
|
102
103
|
const { randomBytes: randomBytes2 } = await import("crypto");
|
|
@@ -144,7 +145,7 @@ async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
|
144
145
|
signal
|
|
145
146
|
});
|
|
146
147
|
signal?.throwIfAborted();
|
|
147
|
-
const data = await
|
|
148
|
+
const data = await readFile4(outputPath);
|
|
148
149
|
signal?.throwIfAborted();
|
|
149
150
|
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
150
151
|
} finally {
|
|
@@ -663,17 +664,25 @@ function throwIfAborted(signal) {
|
|
|
663
664
|
|
|
664
665
|
// src/api.ts
|
|
665
666
|
import { ConversionError } from "@bendyline/squisq-formats";
|
|
667
|
+
var playerBundlePromise;
|
|
668
|
+
function loadPlayerBundle() {
|
|
669
|
+
playerBundlePromise ??= readFile3(
|
|
670
|
+
new URL("../dist/squisq-player.global.js", import.meta.url),
|
|
671
|
+
"utf8"
|
|
672
|
+
);
|
|
673
|
+
return playerBundlePromise;
|
|
674
|
+
}
|
|
666
675
|
async function convert(source, to, options = {}) {
|
|
667
676
|
return formatsConvert(source, to, {
|
|
668
677
|
registry: createCliRegistry(),
|
|
669
|
-
resolvePlayerScript:
|
|
678
|
+
resolvePlayerScript: loadPlayerBundle,
|
|
670
679
|
...options
|
|
671
680
|
});
|
|
672
681
|
}
|
|
673
682
|
async function prepareConversion(source, options = {}) {
|
|
674
683
|
return formatsPrepareConversion(source, {
|
|
675
684
|
registry: createCliRegistry(),
|
|
676
|
-
resolvePlayerScript:
|
|
685
|
+
resolvePlayerScript: loadPlayerBundle,
|
|
677
686
|
...options
|
|
678
687
|
});
|
|
679
688
|
}
|
|
@@ -732,10 +741,10 @@ async function captureDocFrames(doc, container, options) {
|
|
|
732
741
|
}
|
|
733
742
|
onProgress?.("generating render HTML", 10);
|
|
734
743
|
signal?.throwIfAborted();
|
|
735
|
-
const
|
|
744
|
+
const playerBundle = await loadPlayerBundle();
|
|
736
745
|
signal?.throwIfAborted();
|
|
737
746
|
const renderHtml = generateRenderHtml(doc, {
|
|
738
|
-
playerScript:
|
|
747
|
+
playerScript: playerBundle,
|
|
739
748
|
images,
|
|
740
749
|
audio: audio.size > 0 ? audio : void 0,
|
|
741
750
|
width,
|
|
@@ -876,7 +885,7 @@ async function renderDocToMp4(doc, container, options) {
|
|
|
876
885
|
fps,
|
|
877
886
|
width: dimensions.width,
|
|
878
887
|
height: dimensions.height,
|
|
879
|
-
captionStyle: options.captionStyle,
|
|
888
|
+
captionStyle: options.captionStyle === "off" ? void 0 : options.captionStyle,
|
|
880
889
|
coverPreRoll: options.coverPreRoll ?? 0,
|
|
881
890
|
animationsEnabled: options.animationsEnabled ?? true,
|
|
882
891
|
onProgress: options.onProgress,
|
|
@@ -939,7 +948,7 @@ async function renderDocToGif(doc, container, options) {
|
|
|
939
948
|
fps,
|
|
940
949
|
width,
|
|
941
950
|
height,
|
|
942
|
-
captionStyle: options.captionStyle,
|
|
951
|
+
captionStyle: options.captionStyle === "off" ? void 0 : options.captionStyle ?? "standard",
|
|
943
952
|
coverPreRoll: options.coverPreRoll ?? 0,
|
|
944
953
|
animationsEnabled: options.animationsEnabled ?? false,
|
|
945
954
|
onProgress: options.onProgress,
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-GGVASRPG.js";
|
|
6
6
|
|
|
7
7
|
// src/api.ts
|
|
8
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
8
9
|
import { resolveMediaSchedule } from "@bendyline/squisq/schemas";
|
|
9
10
|
import { flattenBlocks } from "@bendyline/squisq/doc";
|
|
10
11
|
import { ffmpegGifOutputArgs, generateRenderHtml } from "@bendyline/squisq-video";
|
|
@@ -93,7 +94,7 @@ async function buildMixedAudioTrack(doc, container, ffmpegPath, coverPreRoll, si
|
|
|
93
94
|
return mixTimelineClips(ffmpegPath, usable, signal);
|
|
94
95
|
}
|
|
95
96
|
async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
96
|
-
const { writeFile, readFile:
|
|
97
|
+
const { writeFile, readFile: readFile4, mkdir, rm: rm2 } = await import("fs/promises");
|
|
97
98
|
const { join: join3 } = await import("path");
|
|
98
99
|
const { tmpdir: tmpdir2 } = await import("os");
|
|
99
100
|
const { randomBytes: randomBytes2 } = await import("crypto");
|
|
@@ -141,7 +142,7 @@ async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
|
141
142
|
signal
|
|
142
143
|
});
|
|
143
144
|
signal?.throwIfAborted();
|
|
144
|
-
const data = await
|
|
145
|
+
const data = await readFile4(outputPath);
|
|
145
146
|
signal?.throwIfAborted();
|
|
146
147
|
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
147
148
|
} finally {
|
|
@@ -307,7 +308,7 @@ function mp4Format() {
|
|
|
307
308
|
const animationsEnabled = typeof mp4Opts.animationsEnabled === "boolean" ? mp4Opts.animationsEnabled : MP4_DEFAULTS.animationsEnabled;
|
|
308
309
|
const outputPath = join(tmpdir(), `squisq-mp4-${randomBytes(8).toString("hex")}.mp4`);
|
|
309
310
|
try {
|
|
310
|
-
const { renderDocToMp4: renderDocToMp42 } = await import("./api-
|
|
311
|
+
const { renderDocToMp4: renderDocToMp42 } = await import("./api-A7FKMZY6.js");
|
|
311
312
|
await renderDocToMp42(input.doc, input.container, {
|
|
312
313
|
outputPath,
|
|
313
314
|
fps,
|
|
@@ -347,7 +348,7 @@ function gifFormat() {
|
|
|
347
348
|
const defaultHeight = portrait ? GIF_DEFAULTS.width : GIF_DEFAULTS.height;
|
|
348
349
|
const outputPath = join(tmpdir(), `squisq-gif-${randomBytes(8).toString("hex")}.gif`);
|
|
349
350
|
try {
|
|
350
|
-
const { renderDocToGif: renderDocToGif2 } = await import("./api-
|
|
351
|
+
const { renderDocToGif: renderDocToGif2 } = await import("./api-A7FKMZY6.js");
|
|
351
352
|
const result = await renderDocToGif2(input.doc, input.container, {
|
|
352
353
|
outputPath,
|
|
353
354
|
fps: typeof gifOpts.fps === "number" ? gifOpts.fps : GIF_DEFAULTS.fps,
|
|
@@ -660,17 +661,25 @@ function throwIfAborted(signal) {
|
|
|
660
661
|
|
|
661
662
|
// src/api.ts
|
|
662
663
|
import { ConversionError } from "@bendyline/squisq-formats";
|
|
664
|
+
var playerBundlePromise;
|
|
665
|
+
function loadPlayerBundle() {
|
|
666
|
+
playerBundlePromise ??= readFile3(
|
|
667
|
+
new URL("../dist/squisq-player.global.js", import.meta.url),
|
|
668
|
+
"utf8"
|
|
669
|
+
);
|
|
670
|
+
return playerBundlePromise;
|
|
671
|
+
}
|
|
663
672
|
async function convert(source, to, options = {}) {
|
|
664
673
|
return formatsConvert(source, to, {
|
|
665
674
|
registry: createCliRegistry(),
|
|
666
|
-
resolvePlayerScript:
|
|
675
|
+
resolvePlayerScript: loadPlayerBundle,
|
|
667
676
|
...options
|
|
668
677
|
});
|
|
669
678
|
}
|
|
670
679
|
async function prepareConversion(source, options = {}) {
|
|
671
680
|
return formatsPrepareConversion(source, {
|
|
672
681
|
registry: createCliRegistry(),
|
|
673
|
-
resolvePlayerScript:
|
|
682
|
+
resolvePlayerScript: loadPlayerBundle,
|
|
674
683
|
...options
|
|
675
684
|
});
|
|
676
685
|
}
|
|
@@ -729,10 +738,10 @@ async function captureDocFrames(doc, container, options) {
|
|
|
729
738
|
}
|
|
730
739
|
onProgress?.("generating render HTML", 10);
|
|
731
740
|
signal?.throwIfAborted();
|
|
732
|
-
const
|
|
741
|
+
const playerBundle = await loadPlayerBundle();
|
|
733
742
|
signal?.throwIfAborted();
|
|
734
743
|
const renderHtml = generateRenderHtml(doc, {
|
|
735
|
-
playerScript:
|
|
744
|
+
playerScript: playerBundle,
|
|
736
745
|
images,
|
|
737
746
|
audio: audio.size > 0 ? audio : void 0,
|
|
738
747
|
width,
|
|
@@ -873,7 +882,7 @@ async function renderDocToMp4(doc, container, options) {
|
|
|
873
882
|
fps,
|
|
874
883
|
width: dimensions.width,
|
|
875
884
|
height: dimensions.height,
|
|
876
|
-
captionStyle: options.captionStyle,
|
|
885
|
+
captionStyle: options.captionStyle === "off" ? void 0 : options.captionStyle,
|
|
877
886
|
coverPreRoll: options.coverPreRoll ?? 0,
|
|
878
887
|
animationsEnabled: options.animationsEnabled ?? true,
|
|
879
888
|
onProgress: options.onProgress,
|
|
@@ -936,7 +945,7 @@ async function renderDocToGif(doc, container, options) {
|
|
|
936
945
|
fps,
|
|
937
946
|
width,
|
|
938
947
|
height,
|
|
939
|
-
captionStyle: options.captionStyle,
|
|
948
|
+
captionStyle: options.captionStyle === "off" ? void 0 : options.captionStyle ?? "standard",
|
|
940
949
|
coverPreRoll: options.coverPreRoll ?? 0,
|
|
941
950
|
animationsEnabled: options.animationsEnabled ?? false,
|
|
942
951
|
onProgress: options.onProgress,
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
readInput,
|
|
8
8
|
renderDocToGif,
|
|
9
9
|
renderDocToMp4
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ZEN42AFN.js";
|
|
11
11
|
import "./chunk-GGVASRPG.js";
|
|
12
12
|
|
|
13
13
|
// src/index.ts
|
|
@@ -17,6 +17,7 @@ import { Command } from "commander";
|
|
|
17
17
|
// src/commands/convert.ts
|
|
18
18
|
import { mkdir } from "fs/promises";
|
|
19
19
|
import { dirname, basename, extname, join, resolve } from "path";
|
|
20
|
+
import { Option } from "commander";
|
|
20
21
|
import { BUILTIN_FORMAT_IDS, ConversionError } from "@bendyline/squisq-formats";
|
|
21
22
|
|
|
22
23
|
// src/util/applyTransformPipeline.ts
|
|
@@ -163,6 +164,12 @@ function parseFormats(value) {
|
|
|
163
164
|
`Unknown format${unknown.length > 1 ? "s" : ""}: ${hints}. Valid: ${VALID_FORMATS.join(", ")}`
|
|
164
165
|
);
|
|
165
166
|
}
|
|
167
|
+
const duplicates = [...new Set(requested.filter((id, index) => requested.indexOf(id) !== index))];
|
|
168
|
+
if (duplicates.length > 0) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`Duplicate format${duplicates.length > 1 ? "s" : ""}: ${duplicates.map((id) => `"${id}"`).join(", ")}. Each format may only be requested once.`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
166
173
|
return requested;
|
|
167
174
|
}
|
|
168
175
|
function formatFromOutputPath(outputPath) {
|
|
@@ -180,12 +187,21 @@ function formatFromOutputPath(outputPath) {
|
|
|
180
187
|
function registerConvertCommand(program2) {
|
|
181
188
|
program2.command("convert").description(
|
|
182
189
|
"Convert a document to DOCX, PPTX, PDF, HTML, EPUB, MP4, animated GIF, and container formats"
|
|
183
|
-
).argument("<input>", "Path to .md/.docx/.pptx/.pdf/.xlsx/.csv/.html file, .zip/.dbk, or folder").
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"
|
|
188
|
-
|
|
190
|
+
).argument("<input>", "Path to .md/.docx/.pptx/.pdf/.xlsx/.csv/.html file, .zip/.dbk, or folder").addOption(
|
|
191
|
+
new Option(
|
|
192
|
+
"-o, --output <file>",
|
|
193
|
+
"Single output file (format inferred from its extension)"
|
|
194
|
+
).conflicts(["outputDir", "formats"])
|
|
195
|
+
).addOption(
|
|
196
|
+
new Option(
|
|
197
|
+
"-d, --output-dir <dir>",
|
|
198
|
+
"Output directory for multi-format export (default: same as input)"
|
|
199
|
+
).conflicts("output")
|
|
200
|
+
).addOption(
|
|
201
|
+
new Option(
|
|
202
|
+
"-f, --formats <list>",
|
|
203
|
+
`Comma-separated formats to produce (default: a standard set). Valid: ${VALID_FORMATS.join(", ")}`
|
|
204
|
+
).conflicts("output")
|
|
189
205
|
).option("--overwrite", "Replace existing output files (default: refuse and exit non-zero)").option("-t, --theme <id>", "Squisq theme ID to apply (e.g., documentary, cinematic, bold)").option(
|
|
190
206
|
"--transform <style>",
|
|
191
207
|
"Transform style to apply before export (e.g., documentary, magazine, minimal)"
|
|
@@ -305,8 +321,7 @@ function registerVideoCommand(program2) {
|
|
|
305
321
|
"landscape"
|
|
306
322
|
).option(
|
|
307
323
|
"--captions <style>",
|
|
308
|
-
`Caption style: ${VALID_CAPTIONS.join(", ")} (default: off)
|
|
309
|
-
"off"
|
|
324
|
+
`Caption style: ${VALID_CAPTIONS.join(", ")} (default: off for MP4, standard for GIF)`
|
|
310
325
|
).option(
|
|
311
326
|
"--no-auto-templates",
|
|
312
327
|
"Disable content-aware template auto-picking for unannotated headings"
|
|
@@ -322,9 +337,10 @@ function registerVideoCommand(program2) {
|
|
|
322
337
|
`GIF dithering: ${VALID_GIF_DITHERS.join(", ")} (default: sierra2_4a)`
|
|
323
338
|
).option("--bayer-scale <number>", "GIF Bayer dither strength from 0 to 5 (default: 3)").action(async (inputPath, outputArg, opts) => {
|
|
324
339
|
try {
|
|
325
|
-
if (outputArg &&
|
|
326
|
-
|
|
340
|
+
if (outputArg && opts.output) {
|
|
341
|
+
throw new Error("The positional output and --output cannot be used together.");
|
|
327
342
|
}
|
|
343
|
+
if (outputArg) opts.output = outputArg;
|
|
328
344
|
await runVideo(inputPath, opts);
|
|
329
345
|
} catch (err) {
|
|
330
346
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -351,9 +367,9 @@ async function runVideo(inputPath, opts) {
|
|
|
351
367
|
}
|
|
352
368
|
const outputFormat = requestedFormat ?? extensionFormat ?? "mp4";
|
|
353
369
|
const maxFps = outputFormat === "gif" ? 100 : 120;
|
|
354
|
-
const fps =
|
|
355
|
-
if (
|
|
356
|
-
throw new Error(`FPS must be
|
|
370
|
+
const fps = Number(opts.fps ?? (outputFormat === "gif" ? "10" : "30"));
|
|
371
|
+
if (!Number.isSafeInteger(fps) || fps < 1 || fps > maxFps) {
|
|
372
|
+
throw new Error(`FPS must be an integer between 1 and ${maxFps}`);
|
|
357
373
|
}
|
|
358
374
|
const quality = opts.quality ?? "normal";
|
|
359
375
|
if (outputFormat === "gif" && opts.quality !== void 0) {
|
|
@@ -368,16 +384,15 @@ async function runVideo(inputPath, opts) {
|
|
|
368
384
|
`Invalid orientation "${orientation}". Valid: ${VALID_ORIENTATIONS.join(", ")}`
|
|
369
385
|
);
|
|
370
386
|
}
|
|
371
|
-
const width = opts.width === void 0 ? void 0 :
|
|
372
|
-
const height = opts.height === void 0 ? void 0 :
|
|
387
|
+
const width = opts.width === void 0 ? void 0 : Number(opts.width);
|
|
388
|
+
const height = opts.height === void 0 ? void 0 : Number(opts.height);
|
|
373
389
|
validateVideoExportOptions({ width, height, orientation });
|
|
374
|
-
const captions = opts.captions ?? "off";
|
|
390
|
+
const captions = opts.captions ?? (outputFormat === "gif" ? "standard" : "off");
|
|
375
391
|
if (!VALID_CAPTIONS.includes(captions)) {
|
|
376
392
|
throw new Error(`Invalid captions "${captions}". Valid: ${VALID_CAPTIONS.join(", ")}`);
|
|
377
393
|
}
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
if (isNaN(coverPreRoll) || coverPreRoll < 0) {
|
|
394
|
+
const coverPreRoll = Number(opts.coverPreroll ?? "2");
|
|
395
|
+
if (!Number.isFinite(coverPreRoll) || coverPreRoll < 0) {
|
|
381
396
|
throw new Error("Cover pre-roll must be a number of seconds >= 0");
|
|
382
397
|
}
|
|
383
398
|
const animationsEnabled = opts.animations ?? outputFormat === "mp4";
|
|
@@ -445,7 +460,7 @@ async function runVideo(inputPath, opts) {
|
|
|
445
460
|
orientation,
|
|
446
461
|
width,
|
|
447
462
|
height,
|
|
448
|
-
captionStyle,
|
|
463
|
+
captionStyle: captions,
|
|
449
464
|
coverPreRoll,
|
|
450
465
|
animationsEnabled,
|
|
451
466
|
onProgress: (phase, percent) => writeProgress(phase, percent, 100)
|