@bendyline/squisq-cli 2.8.8 → 2.8.10

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.
@@ -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-XD2ICI5G.js";
13
- import "./chunk-GGVASRPG.js";
12
+ } from "./chunk-4FJZYJC3.js";
13
+ import "./chunk-Y5XKUWJ5.js";
14
14
 
15
15
  // src/index.ts
16
16
  import { createRequire } from "module";
@@ -321,6 +321,8 @@ var VALID_ORIENTATIONS = ["landscape", "portrait"];
321
321
  var VALID_CAPTIONS = ["off", "standard", "social"];
322
322
  var VALID_FORMATS2 = ["mp4", "gif"];
323
323
  var VALID_GIF_DITHERS = ["bayer", "sierra2_4a", "none"];
324
+ var VALID_FRAME_TRANSPORTS = ["pipe", "memory"];
325
+ var VALID_CAPTURE_FORMATS = ["png", "jpeg"];
324
326
  function registerVideoCommand(program2) {
325
327
  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
328
  "--quality <level>",
@@ -342,7 +344,19 @@ function registerVideoCommand(program2) {
342
344
  "--cover-preroll <seconds>",
343
345
  "Seconds of cover-slide pre-roll before the story starts (default: 2)",
344
346
  "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("--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(
347
+ ).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(
348
+ "--frame-transport <mode>",
349
+ `MP4 frame delivery: ${VALID_FRAME_TRANSPORTS.join(", ")} (default: pipe, streams frames to ffmpeg)`
350
+ ).option(
351
+ "--capture-format <format>",
352
+ `MP4 still format captured per frame: ${VALID_CAPTURE_FORMATS.join(", ")} (default: png for high quality, jpeg otherwise)`
353
+ ).option(
354
+ "--frames-dir <dir>",
355
+ "MP4 only: spool every captured frame here and keep it after the render"
356
+ ).option(
357
+ "--resume",
358
+ "MP4 only: reuse frames an identical earlier render spooled into --frames-dir"
359
+ ).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
360
  "--dither <algorithm>",
347
361
  `GIF dithering: ${VALID_GIF_DITHERS.join(", ")} (default: sierra2_4a)`
348
362
  ).option("--bayer-scale <number>", "GIF Bayer dither strength from 0 to 5 (default: 3)").action(async (inputPath, outputArg, opts) => {
@@ -405,6 +419,29 @@ async function runVideo(inputPath, opts) {
405
419
  if (!Number.isFinite(coverPreRoll) || coverPreRoll < 0) {
406
420
  throw new Error("Cover pre-roll must be a number of seconds >= 0");
407
421
  }
422
+ if (outputFormat === "gif" && (opts.frameTransport || opts.captureFormat || opts.framesDir || opts.resume)) {
423
+ throw new Error(
424
+ "--frame-transport, --capture-format, --frames-dir, and --resume only apply to MP4 output"
425
+ );
426
+ }
427
+ const frameTransport = opts.frameTransport ?? "pipe";
428
+ if (!VALID_FRAME_TRANSPORTS.includes(frameTransport)) {
429
+ throw new Error(
430
+ `Invalid frame transport "${frameTransport}". Valid: ${VALID_FRAME_TRANSPORTS.join(", ")}`
431
+ );
432
+ }
433
+ const captureFormat = opts.captureFormat;
434
+ if (captureFormat !== void 0 && !VALID_CAPTURE_FORMATS.includes(captureFormat)) {
435
+ throw new Error(
436
+ `Invalid capture format "${captureFormat}". Valid: ${VALID_CAPTURE_FORMATS.join(", ")}`
437
+ );
438
+ }
439
+ if (opts.resume && !opts.framesDir) {
440
+ throw new Error("--resume requires --frames-dir");
441
+ }
442
+ if ((opts.framesDir || captureFormat) && frameTransport !== "pipe") {
443
+ throw new Error("--frames-dir and --capture-format require --frame-transport pipe");
444
+ }
408
445
  const animationsEnabled = opts.animations ?? outputFormat === "mp4";
409
446
  const loop = opts.loop === void 0 ? 0 : Number(opts.loop);
410
447
  const maxColors = opts.maxColors === void 0 ? 256 : Number(opts.maxColors);
@@ -462,7 +499,7 @@ async function runVideo(inputPath, opts) {
462
499
  doc = { ...doc, themeId: opts.theme };
463
500
  }
464
501
  const motionLabel = animationsEnabled ? "animations on" : "animations off";
465
- const qualityLabel = outputFormat === "mp4" ? `, quality: ${quality}` : "";
502
+ const qualityLabel = outputFormat === "mp4" ? `, quality: ${quality}, transport: ${frameTransport}` : "";
466
503
  console.error(
467
504
  `Rendering ${outputFormat.toUpperCase()}: ${fps} fps${qualityLabel}, orientation: ${orientation}, captions: ${captions}, ${motionLabel}`
468
505
  );
@@ -485,15 +522,20 @@ async function runVideo(inputPath, opts) {
485
522
  bayerScale
486
523
  }) : await renderDocToMp4(doc, container, {
487
524
  ...sharedRenderOptions,
488
- quality
525
+ quality,
526
+ frameTransport,
527
+ captureFormat,
528
+ framesDir: opts.framesDir ? resolve2(opts.framesDir) : void 0,
529
+ resume: opts.resume
489
530
  });
490
531
  clearProgress();
491
532
  const warnings = "warnings" in result2 && Array.isArray(result2.warnings) ? result2.warnings : [];
492
533
  for (const warning of warnings) {
493
534
  console.error(` \u26A0 ${warning}`);
494
535
  }
536
+ const reused = "reusedFrameCount" in result2 && result2.reusedFrameCount ? `, ${result2.reusedFrameCount} reused from ${opts.framesDir}` : "";
495
537
  console.error(
496
- ` \u2713 ${outputPath} (${result2.duration.toFixed(1)}s, ${result2.frameCount} frames)`
538
+ ` \u2713 ${outputPath} (${result2.duration.toFixed(1)}s, ${result2.frameCount} frames${reused})`
497
539
  );
498
540
  console.error("Done.");
499
541
  }
@@ -5,7 +5,7 @@ import {
5
5
  framesToGifNativeBytes,
6
6
  framesToMp4Native,
7
7
  framesToMp4NativeBytes
8
- } from "./chunk-GGVASRPG.js";
8
+ } from "./chunk-Y5XKUWJ5.js";
9
9
  export {
10
10
  GIF_EXPORT_DEFAULTS,
11
11
  framesToGifNative,
@@ -4,7 +4,7 @@ import {
4
4
  framesToGifNativeBytes,
5
5
  framesToMp4Native,
6
6
  framesToMp4NativeBytes
7
- } from "./chunk-Z3BHD4AF.js";
7
+ } from "./chunk-RQSP3OPR.js";
8
8
  export {
9
9
  GIF_EXPORT_DEFAULTS,
10
10
  framesToGifNative,