@bendyline/squisq-cli 1.2.2 → 2.0.1
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 +81 -30
- package/dist/api-LE75OSRQ.js +40 -0
- package/dist/api-LE75OSRQ.js.map +1 -0
- package/dist/api.d.ts +211 -39
- package/dist/api.js +580 -183
- package/dist/api.js.map +1 -1
- package/dist/chunk-FVFK4ZJU.js +923 -0
- package/dist/chunk-FVFK4ZJU.js.map +1 -0
- package/dist/chunk-JDHONKFZ.js +210 -0
- package/dist/chunk-JDHONKFZ.js.map +1 -0
- package/dist/chunk-OEN2RADG.js +208 -0
- package/dist/chunk-OEN2RADG.js.map +1 -0
- package/dist/index.js +116 -539
- package/dist/index.js.map +1 -1
- package/dist/nativeEncoder-ET3HUSOE.js +15 -0
- package/dist/nativeEncoder-ET3HUSOE.js.map +1 -0
- package/dist/nativeEncoder-LRPK2YV5.js +16 -0
- package/dist/nativeEncoder-LRPK2YV5.js.map +1 -0
- package/package.json +7 -7
- package/dist/nativeEncoder-2HGWDEZZ.js +0 -86
- package/dist/nativeEncoder-2HGWDEZZ.js.map +0 -1
- package/dist/nativeEncoder-DLLAT2RT.js +0 -88
- package/dist/nativeEncoder-DLLAT2RT.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @bendyline/squisq-cli
|
|
2
2
|
|
|
3
|
-
Command-line tool and programmatic API for converting Squisq documents between DOCX, PPTX, PDF, XLSX, CSV, HTML, EPUB, Markdown, and container ZIP — and rendering them to MP4 video. Reads Markdown, binary documents (`.docx`/`.pptx`/`.pdf`/`.xlsx`/`.csv`/`.html`), ZIP/`.dbk` containers, folders, or pre-built Doc JSON as input.
|
|
3
|
+
Command-line tool and programmatic API for converting Squisq documents between DOCX, PPTX, PDF, XLSX, CSV, HTML, EPUB, Markdown, and container ZIP — and rendering them to MP4 video or animated GIF. Reads Markdown, binary documents (`.docx`/`.pptx`/`.pdf`/`.xlsx`/`.csv`/`.html`), ZIP/`.dbk` containers, folders, or pre-built Doc JSON as input.
|
|
4
4
|
|
|
5
5
|
Part of the [Squisq](https://github.com/bendyline/squisq) monorepo.
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ Part of the [Squisq](https://github.com/bendyline/squisq) monorepo.
|
|
|
13
13
|
npm install -g @bendyline/squisq-cli
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
For `squisq video` (and `convert` to `mp4`) you also need:
|
|
16
|
+
For `squisq video` (and `convert` to `mp4` or `gif`) you also need:
|
|
17
17
|
|
|
18
18
|
- ffmpeg — resolved from the `SQUISQ_FFMPEG` env var, then your `PATH` (`brew install ffmpeg` / `apt install ffmpeg` / `winget install ffmpeg`), then an optionally-installed `ffmpeg-static` package.
|
|
19
19
|
- A Playwright-managed Chromium for headless frame capture (e.g. `npx playwright install chromium`)
|
|
@@ -29,55 +29,62 @@ Convert a document to one or more formats. Input can be Markdown or a binary doc
|
|
|
29
29
|
```bash
|
|
30
30
|
squisq convert input.md -o report.docx # single file; format inferred from extension
|
|
31
31
|
squisq convert report.docx -o report.md # binary input → markdown
|
|
32
|
-
squisq convert input.md --
|
|
32
|
+
squisq convert input.md --formats pptx # single format to the default output dir
|
|
33
33
|
squisq convert input.md --formats docx,pptx,pdf # multiple formats
|
|
34
34
|
squisq convert project.dbk --output-dir ./out --formats html,docx
|
|
35
|
-
squisq convert input.md --theme cinematic --transform magazine --
|
|
35
|
+
squisq convert input.md --theme cinematic --transform magazine --formats pptx
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
| Option | Description
|
|
39
|
-
| --------------------- |
|
|
40
|
-
| `-o, --output <file>` | **Single** output file; format inferred from its extension (cannot combine with `--formats
|
|
41
|
-
| `-d, --output-dir` | Output directory (multi-format mode)
|
|
42
|
-
| `-f, --formats` | Comma-separated: `docx`, `pptx`, `pdf`, `html`, `htmlzip`, `epub`, `dbk`, `md`, `xlsx`, `csv`, `mp4`
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
| `--
|
|
46
|
-
| `--no-auto-templates` | Disable content-aware template auto-picking for unannotated headings | (auto on) |
|
|
38
|
+
| Option | Description | Default |
|
|
39
|
+
| --------------------- | ----------------------------------------------------------------------------------------------------------- | ------------- |
|
|
40
|
+
| `-o, --output <file>` | **Single** output file; format inferred from its extension (cannot combine with `--formats`) | — |
|
|
41
|
+
| `-d, --output-dir` | Output directory (multi-format mode) | same as input |
|
|
42
|
+
| `-f, --formats` | Comma-separated: `docx`, `pptx`, `pdf`, `html`, `htmlzip`, `epub`, `dbk`, `md`, `xlsx`, `csv`, `mp4`, `gif` | default set |
|
|
43
|
+
| `-t, --theme` | Squisq theme id — built-in or a custom theme inlined in the doc's frontmatter | none |
|
|
44
|
+
| `--transform` | Transform style applied before export (e.g. `documentary`, `magazine`, `minimal`) | none |
|
|
45
|
+
| `--no-auto-templates` | Disable content-aware template auto-picking for unannotated headings | (auto on) |
|
|
47
46
|
|
|
48
|
-
> **v1.5 breaking flag change:** `-o` is now a **single-file** destination (format inferred from the extension). The old `-o` output-**directory** behavior is now `-d, --output-dir`. A bare `convert <input>` with no `-o`/`--
|
|
47
|
+
> **v1.5 breaking flag change:** `-o` is now a **single-file** destination (format inferred from the extension). The old `-o` output-**directory** behavior is now `-d, --output-dir`. A bare `convert <input>` with no `-o`/`--formats` writes a default set that deliberately excludes `md`/`xlsx`/`csv`/`mp4`/`gif`.
|
|
49
48
|
|
|
50
49
|
Notes:
|
|
51
50
|
|
|
52
51
|
- `html` produces a single self-contained file with the standalone player inlined (static mode); `htmlzip` produces a `<name>.html.zip` archive with external assets and optional audio.
|
|
53
52
|
- `epub` embeds images from the input container and, when the doc has narration segments, generates EPUB 3 Media Overlays.
|
|
54
|
-
- `xlsx` export is tables-only; `csv` export emits the first table; `mp4`
|
|
53
|
+
- `xlsx` export is tables-only; `csv` export emits the first table; `mp4` and `gif` require the render toolchain (see Install).
|
|
55
54
|
- `dbk` re-serializes the input container as a ZIP.
|
|
56
55
|
|
|
57
56
|
### `squisq video <input> [output]`
|
|
58
57
|
|
|
59
|
-
Render a document to MP4
|
|
58
|
+
Render a document to MP4 or animated GIF. Playwright captures deterministic frames from a headless player page; native ffmpeg encodes H.264 + AAC for MP4 or a generated global palette for GIF. GIF has no audio track.
|
|
60
59
|
|
|
61
60
|
```bash
|
|
62
61
|
squisq video input.md output.mp4
|
|
62
|
+
squisq video input.md output.gif
|
|
63
|
+
squisq video input.md --format gif --no-animations
|
|
63
64
|
squisq video project.dbk --quality high --fps 30
|
|
64
65
|
squisq video ./my-folder --orientation portrait --captions social
|
|
65
66
|
squisq video input.md -t documentary --transform magazine --cover-preroll 1.5
|
|
66
67
|
squisq video doc.json -o out.mp4
|
|
67
68
|
```
|
|
68
69
|
|
|
69
|
-
| Option
|
|
70
|
-
|
|
|
71
|
-
| `-o, --output`
|
|
72
|
-
| `--
|
|
73
|
-
| `--
|
|
74
|
-
| `--
|
|
75
|
-
| `--
|
|
76
|
-
|
|
|
77
|
-
| `--
|
|
78
|
-
| `--
|
|
79
|
-
| `--
|
|
80
|
-
| `--
|
|
70
|
+
| Option | Description | Default |
|
|
71
|
+
| ---------------------------------- | -------------------------------------------------------------------- | ------------------------- |
|
|
72
|
+
| `-o, --output` | Output `.mp4` or `.gif` path; extension selects the format | `<input>.mp4` |
|
|
73
|
+
| `--format` | `mp4` or `gif` | inferred, otherwise `mp4` |
|
|
74
|
+
| `--fps` | Frames per second (MP4 1–120; GIF 1–100) | MP4 30; GIF 10 |
|
|
75
|
+
| `--quality` | MP4-only quality: `draft`, `normal`, or `high` | normal |
|
|
76
|
+
| `--orientation` | `landscape` or `portrait` | landscape |
|
|
77
|
+
| `--captions` | `off`, `standard`, or `social` | off |
|
|
78
|
+
| `--animations` / `--no-animations` | Enable/disable layer animations and block transitions | MP4 enabled; GIF disabled |
|
|
79
|
+
| `--loop` | GIF repeat count (`0` forever, `-1` no loop) | 0 |
|
|
80
|
+
| `--max-colors` | GIF palette size, 2–256 | 256 |
|
|
81
|
+
| `--dither` | GIF dithering: `bayer`, `sierra2_4a`, or `none` | `sierra2_4a` |
|
|
82
|
+
| `--bayer-scale` | Ordered Bayer strength, 0–5 | 3 |
|
|
83
|
+
| `-t, --theme` | Squisq theme id to apply | none |
|
|
84
|
+
| `--transform` | Transform style to apply before rendering | none |
|
|
85
|
+
| `--cover-preroll` | Seconds of cover-slide pre-roll before the story starts | 2 |
|
|
86
|
+
| `--width` / `--height` | Dimension overrides in pixels | MP4 1080p; GIF 960×540 |
|
|
87
|
+
| `--no-auto-templates` | Disable content-aware template auto-picking for unannotated headings | (auto on) |
|
|
81
88
|
|
|
82
89
|
### `squisq doctor`
|
|
83
90
|
|
|
@@ -118,11 +125,11 @@ All commands accept:
|
|
|
118
125
|
|
|
119
126
|
## Programmatic API
|
|
120
127
|
|
|
121
|
-
Import `@bendyline/squisq-cli/api` to use the same pipeline as a library from Node.js — no shell-out required. Like the CLI, `renderDocToMp4`
|
|
128
|
+
Import `@bendyline/squisq-cli/api` to use the same pipeline as a library from Node.js — no shell-out required. Like the CLI, `renderDocToMp4` and `renderDocToGif` require ffmpeg and a Playwright-managed Chromium.
|
|
122
129
|
|
|
123
130
|
### `convert()`
|
|
124
131
|
|
|
125
|
-
A pre-bound wrapper over `@bendyline/squisq-formats`' `convert()` that injects the CLI's format registry (every built-in exporter plus the CLI-only `mp4`
|
|
132
|
+
A pre-bound wrapper over `@bendyline/squisq-formats`' `convert()` that injects the CLI's format registry (every built-in exporter plus the CLI-only `mp4` and `gif` formats) and a default `resolvePlayerScript` (so HTML/player-embedding exports work out of the box). Both are overridable via `options`. `createCliRegistry()` returns that same registry for direct use.
|
|
126
133
|
|
|
127
134
|
```ts
|
|
128
135
|
import { convert, createCliRegistry, ConversionError } from '@bendyline/squisq-cli/api';
|
|
@@ -153,6 +160,7 @@ const result = await renderDocToMp4(doc, input.container, {
|
|
|
153
160
|
quality: 'high', // 'draft' | 'normal' | 'high' (default 'normal')
|
|
154
161
|
orientation: 'landscape', // 'landscape' | 'portrait' (default 'landscape')
|
|
155
162
|
captionStyle: 'social', // 'standard' | 'social' — omit for no captions
|
|
163
|
+
animationsEnabled: false, // static slide changes; media/timing remain active
|
|
156
164
|
coverPreRoll: 2, // seconds of cover-slide pre-roll (default 0)
|
|
157
165
|
onProgress: (phase, pct) => console.log(`${phase}: ${pct}%`),
|
|
158
166
|
});
|
|
@@ -162,6 +170,49 @@ console.log(`Rendered ${result.frameCount} frames (${result.duration}s) → ${re
|
|
|
162
170
|
|
|
163
171
|
The container's audio segments become the MP4's audio track; timed media clips and block videos found in the doc are embedded and mixed at their scheduled positions.
|
|
164
172
|
|
|
173
|
+
### `renderDocToGif`
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { renderDocToGif } from '@bendyline/squisq-cli/api';
|
|
177
|
+
|
|
178
|
+
const result = await renderDocToGif(doc, input.container, {
|
|
179
|
+
outputPath: './output.gif',
|
|
180
|
+
fps: 10,
|
|
181
|
+
animationsEnabled: false,
|
|
182
|
+
maxColors: 256,
|
|
183
|
+
loop: 0,
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
GIF defaults to 960×540 landscape (540×960 portrait), 10 fps, an infinite
|
|
188
|
+
loop, and disabled slide animations/transitions. Embedded video still advances,
|
|
189
|
+
but all audio is omitted and reported through `result.warnings`.
|
|
190
|
+
|
|
191
|
+
### Native frame encoding
|
|
192
|
+
|
|
193
|
+
For callers that already have PNG frames and do not need Playwright capture,
|
|
194
|
+
the API exposes the native FFmpeg layer directly:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { framesToGifNativeBytes, framesToMp4NativeBytes } from '@bendyline/squisq-cli/api';
|
|
198
|
+
|
|
199
|
+
const mp4 = await framesToMp4NativeBytes('/usr/bin/ffmpeg', pngFrames, audioBytes, {
|
|
200
|
+
fps: 30,
|
|
201
|
+
quality: 'high',
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const gif = await framesToGifNativeBytes('/usr/bin/ffmpeg', pngFrames, {
|
|
205
|
+
fps: 10,
|
|
206
|
+
width: 960,
|
|
207
|
+
height: 540,
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The path variants write to a caller-supplied output; each `Bytes` variant returns
|
|
212
|
+
a `Uint8Array`. MP4 pads short audio with silence so narration cannot truncate
|
|
213
|
+
the timeline. GIF uses a compression-friendly diff palette and changed-rectangle
|
|
214
|
+
application.
|
|
215
|
+
|
|
165
216
|
### `extractThumbnails`
|
|
166
217
|
|
|
167
218
|
Extract JPEG thumbnails from the first frame of a rendered video:
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CapturedFrameBudgetError,
|
|
4
|
+
ConversionError,
|
|
5
|
+
MAX_CAPTURED_FRAME_BYTES,
|
|
6
|
+
MemoryContentContainer,
|
|
7
|
+
convert,
|
|
8
|
+
createCliRegistry,
|
|
9
|
+
extractThumbnails,
|
|
10
|
+
prepareConversion,
|
|
11
|
+
readInput,
|
|
12
|
+
renderDocToGif,
|
|
13
|
+
renderDocToMp4
|
|
14
|
+
} from "./chunk-FVFK4ZJU.js";
|
|
15
|
+
import {
|
|
16
|
+
GIF_EXPORT_DEFAULTS,
|
|
17
|
+
framesToGifNative,
|
|
18
|
+
framesToGifNativeBytes,
|
|
19
|
+
framesToMp4Native,
|
|
20
|
+
framesToMp4NativeBytes
|
|
21
|
+
} from "./chunk-JDHONKFZ.js";
|
|
22
|
+
export {
|
|
23
|
+
CapturedFrameBudgetError,
|
|
24
|
+
ConversionError,
|
|
25
|
+
GIF_EXPORT_DEFAULTS,
|
|
26
|
+
MAX_CAPTURED_FRAME_BYTES,
|
|
27
|
+
MemoryContentContainer,
|
|
28
|
+
convert,
|
|
29
|
+
createCliRegistry,
|
|
30
|
+
extractThumbnails,
|
|
31
|
+
framesToGifNative,
|
|
32
|
+
framesToGifNativeBytes,
|
|
33
|
+
framesToMp4Native,
|
|
34
|
+
framesToMp4NativeBytes,
|
|
35
|
+
prepareConversion,
|
|
36
|
+
readInput,
|
|
37
|
+
renderDocToGif,
|
|
38
|
+
renderDocToMp4
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=api-LE75OSRQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/api.d.ts
CHANGED
|
@@ -1,12 +1,53 @@
|
|
|
1
1
|
import { Doc } from '@bendyline/squisq/schemas';
|
|
2
2
|
import { ContentContainer } from '@bendyline/squisq/storage';
|
|
3
3
|
export { MemoryContentContainer } from '@bendyline/squisq/storage';
|
|
4
|
-
import { VideoQuality, VideoOrientation } from '@bendyline/squisq-video';
|
|
5
|
-
export { VideoOrientation, VideoQuality } from '@bendyline/squisq-video';
|
|
6
|
-
import { FormatId,
|
|
7
|
-
export { ConversionError, ConversionErrorCode, ConversionErrorOptions, ConversionResult, ConvertOptions, ConvertSource, FormatDefinition, FormatId, FormatRegistry, NormalizedInput } from '@bendyline/squisq-formats';
|
|
4
|
+
import { VideoQuality, VideoOrientation, GifDither, VideoExportOptions } from '@bendyline/squisq-video';
|
|
5
|
+
export { GifDither, VideoOrientation, VideoQuality } from '@bendyline/squisq-video';
|
|
6
|
+
import { FormatRegistry, FormatId, ConvertOptions, ConvertSource, ConversionResult, PreparedConversion } from '@bendyline/squisq-formats';
|
|
7
|
+
export { ConversionError, ConversionErrorCode, ConversionErrorOptions, ConversionResult, ConvertOptions, ConvertSource, FormatDefinition, FormatId, FormatRegistry, NormalizedInput, PreparedConversion, PreparedExportOptions } from '@bendyline/squisq-formats';
|
|
8
8
|
import { MarkdownDocument } from '@bendyline/squisq/markdown';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* CLI format registry.
|
|
12
|
+
*
|
|
13
|
+
* The CLI extends the formats package's default registry with `mp4` and `gif`
|
|
14
|
+
* formats so `convert()` can produce rendered media like DOCX/PDF/HTML. These
|
|
15
|
+
* exports are Node-only (Playwright + FFmpeg), which is why they live here
|
|
16
|
+
* rather than in the browser-pure formats package.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface Mp4FormatOptions {
|
|
20
|
+
onProgress?: (phase: string, percent: number) => void;
|
|
21
|
+
fps?: number;
|
|
22
|
+
quality?: VideoQuality;
|
|
23
|
+
orientation?: VideoOrientation;
|
|
24
|
+
width?: number;
|
|
25
|
+
height?: number;
|
|
26
|
+
captionStyle?: 'standard' | 'social';
|
|
27
|
+
coverPreRoll?: number;
|
|
28
|
+
animationsEnabled?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Per-export options for `convert(..., 'gif')`. */
|
|
31
|
+
interface GifFormatOptions {
|
|
32
|
+
onProgress?: (phase: string, percent: number) => void;
|
|
33
|
+
fps?: number;
|
|
34
|
+
orientation?: VideoOrientation;
|
|
35
|
+
width?: number;
|
|
36
|
+
height?: number;
|
|
37
|
+
captionStyle?: 'standard' | 'social';
|
|
38
|
+
coverPreRoll?: number;
|
|
39
|
+
animationsEnabled?: boolean;
|
|
40
|
+
loop?: number;
|
|
41
|
+
maxColors?: number;
|
|
42
|
+
dither?: GifDither;
|
|
43
|
+
bayerScale?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build the CLI's format registry: every built-in format from
|
|
47
|
+
* `@bendyline/squisq-formats` plus the CLI-only rendered-media exporters.
|
|
48
|
+
*/
|
|
49
|
+
declare function createCliRegistry(): FormatRegistry;
|
|
50
|
+
|
|
10
51
|
/**
|
|
11
52
|
* readInput
|
|
12
53
|
*
|
|
@@ -37,33 +78,121 @@ interface ReadInputResult {
|
|
|
37
78
|
/** Detected source format id (registry id, or `md`/`json`/`dbk`/`folder`). */
|
|
38
79
|
sourceFormat: FormatId;
|
|
39
80
|
}
|
|
81
|
+
interface ReadInputOptions {
|
|
82
|
+
/** Cancel filesystem traversal, import, or audio resolution at a bounded boundary. */
|
|
83
|
+
signal?: AbortSignal;
|
|
84
|
+
/**
|
|
85
|
+
* Infer a Squisq theme from an OOXML source's theme part (PPTX today).
|
|
86
|
+
* Default true — the importer carries it as frontmatter.
|
|
87
|
+
*/
|
|
88
|
+
inferTheme?: boolean;
|
|
89
|
+
/** Derive custom layout templates from PPTX slide layouts. Default true. */
|
|
90
|
+
inferLayouts?: boolean;
|
|
91
|
+
}
|
|
40
92
|
/**
|
|
41
93
|
* Read input from a file path (markdown, JSON Doc, ZIP/DBK container, folder,
|
|
42
94
|
* or an importable binary format) and resolve it to a populated
|
|
43
95
|
* {@link ReadInputResult}.
|
|
44
96
|
*/
|
|
45
|
-
declare function readInput(inputPath: string): Promise<ReadInputResult>;
|
|
97
|
+
declare function readInput(inputPath: string, options?: ReadInputOptions): Promise<ReadInputResult>;
|
|
46
98
|
|
|
47
99
|
/**
|
|
48
|
-
*
|
|
100
|
+
* Hard retained-memory ceiling for PNG frames captured before native encoding.
|
|
49
101
|
*
|
|
50
|
-
* The
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
102
|
+
* The Node renderer currently keeps captured PNGs in memory while FFmpeg is
|
|
103
|
+
* prepared. Bounding the actual encoded PNG bytes prevents visually complex
|
|
104
|
+
* documents from exhausting the process even when their dimensions and frame
|
|
105
|
+
* counts satisfy higher-level render budgets.
|
|
54
106
|
*/
|
|
107
|
+
declare const MAX_CAPTURED_FRAME_BYTES: number;
|
|
108
|
+
/** Stable failure raised before another captured frame is retained. */
|
|
109
|
+
declare class CapturedFrameBudgetError extends Error {
|
|
110
|
+
readonly capturedBytes: number;
|
|
111
|
+
readonly attemptedFrameBytes: number;
|
|
112
|
+
readonly maximumBytes: number;
|
|
113
|
+
readonly code = "captured-frame-budget-exceeded";
|
|
114
|
+
constructor(capturedBytes: number, attemptedFrameBytes: number, maximumBytes: number);
|
|
115
|
+
}
|
|
55
116
|
|
|
56
117
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
118
|
+
* Native FFmpeg Encoder
|
|
119
|
+
*
|
|
120
|
+
* Encodes PNG frames to MP4 or animated GIF using a locally installed ffmpeg
|
|
121
|
+
* binary. Writes frames to a temporary directory, invokes ffmpeg as a child
|
|
122
|
+
* process, and reads the resulting media file.
|
|
123
|
+
*
|
|
124
|
+
* This is the Node fast path used after the CLI detects native FFmpeg. Browser
|
|
125
|
+
* callers use the WebCodecs/ffmpeg.wasm paths in the video packages instead.
|
|
59
126
|
*/
|
|
60
|
-
declare function createCliRegistry(): FormatRegistry;
|
|
61
127
|
|
|
128
|
+
/** Node-native video options with cooperative and child-process cancellation. */
|
|
129
|
+
interface NativeVideoExportOptions extends VideoExportOptions {
|
|
130
|
+
signal?: AbortSignal;
|
|
131
|
+
}
|
|
132
|
+
/** Options for native animated-GIF encoding. */
|
|
133
|
+
interface GifExportOptions {
|
|
134
|
+
/** Cancel staging or encoding and terminate the native FFmpeg process. */
|
|
135
|
+
signal?: AbortSignal;
|
|
136
|
+
/** Frames per second (default: 10; GIF timing is centisecond-based). */
|
|
137
|
+
fps?: number;
|
|
138
|
+
/** Output width in pixels (default: 960 landscape / 540 portrait). */
|
|
139
|
+
width?: number;
|
|
140
|
+
/** Output height in pixels (default: 540 landscape / 960 portrait). */
|
|
141
|
+
height?: number;
|
|
142
|
+
/** Viewport orientation used for default dimensions. */
|
|
143
|
+
orientation?: VideoOrientation;
|
|
144
|
+
/** Number of repeats; 0 loops forever and -1 disables looping. */
|
|
145
|
+
loop?: number;
|
|
146
|
+
/** Palette size, between 2 and 256 (default: 256). */
|
|
147
|
+
maxColors?: number;
|
|
148
|
+
/** Palette dithering algorithm (default: sierra2_4a). */
|
|
149
|
+
dither?: GifDither;
|
|
150
|
+
/** Bayer strength when dither is bayer (0-5, default: 3). */
|
|
151
|
+
bayerScale?: number;
|
|
152
|
+
/** Encoding progress callback. */
|
|
153
|
+
onProgress?: (percent: number, phase: string) => void;
|
|
154
|
+
}
|
|
155
|
+
/** Compression-friendly defaults for animated GIF output. */
|
|
156
|
+
declare const GIF_EXPORT_DEFAULTS: {
|
|
157
|
+
readonly fps: 10;
|
|
158
|
+
readonly width: 960;
|
|
159
|
+
readonly height: 540;
|
|
160
|
+
readonly loop: 0;
|
|
161
|
+
readonly maxColors: 256;
|
|
162
|
+
readonly dither: GifDither;
|
|
163
|
+
};
|
|
62
164
|
/**
|
|
63
|
-
*
|
|
165
|
+
* Encode frame PNGs to MP4 using native ffmpeg.
|
|
64
166
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
167
|
+
* @param ffmpegPath - Absolute path to the ffmpeg binary
|
|
168
|
+
* @param frames - Array of PNG image bytes (one per frame)
|
|
169
|
+
* @param audio - Optional audio file bytes to mux
|
|
170
|
+
* @param outputPath - Where to write the final MP4
|
|
171
|
+
* @param options - Encoding options (fps, quality, dimensions, progress)
|
|
172
|
+
*/
|
|
173
|
+
declare function framesToMp4Native(ffmpegPath: string, frames: Uint8Array[], audio: Uint8Array | null, outputPath: string, options?: NativeVideoExportOptions): Promise<void>;
|
|
174
|
+
/**
|
|
175
|
+
* Encode frames using native ffmpeg and return the MP4 bytes (instead of writing to disk).
|
|
176
|
+
* Useful when the caller needs the bytes in memory.
|
|
177
|
+
*/
|
|
178
|
+
declare function framesToMp4NativeBytes(ffmpegPath: string, frames: Uint8Array[], audio: Uint8Array | null, options?: NativeVideoExportOptions): Promise<Uint8Array>;
|
|
179
|
+
/**
|
|
180
|
+
* Encode PNG frames to an animated GIF using native FFmpeg.
|
|
181
|
+
*
|
|
182
|
+
* The shared palette graph uses changed-frame statistics and rectangle-scoped
|
|
183
|
+
* palette application so static slide regions remain stable and compress well.
|
|
184
|
+
* GIF has no audio track; callers should surface that limitation before calling.
|
|
185
|
+
*/
|
|
186
|
+
declare function framesToGifNative(ffmpegPath: string, frames: Uint8Array[], outputPath: string, options?: GifExportOptions): Promise<void>;
|
|
187
|
+
/** Encode frames with native FFmpeg and return animated-GIF bytes. */
|
|
188
|
+
declare function framesToGifNativeBytes(ffmpegPath: string, frames: Uint8Array[], options?: GifExportOptions): Promise<Uint8Array>;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Programmatic Rendered-Media API
|
|
192
|
+
*
|
|
193
|
+
* Provides library-style entry points for rendering Squisq documents to MP4
|
|
194
|
+
* or animated GIF from Node.js callers (e.g., Qualla's pipeline). This avoids
|
|
195
|
+
* the need to shell
|
|
67
196
|
* out to the `squisq video` CLI and gives callers full control over the Doc,
|
|
68
197
|
* MemoryContentContainer, and encoding options.
|
|
69
198
|
*
|
|
@@ -78,28 +207,45 @@ declare function createCliRegistry(): FormatRegistry;
|
|
|
78
207
|
* quality: 'normal',
|
|
79
208
|
* orientation: 'landscape',
|
|
80
209
|
* });
|
|
210
|
+
*
|
|
211
|
+
* await renderDocToGif(doc, container, {
|
|
212
|
+
* outputPath: '/tmp/output.gif',
|
|
213
|
+
* animationsEnabled: false,
|
|
214
|
+
* });
|
|
81
215
|
*/
|
|
82
216
|
|
|
217
|
+
/** Convert options with the CLI-only rendered-media adapters strongly typed. */
|
|
218
|
+
type CliConvertOptions = Omit<ConvertOptions, 'formatOptions'> & {
|
|
219
|
+
formatOptions?: ConvertOptions['formatOptions'] & {
|
|
220
|
+
mp4?: Mp4FormatOptions;
|
|
221
|
+
gif?: GifFormatOptions;
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
|
|
83
225
|
/**
|
|
84
226
|
* Convert a document to a target format using the CLI's format registry.
|
|
85
227
|
*
|
|
86
228
|
* This is a thin, pre-bound wrapper over `convert()` from
|
|
87
229
|
* `@bendyline/squisq-formats`: it injects the CLI registry (which adds the
|
|
88
|
-
* `mp4`
|
|
230
|
+
* `mp4` and `gif` formats on top of every built-in exporter) and a default
|
|
89
231
|
* `resolvePlayerScript` that lazily loads the standalone player IIFE bundle
|
|
90
232
|
* (required for HTML/EPUB-style exports). Callers may override either via
|
|
91
233
|
* `options`.
|
|
92
234
|
*
|
|
93
235
|
* @param source - A bytes / markdown / doc {@link ConvertSource}.
|
|
94
|
-
* @param to - Target format id (`docx`, `pdf`, `pptx`, `html`, `mp4`, …).
|
|
236
|
+
* @param to - Target format id (`docx`, `pdf`, `pptx`, `html`, `mp4`, `gif`, …).
|
|
95
237
|
* @param options - Conversion options; `registry` and `resolvePlayerScript`
|
|
96
238
|
* default to the CLI's values but can be overridden.
|
|
97
239
|
* @returns The encoded bytes plus mime type, suggested filename, and warnings.
|
|
98
240
|
* @throws {@link ConversionError} on any failure, with a stable `code`.
|
|
99
241
|
*/
|
|
100
|
-
declare function convert(source: ConvertSource, to: FormatId, options?:
|
|
242
|
+
declare function convert(source: ConvertSource, to: FormatId, options?: CliConvertOptions): Promise<ConversionResult>;
|
|
243
|
+
/** Normalize and transform once using the CLI registry, then export one or more targets. */
|
|
244
|
+
declare function prepareConversion(source: ConvertSource, options?: CliConvertOptions): Promise<PreparedConversion>;
|
|
101
245
|
/** Options for renderDocToMp4. */
|
|
102
246
|
interface RenderDocToMp4Options {
|
|
247
|
+
/** Cancel capture/encoding and terminate browser and FFmpeg work. */
|
|
248
|
+
signal?: AbortSignal;
|
|
103
249
|
/** Output file path for the MP4. */
|
|
104
250
|
outputPath: string;
|
|
105
251
|
/** Frames per second (default: 30). */
|
|
@@ -114,6 +260,8 @@ interface RenderDocToMp4Options {
|
|
|
114
260
|
height?: number;
|
|
115
261
|
/** Caption style to bake into the video (default: none). */
|
|
116
262
|
captionStyle?: 'standard' | 'social';
|
|
263
|
+
/** Render layer animations and block transitions (default: true). */
|
|
264
|
+
animationsEnabled?: boolean;
|
|
117
265
|
/**
|
|
118
266
|
* Seconds of cover-slide pre-roll before the story starts (default: 0).
|
|
119
267
|
*
|
|
@@ -127,6 +275,37 @@ interface RenderDocToMp4Options {
|
|
|
127
275
|
*/
|
|
128
276
|
onProgress?: (phase: string, percent: number) => void;
|
|
129
277
|
}
|
|
278
|
+
/** Options for rendering a document to an animated GIF. */
|
|
279
|
+
interface RenderDocToGifOptions {
|
|
280
|
+
/** Cancel capture/encoding and terminate browser and FFmpeg work. */
|
|
281
|
+
signal?: AbortSignal;
|
|
282
|
+
/** Output file path for the GIF. */
|
|
283
|
+
outputPath: string;
|
|
284
|
+
/** Frames per second (default: 10). */
|
|
285
|
+
fps?: number;
|
|
286
|
+
/** Viewport orientation (default: landscape). */
|
|
287
|
+
orientation?: VideoOrientation;
|
|
288
|
+
/** Override output width (default: 960 landscape / 540 portrait). */
|
|
289
|
+
width?: number;
|
|
290
|
+
/** Override output height (default: 540 landscape / 960 portrait). */
|
|
291
|
+
height?: number;
|
|
292
|
+
/** Caption style to bake into the GIF (default: none). */
|
|
293
|
+
captionStyle?: 'standard' | 'social';
|
|
294
|
+
/** Seconds of cover-slide pre-roll (default: 0). */
|
|
295
|
+
coverPreRoll?: number;
|
|
296
|
+
/** Render layer animations and block transitions (default: false). */
|
|
297
|
+
animationsEnabled?: boolean;
|
|
298
|
+
/** Number of repeats; 0 loops forever and -1 disables looping. */
|
|
299
|
+
loop?: number;
|
|
300
|
+
/** Palette size, from 2 through 256 (default: 256). */
|
|
301
|
+
maxColors?: number;
|
|
302
|
+
/** Palette dithering algorithm (default: sierra2_4a). */
|
|
303
|
+
dither?: GifDither;
|
|
304
|
+
/** Bayer strength when dither is bayer (0-5, default: 3). */
|
|
305
|
+
bayerScale?: number;
|
|
306
|
+
/** Progress callback. */
|
|
307
|
+
onProgress?: (phase: string, percent: number) => void;
|
|
308
|
+
}
|
|
130
309
|
/** Result returned by renderDocToMp4. */
|
|
131
310
|
interface RenderDocToMp4Result {
|
|
132
311
|
/** Duration of the rendered video in seconds (including pre-roll). */
|
|
@@ -136,24 +315,15 @@ interface RenderDocToMp4Result {
|
|
|
136
315
|
/** Output file path. */
|
|
137
316
|
outputPath: string;
|
|
138
317
|
}
|
|
139
|
-
/**
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
*
|
|
146
|
-
* Requires:
|
|
147
|
-
* - Playwright (chromium) — for headless frame capture
|
|
148
|
-
* - FFmpeg — for video encoding (resolved from SQUISQ_FFMPEG, PATH, or an
|
|
149
|
-
* optionally installed `ffmpeg-static` package — see detectFfmpeg)
|
|
150
|
-
*
|
|
151
|
-
* @param doc - The Doc structure to render
|
|
152
|
-
* @param container - MemoryContentContainer with audio/image files
|
|
153
|
-
* @param options - Rendering and encoding options
|
|
154
|
-
* @returns Result with duration and frame count
|
|
155
|
-
*/
|
|
318
|
+
/** Result returned by renderDocToGif. */
|
|
319
|
+
interface RenderDocToGifResult extends RenderDocToMp4Result {
|
|
320
|
+
/** Non-fatal fidelity warnings, including GIF's lack of audio. */
|
|
321
|
+
warnings: string[];
|
|
322
|
+
}
|
|
323
|
+
/** Render a Doc + media container to an MP4 video file. */
|
|
156
324
|
declare function renderDocToMp4(doc: Doc, container: ContentContainer, options: RenderDocToMp4Options): Promise<RenderDocToMp4Result>;
|
|
325
|
+
/** Render a Doc + media container to a silent animated GIF. */
|
|
326
|
+
declare function renderDocToGif(doc: Doc, container: ContentContainer, options: RenderDocToGifOptions): Promise<RenderDocToGifResult>;
|
|
157
327
|
/** A thumbnail size specification. */
|
|
158
328
|
interface ThumbnailSpec {
|
|
159
329
|
/** Label for the thumbnail (used in filename: `{slug}-{width}x{height}.jpg`). */
|
|
@@ -167,7 +337,7 @@ interface ThumbnailSpec {
|
|
|
167
337
|
}
|
|
168
338
|
/** Options for extractThumbnails. */
|
|
169
339
|
interface ExtractThumbnailsOptions {
|
|
170
|
-
/** Path to the source MP4
|
|
340
|
+
/** Path to the source MP4 or animated GIF. */
|
|
171
341
|
videoPath: string;
|
|
172
342
|
/** Directory to write thumbnails into. */
|
|
173
343
|
outputDir: string;
|
|
@@ -177,11 +347,13 @@ interface ExtractThumbnailsOptions {
|
|
|
177
347
|
sizes: ThumbnailSpec[];
|
|
178
348
|
/** Overwrite existing thumbnails (default: false). */
|
|
179
349
|
force?: boolean;
|
|
350
|
+
/** Cancels FFmpeg and rejects with the caller's exact abort reason. */
|
|
351
|
+
signal?: AbortSignal;
|
|
180
352
|
}
|
|
181
353
|
/**
|
|
182
|
-
* Extract thumbnail images from the first frame of an MP4
|
|
354
|
+
* Extract thumbnail images from the first frame of an MP4 or animated GIF.
|
|
183
355
|
* Produces JPEG files at each specified size using FFmpeg video filters.
|
|
184
356
|
*/
|
|
185
357
|
declare function extractThumbnails(options: ExtractThumbnailsOptions): Promise<void>;
|
|
186
358
|
|
|
187
|
-
export { type ExtractThumbnailsOptions, type ReadInputResult, type RenderDocToMp4Options, type RenderDocToMp4Result, type ThumbnailSpec, convert, createCliRegistry, extractThumbnails, readInput, renderDocToMp4 };
|
|
359
|
+
export { CapturedFrameBudgetError, type CliConvertOptions, type ExtractThumbnailsOptions, GIF_EXPORT_DEFAULTS, type GifExportOptions, type GifFormatOptions, MAX_CAPTURED_FRAME_BYTES, type Mp4FormatOptions, type NativeVideoExportOptions, type ReadInputResult, type RenderDocToGifOptions, type RenderDocToGifResult, type RenderDocToMp4Options, type RenderDocToMp4Result, type ThumbnailSpec, convert, createCliRegistry, extractThumbnails, framesToGifNative, framesToGifNativeBytes, framesToMp4Native, framesToMp4NativeBytes, prepareConversion, readInput, renderDocToGif, renderDocToMp4 };
|