@bendyline/squisq-cli 2.8.9 → 2.8.11
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 +12 -0
- package/dist/{api-QLYK5AQE.js → api-P7LOLIZT.js} +4 -2
- package/dist/api.d.ts +120 -2
- package/dist/api.js +480 -84
- package/dist/{chunk-XD2ICI5G.js → chunk-6MZC4YQV.js} +483 -87
- package/dist/{chunk-Z3BHD4AF.js → chunk-RQSP3OPR.js} +2 -1
- package/dist/{chunk-GGVASRPG.js → chunk-Y5XKUWJ5.js} +2 -1
- package/dist/index.js +58 -7
- package/dist/{nativeEncoder-PMVSFLMN.js → nativeEncoder-RILHXTXI.js} +1 -1
- package/dist/{nativeEncoder-RZ3T4Z6S.js → nativeEncoder-T43J5UMP.js} +1 -1
- package/dist/squisq-player.full.global.js +428 -428
- package/dist/squisq-player.global.js +71 -71
- package/package.json +5 -5
|
@@ -141,7 +141,7 @@ async function framesToMp4Native(ffmpegPath, frames, audio, outputPath, options
|
|
|
141
141
|
}
|
|
142
142
|
args.push(outputPath);
|
|
143
143
|
await runFfmpeg(ffmpegPath, args, {
|
|
144
|
-
timeoutMs: 6e5,
|
|
144
|
+
timeoutMs: Math.max(6e5, frames.length * 250),
|
|
145
145
|
failureMessage: "ffmpeg failed",
|
|
146
146
|
signal: options.signal
|
|
147
147
|
});
|
|
@@ -227,6 +227,7 @@ async function framesToGifNativeBytes(ffmpegPath, frames, options = {}) {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
export {
|
|
230
|
+
lastMeaningfulFfmpegLine,
|
|
230
231
|
runFfmpeg,
|
|
231
232
|
GIF_EXPORT_DEFAULTS,
|
|
232
233
|
framesToMp4Native,
|
|
@@ -143,7 +143,7 @@ async function framesToMp4Native(ffmpegPath, frames, audio, outputPath, options
|
|
|
143
143
|
}
|
|
144
144
|
args.push(outputPath);
|
|
145
145
|
await runFfmpeg(ffmpegPath, args, {
|
|
146
|
-
timeoutMs: 6e5,
|
|
146
|
+
timeoutMs: Math.max(6e5, frames.length * 250),
|
|
147
147
|
failureMessage: "ffmpeg failed",
|
|
148
148
|
signal: options.signal
|
|
149
149
|
});
|
|
@@ -229,6 +229,7 @@ async function framesToGifNativeBytes(ffmpegPath, frames, options = {}) {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
export {
|
|
232
|
+
lastMeaningfulFfmpegLine,
|
|
232
233
|
runFfmpeg,
|
|
233
234
|
GIF_EXPORT_DEFAULTS,
|
|
234
235
|
framesToMp4Native,
|
package/dist/index.js
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
renderDocToDashboardPng,
|
|
10
10
|
renderDocToGif,
|
|
11
11
|
renderDocToMp4
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-6MZC4YQV.js";
|
|
13
|
+
import "./chunk-Y5XKUWJ5.js";
|
|
14
14
|
|
|
15
15
|
// src/index.ts
|
|
16
16
|
import { createRequire } from "module";
|
|
@@ -319,8 +319,11 @@ import { validateVideoExportOptions } from "@bendyline/squisq-video";
|
|
|
319
319
|
var VALID_QUALITIES = ["draft", "normal", "high"];
|
|
320
320
|
var VALID_ORIENTATIONS = ["landscape", "portrait"];
|
|
321
321
|
var VALID_CAPTIONS = ["off", "standard", "social"];
|
|
322
|
+
var VALID_MOTION = ["calm", "documentary", "vibrant"];
|
|
322
323
|
var VALID_FORMATS2 = ["mp4", "gif"];
|
|
323
324
|
var VALID_GIF_DITHERS = ["bayer", "sierra2_4a", "none"];
|
|
325
|
+
var VALID_FRAME_TRANSPORTS = ["pipe", "memory"];
|
|
326
|
+
var VALID_CAPTURE_FORMATS = ["png", "jpeg"];
|
|
324
327
|
function registerVideoCommand(program2) {
|
|
325
328
|
program2.command("video").description("Render a squisq document to MP4 video or animated GIF").argument("<input>", "Path to .md/.json file, .zip/.dbk container, or folder").argument("[output]", "Output .mp4 or .gif path (default: <input>.<format>)").option("-o, --output <path>", "Output .mp4 or .gif path (format inferred from extension)").option("--format <format>", `Output format: ${VALID_FORMATS2.join(", ")} (default: mp4)`).option("--fps <number>", "Frames per second (default: 30 for MP4, 10 for GIF)").option(
|
|
326
329
|
"--quality <level>",
|
|
@@ -336,13 +339,28 @@ function registerVideoCommand(program2) {
|
|
|
336
339
|
"--no-auto-templates",
|
|
337
340
|
"Disable content-aware template auto-picking for unannotated headings"
|
|
338
341
|
).option("-t, --theme <id>", "Squisq theme ID to apply (e.g., documentary, cinematic, bold)").option(
|
|
342
|
+
"--motion <profile>",
|
|
343
|
+
`Motion profile: ${VALID_MOTION.join(", ")} (default: the doc's, then the theme's)`
|
|
344
|
+
).option(
|
|
339
345
|
"--transform <style>",
|
|
340
346
|
"Transform style to apply before rendering (e.g., documentary, magazine, minimal)"
|
|
341
347
|
).option(
|
|
342
348
|
"--cover-preroll <seconds>",
|
|
343
349
|
"Seconds of cover-slide pre-roll before the story starts (default: 2)",
|
|
344
350
|
"2"
|
|
345
|
-
).option("--width <pixels>", "Override video width (must be even for MP4)").option("--height <pixels>", "Override video height (must be even for MP4)").option("--overwrite", "Replace an existing output file (default: refuse and exit non-zero)").option(
|
|
351
|
+
).option("--width <pixels>", "Override video width (must be even for MP4)").option("--height <pixels>", "Override video height (must be even for MP4)").option("--overwrite", "Replace an existing output file (default: refuse and exit non-zero)").option(
|
|
352
|
+
"--frame-transport <mode>",
|
|
353
|
+
`MP4 frame delivery: ${VALID_FRAME_TRANSPORTS.join(", ")} (default: pipe, streams frames to ffmpeg)`
|
|
354
|
+
).option(
|
|
355
|
+
"--capture-format <format>",
|
|
356
|
+
`MP4 still format captured per frame: ${VALID_CAPTURE_FORMATS.join(", ")} (default: png for high quality, jpeg otherwise)`
|
|
357
|
+
).option(
|
|
358
|
+
"--frames-dir <dir>",
|
|
359
|
+
"MP4 only: spool every captured frame here and keep it after the render"
|
|
360
|
+
).option(
|
|
361
|
+
"--resume",
|
|
362
|
+
"MP4 only: reuse frames an identical earlier render spooled into --frames-dir"
|
|
363
|
+
).option("--animations", "Enable slide animations/transitions (GIF default: disabled)").option("--no-animations", "Disable slide animations/transitions").option("--loop <count>", "GIF repeat count: 0 forever, -1 no loop (default: 0)").option("--max-colors <count>", "GIF palette size from 2 to 256 (default: 256)").option(
|
|
346
364
|
"--dither <algorithm>",
|
|
347
365
|
`GIF dithering: ${VALID_GIF_DITHERS.join(", ")} (default: sierra2_4a)`
|
|
348
366
|
).option("--bayer-scale <number>", "GIF Bayer dither strength from 0 to 5 (default: 3)").action(async (inputPath, outputArg, opts) => {
|
|
@@ -401,10 +419,37 @@ async function runVideo(inputPath, opts) {
|
|
|
401
419
|
if (!VALID_CAPTIONS.includes(captions)) {
|
|
402
420
|
throw new Error(`Invalid captions "${captions}". Valid: ${VALID_CAPTIONS.join(", ")}`);
|
|
403
421
|
}
|
|
422
|
+
const motion = opts.motion;
|
|
423
|
+
if (motion !== void 0 && !VALID_MOTION.includes(motion)) {
|
|
424
|
+
throw new Error(`Invalid motion "${motion}". Valid: ${VALID_MOTION.join(", ")}`);
|
|
425
|
+
}
|
|
404
426
|
const coverPreRoll = Number(opts.coverPreroll ?? "2");
|
|
405
427
|
if (!Number.isFinite(coverPreRoll) || coverPreRoll < 0) {
|
|
406
428
|
throw new Error("Cover pre-roll must be a number of seconds >= 0");
|
|
407
429
|
}
|
|
430
|
+
if (outputFormat === "gif" && (opts.frameTransport || opts.captureFormat || opts.framesDir || opts.resume)) {
|
|
431
|
+
throw new Error(
|
|
432
|
+
"--frame-transport, --capture-format, --frames-dir, and --resume only apply to MP4 output"
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
const frameTransport = opts.frameTransport ?? "pipe";
|
|
436
|
+
if (!VALID_FRAME_TRANSPORTS.includes(frameTransport)) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
`Invalid frame transport "${frameTransport}". Valid: ${VALID_FRAME_TRANSPORTS.join(", ")}`
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
const captureFormat = opts.captureFormat;
|
|
442
|
+
if (captureFormat !== void 0 && !VALID_CAPTURE_FORMATS.includes(captureFormat)) {
|
|
443
|
+
throw new Error(
|
|
444
|
+
`Invalid capture format "${captureFormat}". Valid: ${VALID_CAPTURE_FORMATS.join(", ")}`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
if (opts.resume && !opts.framesDir) {
|
|
448
|
+
throw new Error("--resume requires --frames-dir");
|
|
449
|
+
}
|
|
450
|
+
if ((opts.framesDir || captureFormat) && frameTransport !== "pipe") {
|
|
451
|
+
throw new Error("--frames-dir and --capture-format require --frame-transport pipe");
|
|
452
|
+
}
|
|
408
453
|
const animationsEnabled = opts.animations ?? outputFormat === "mp4";
|
|
409
454
|
const loop = opts.loop === void 0 ? 0 : Number(opts.loop);
|
|
410
455
|
const maxColors = opts.maxColors === void 0 ? 256 : Number(opts.maxColors);
|
|
@@ -461,8 +506,8 @@ async function runVideo(inputPath, opts) {
|
|
|
461
506
|
if (opts.theme) {
|
|
462
507
|
doc = { ...doc, themeId: opts.theme };
|
|
463
508
|
}
|
|
464
|
-
const motionLabel = animationsEnabled ?
|
|
465
|
-
const qualityLabel = outputFormat === "mp4" ? `, quality: ${quality}` : "";
|
|
509
|
+
const motionLabel = animationsEnabled ? `animations on${motion ? ` (${motion})` : ""}` : "animations off";
|
|
510
|
+
const qualityLabel = outputFormat === "mp4" ? `, quality: ${quality}, transport: ${frameTransport}` : "";
|
|
466
511
|
console.error(
|
|
467
512
|
`Rendering ${outputFormat.toUpperCase()}: ${fps} fps${qualityLabel}, orientation: ${orientation}, captions: ${captions}, ${motionLabel}`
|
|
468
513
|
);
|
|
@@ -473,6 +518,7 @@ async function runVideo(inputPath, opts) {
|
|
|
473
518
|
width,
|
|
474
519
|
height,
|
|
475
520
|
captionStyle: captions,
|
|
521
|
+
motion,
|
|
476
522
|
coverPreRoll,
|
|
477
523
|
animationsEnabled,
|
|
478
524
|
onProgress: (phase, percent) => writeProgress(phase, percent, 100)
|
|
@@ -485,15 +531,20 @@ async function runVideo(inputPath, opts) {
|
|
|
485
531
|
bayerScale
|
|
486
532
|
}) : await renderDocToMp4(doc, container, {
|
|
487
533
|
...sharedRenderOptions,
|
|
488
|
-
quality
|
|
534
|
+
quality,
|
|
535
|
+
frameTransport,
|
|
536
|
+
captureFormat,
|
|
537
|
+
framesDir: opts.framesDir ? resolve2(opts.framesDir) : void 0,
|
|
538
|
+
resume: opts.resume
|
|
489
539
|
});
|
|
490
540
|
clearProgress();
|
|
491
541
|
const warnings = "warnings" in result2 && Array.isArray(result2.warnings) ? result2.warnings : [];
|
|
492
542
|
for (const warning of warnings) {
|
|
493
543
|
console.error(` \u26A0 ${warning}`);
|
|
494
544
|
}
|
|
545
|
+
const reused = "reusedFrameCount" in result2 && result2.reusedFrameCount ? `, ${result2.reusedFrameCount} reused from ${opts.framesDir}` : "";
|
|
495
546
|
console.error(
|
|
496
|
-
` \u2713 ${outputPath} (${result2.duration.toFixed(1)}s, ${result2.frameCount} frames)`
|
|
547
|
+
` \u2713 ${outputPath} (${result2.duration.toFixed(1)}s, ${result2.frameCount} frames${reused})`
|
|
497
548
|
);
|
|
498
549
|
console.error("Done.");
|
|
499
550
|
}
|