@bendyline/squisq-cli 1.0.0 → 1.1.0

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 (41) hide show
  1. package/README.md +121 -0
  2. package/dist/__tests__/convert.test.d.ts +7 -0
  3. package/dist/__tests__/convert.test.d.ts.map +1 -0
  4. package/dist/__tests__/convert.test.js +127 -0
  5. package/dist/__tests__/convert.test.js.map +1 -0
  6. package/dist/__tests__/readInput.test.d.ts +5 -0
  7. package/dist/__tests__/readInput.test.d.ts.map +1 -0
  8. package/dist/__tests__/readInput.test.js +120 -0
  9. package/dist/__tests__/readInput.test.js.map +1 -0
  10. package/dist/api.d.ts +106 -0
  11. package/dist/api.d.ts.map +1 -0
  12. package/dist/api.js +324 -0
  13. package/dist/api.js.map +1 -0
  14. package/dist/commands/convert.d.ts +15 -0
  15. package/dist/commands/convert.d.ts.map +1 -0
  16. package/dist/commands/convert.js +186 -0
  17. package/dist/commands/convert.js.map +1 -0
  18. package/dist/commands/video.d.ts +12 -0
  19. package/dist/commands/video.d.ts.map +1 -0
  20. package/dist/commands/video.js +122 -0
  21. package/dist/commands/video.js.map +1 -0
  22. package/dist/index.d.ts +12 -2
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +20 -425
  25. package/dist/index.js.map +1 -1
  26. package/dist/nativeEncoder-EXDP2O5B.js +0 -0
  27. package/dist/nativeEncoder-MWHOONST.js +88 -0
  28. package/dist/nativeEncoder-MWHOONST.js.map +1 -0
  29. package/dist/util/detectFfmpeg.d.ts +15 -0
  30. package/dist/util/detectFfmpeg.d.ts.map +1 -0
  31. package/dist/util/detectFfmpeg.js +29 -0
  32. package/dist/util/detectFfmpeg.js.map +1 -0
  33. package/dist/util/nativeEncoder.d.ts +27 -0
  34. package/dist/util/nativeEncoder.d.ts.map +1 -0
  35. package/dist/util/nativeEncoder.js +106 -0
  36. package/dist/util/nativeEncoder.js.map +1 -0
  37. package/dist/util/readInput.d.ts +31 -0
  38. package/dist/util/readInput.d.ts.map +1 -0
  39. package/dist/util/readInput.js +142 -0
  40. package/dist/util/readInput.js.map +1 -0
  41. package/package.json +17 -5
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @bendyline/squisq-cli
2
+
3
+ Command-line tool for converting Squisq documents to multiple formats and rendering them to video. Reads Markdown files, ZIP/DBK containers, or folders as input.
4
+
5
+ Part of the [Squisq](https://github.com/bendyline/squisq) monorepo.
6
+
7
+ [![npm](https://img.shields.io/npm/v/@bendyline/squisq-cli)](https://www.npmjs.com/package/@bendyline/squisq-cli)
8
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bendyline/squisq/blob/main/LICENSE)
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install -g @bendyline/squisq-cli
14
+ ```
15
+
16
+ ## Commands
17
+
18
+ ### `squisq convert`
19
+
20
+ Convert a Squisq document to DOCX, PPTX, PDF, HTML, or DBK (container ZIP):
21
+
22
+ ```bash
23
+ squisq convert input.md --formats docx,pptx,pdf
24
+ squisq convert project.dbk --output-dir ./out --formats html,docx
25
+ squisq convert ./my-folder --formats pdf --theme documentary
26
+ squisq convert input.md --theme cinematic --transform magazine --formats pptx
27
+ ```
28
+
29
+ | Option | Description | Default |
30
+ | -------------- | ------------------------------------------------ | ----------- |
31
+ | `--output-dir` | Output directory | current dir |
32
+ | `--formats` | Comma-separated list: docx, pptx, pdf, html, dbk | all |
33
+ | `--theme` | Squisq theme ID (e.g., documentary, cinematic) | none |
34
+ | `--transform` | Transform style (e.g., documentary, magazine) | none |
35
+
36
+ ### `squisq video`
37
+
38
+ Render a Squisq document to MP4 video using Playwright for frame capture and native ffmpeg for encoding:
39
+
40
+ ```bash
41
+ squisq video input.md output.mp4
42
+ squisq video project.dbk --quality high --fps 30
43
+ squisq video ./my-folder --orientation portrait --captions social
44
+ ```
45
+
46
+ | Option | Description | Default |
47
+ | --------------- | ------------------------- | --------- |
48
+ | `--fps` | Frames per second (1–120) | 30 |
49
+ | `--quality` | draft, normal, or high | normal |
50
+ | `--orientation` | landscape or portrait | landscape |
51
+ | `--captions` | off, standard, or social | off |
52
+ | `--width` | Override width in pixels | auto |
53
+ | `--height` | Override height in pixels | auto |
54
+
55
+ **Requires:** [ffmpeg](https://ffmpeg.org/) installed and available on your PATH.
56
+
57
+ ## Programmatic API
58
+
59
+ Use the CLI as a library from Node.js — no shell-out required:
60
+
61
+ ```ts
62
+ import { renderDocToMp4, MemoryContentContainer, readInput } from '@bendyline/squisq-cli/api';
63
+
64
+ // Load a document from disk
65
+ const { doc, container } = await readInput('./my-article.md');
66
+
67
+ // Render to MP4
68
+ const result = await renderDocToMp4(doc, container, {
69
+ outputPath: './output.mp4',
70
+ fps: 30,
71
+ quality: 'high',
72
+ orientation: 'landscape',
73
+ captionStyle: 'social',
74
+ onProgress: (phase, pct) => console.log(`${phase}: ${pct}%`),
75
+ });
76
+
77
+ console.log(`Rendered ${result.frameCount} frames (${result.duration}s)`);
78
+ ```
79
+
80
+ ### `extractThumbnails`
81
+
82
+ Extract JPEG thumbnails from the first frame of a rendered video:
83
+
84
+ ```ts
85
+ import { extractThumbnails } from '@bendyline/squisq-cli/api';
86
+
87
+ await extractThumbnails({
88
+ videoPath: './output.mp4',
89
+ outputDir: './thumbs',
90
+ slug: 'my-article',
91
+ sizes: [
92
+ { name: 'og', width: 1200, height: 630, filter: 'scale=1200:630' },
93
+ { name: 'thumb', width: 480, height: 270, filter: 'scale=480:270' },
94
+ ],
95
+ });
96
+ ```
97
+
98
+ See the full [API Reference](../../docs/API.md#bendylinesquisq-cli) for all types and options.
99
+
100
+ ## Input Formats
101
+
102
+ The CLI accepts three types of input:
103
+
104
+ | Input | Description |
105
+ | ------------- | --------------------------------------------------------- |
106
+ | `.md` file | Plain Markdown file |
107
+ | `.zip`/`.dbk` | Container archive with document + embedded media |
108
+ | Folder | Directory with a Markdown file and associated media files |
109
+
110
+ ## Related Packages
111
+
112
+ | Package | Description |
113
+ | ------------------------------------------------------------------------------------ | ----------------------------------------------- |
114
+ | [@bendyline/squisq](https://www.npmjs.com/package/@bendyline/squisq) | Headless core — schemas, templates, markdown |
115
+ | [@bendyline/squisq-formats](https://www.npmjs.com/package/@bendyline/squisq-formats) | DOCX, PDF, HTML import/export (used by convert) |
116
+ | [@bendyline/squisq-video](https://www.npmjs.com/package/@bendyline/squisq-video) | Headless video rendering (used by video) |
117
+ | [@bendyline/squisq-react](https://www.npmjs.com/package/@bendyline/squisq-react) | React components for rendering docs |
118
+
119
+ ## License
120
+
121
+ [MIT](https://github.com/bendyline/squisq/blob/main/LICENSE)
@@ -0,0 +1,7 @@
1
+ /**
2
+ * convert command integration tests
3
+ *
4
+ * Exercises the full convert pipeline: input → all formats → output validation.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=convert.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/convert.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * convert command integration tests
3
+ *
4
+ * Exercises the full convert pipeline: input → all formats → output validation.
5
+ */
6
+ import { describe, it, before, after, beforeEach, afterEach } from 'mocha';
7
+ import { expect } from 'chai';
8
+ import { execFile } from 'node:child_process';
9
+ import { promisify } from 'node:util';
10
+ import { readFile, mkdir, rm, stat, writeFile as fsWriteFile } from 'node:fs/promises';
11
+ import { join } from 'node:path';
12
+ import { tmpdir } from 'node:os';
13
+ import { containerToZip } from '@bendyline/squisq-formats/container';
14
+ import { MemoryContentContainer } from '@bendyline/squisq/storage';
15
+ import { zipToContainer } from '@bendyline/squisq-formats/container';
16
+ const exec = promisify(execFile);
17
+ const CLI_PATH = join(import.meta.dirname, '..', '..', 'dist', 'index.js');
18
+ const FIXTURE_MD = join(import.meta.dirname, 'fixtures', 'test.md');
19
+ /**
20
+ * Run the CLI with the given args and return stdout/stderr.
21
+ */
22
+ async function runCli(...args) {
23
+ return exec('node', [CLI_PATH, ...args], { timeout: 30000 });
24
+ }
25
+ describe('convert command', () => {
26
+ let tempDir;
27
+ beforeEach(async () => {
28
+ tempDir = join(tmpdir(), `squisq-convert-${Date.now()}-${Math.random().toString(36).slice(2)}`);
29
+ await mkdir(tempDir, { recursive: true });
30
+ });
31
+ afterEach(async () => {
32
+ await rm(tempDir, { recursive: true, force: true });
33
+ });
34
+ it('converts a .md file to all formats', async () => {
35
+ const { stderr } = await runCli('convert', FIXTURE_MD, '-o', tempDir);
36
+ expect(stderr).to.include('Done.');
37
+ // Verify all four output files exist and are non-empty
38
+ for (const ext of ['docx', 'pdf', 'html', 'dbk']) {
39
+ const outPath = join(tempDir, `test.${ext}`);
40
+ const info = await stat(outPath);
41
+ expect(info.size).to.be.greaterThan(0);
42
+ }
43
+ });
44
+ it('respects --formats to produce only selected formats', async () => {
45
+ await runCli('convert', FIXTURE_MD, '-o', tempDir, '-f', 'docx,pdf');
46
+ // These should exist
47
+ const docxStat = await stat(join(tempDir, 'test.docx'));
48
+ expect(docxStat.size).to.be.greaterThan(0);
49
+ const pdfStat = await stat(join(tempDir, 'test.pdf'));
50
+ expect(pdfStat.size).to.be.greaterThan(0);
51
+ // These should NOT exist
52
+ for (const ext of ['html', 'dbk']) {
53
+ try {
54
+ await stat(join(tempDir, `test.${ext}`));
55
+ expect.fail(`test.${ext} should not exist`);
56
+ }
57
+ catch (err) {
58
+ expect(err.code).to.equal('ENOENT');
59
+ }
60
+ }
61
+ });
62
+ it('converts a .dbk container to all formats', async () => {
63
+ // Create a minimal .dbk container
64
+ const container = new MemoryContentContainer();
65
+ await container.writeDocument('# Container Doc\n\nSome content here.');
66
+ const blob = await containerToZip(container);
67
+ const buf = Buffer.from(await blob.arrayBuffer());
68
+ const dbkPath = join(tempDir, 'input.dbk');
69
+ await fsWriteFile(dbkPath, buf);
70
+ // Convert to a separate output dir to avoid overwriting the input .dbk
71
+ const outDir = join(tempDir, 'out');
72
+ const { stderr } = await runCli('convert', dbkPath, '-o', outDir);
73
+ expect(stderr).to.include('Done.');
74
+ for (const ext of ['docx', 'pdf', 'html', 'dbk']) {
75
+ const info = await stat(join(outDir, `input.${ext}`));
76
+ expect(info.size).to.be.greaterThan(0);
77
+ }
78
+ });
79
+ it('exits with error for nonexistent input', async () => {
80
+ try {
81
+ await runCli('convert', '/nonexistent/file.md', '-o', tempDir);
82
+ expect.fail('Expected a non-zero exit');
83
+ }
84
+ catch (err) {
85
+ // execFile rejects on non-zero exit
86
+ expect(err.stderr).to.include('Error');
87
+ }
88
+ });
89
+ describe('output validation', () => {
90
+ let validationDir;
91
+ before(async () => {
92
+ validationDir = join(tmpdir(), `squisq-validate-${Date.now()}-${Math.random().toString(36).slice(2)}`);
93
+ await mkdir(validationDir, { recursive: true });
94
+ await runCli('convert', FIXTURE_MD, '-o', validationDir);
95
+ });
96
+ after(async () => {
97
+ await rm(validationDir, { recursive: true, force: true });
98
+ });
99
+ it('DOCX is a valid ZIP with WordprocessingML content', async () => {
100
+ const data = await readFile(join(validationDir, 'test.docx'));
101
+ // ZIP files start with PK (0x50 0x4B)
102
+ expect(data[0]).to.equal(0x50);
103
+ expect(data[1]).to.equal(0x4b);
104
+ // Should contain Content_Types and document.xml inside
105
+ const asString = data.toString('latin1');
106
+ expect(asString).to.include('[Content_Types].xml');
107
+ expect(asString).to.include('word/document.xml');
108
+ });
109
+ it('PDF starts with %PDF- magic bytes', async () => {
110
+ const data = await readFile(join(validationDir, 'test.pdf'));
111
+ const header = data.subarray(0, 5).toString('ascii');
112
+ expect(header).to.equal('%PDF-');
113
+ });
114
+ it('HTML contains SquisqPlayer and root element', async () => {
115
+ const html = await readFile(join(validationDir, 'test.html'), 'utf-8');
116
+ expect(html).to.include('SquisqPlayer');
117
+ expect(html).to.include('squisq-root');
118
+ });
119
+ it('DBK container round-trips through zipToContainer', async () => {
120
+ const data = await readFile(join(validationDir, 'test.dbk'));
121
+ const container = await zipToContainer(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
122
+ const md = await container.readDocument();
123
+ expect(md).to.be.a('string').that.includes('# Test Document');
124
+ });
125
+ });
126
+ });
127
+ //# sourceMappingURL=convert.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.test.js","sourceRoot":"","sources":["../../src/__tests__/convert.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * readInput unit tests
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=readInput.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readInput.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/readInput.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * readInput unit tests
3
+ */
4
+ import { describe, it, beforeEach, afterEach } from 'mocha';
5
+ import { expect } from 'chai';
6
+ import { readInput } from '../util/readInput.js';
7
+ import { writeFile, mkdir, rm } from 'node:fs/promises';
8
+ import { join } from 'node:path';
9
+ import { tmpdir } from 'node:os';
10
+ import { containerToZip } from '@bendyline/squisq-formats/container';
11
+ import { MemoryContentContainer } from '@bendyline/squisq/storage';
12
+ const FIXTURE_MD = join(import.meta.dirname, 'fixtures', 'test.md');
13
+ describe('readInput', () => {
14
+ let tempDir;
15
+ beforeEach(async () => {
16
+ tempDir = join(tmpdir(), `squisq-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
17
+ await mkdir(tempDir, { recursive: true });
18
+ });
19
+ afterEach(async () => {
20
+ await rm(tempDir, { recursive: true, force: true });
21
+ });
22
+ it('reads a .md file into a container and MarkdownDocument', async () => {
23
+ const result = await readInput(FIXTURE_MD);
24
+ expect(result.markdownDoc).to.not.equal(null);
25
+ expect(result.markdownDoc).to.have.property('type', 'document');
26
+ expect(result.markdownDoc.children).to.be.an('array');
27
+ expect(result.markdownDoc.children.length).to.be.greaterThan(0);
28
+ const docContent = await result.container.readDocument();
29
+ expect(docContent).to.be.a('string').that.includes('# Test Document');
30
+ });
31
+ it('reads a .zip container', async () => {
32
+ // Build a minimal container ZIP
33
+ const container = new MemoryContentContainer();
34
+ await container.writeDocument('# From ZIP\n\nHello from a ZIP container.');
35
+ const blob = await containerToZip(container);
36
+ const buf = Buffer.from(await blob.arrayBuffer());
37
+ const zipPath = join(tempDir, 'test.zip');
38
+ await writeFile(zipPath, buf);
39
+ const result = await readInput(zipPath);
40
+ expect(result.markdownDoc).to.not.equal(null);
41
+ expect(result.markdownDoc.type).to.equal('document');
42
+ const doc = await result.container.readDocument();
43
+ expect(doc).to.include('# From ZIP');
44
+ });
45
+ it('reads a .dbk container (same as .zip)', async () => {
46
+ const container = new MemoryContentContainer();
47
+ await container.writeDocument('# From DBK\n\nHello from a DBK container.');
48
+ const blob = await containerToZip(container);
49
+ const buf = Buffer.from(await blob.arrayBuffer());
50
+ const dbkPath = join(tempDir, 'test.dbk');
51
+ await writeFile(dbkPath, buf);
52
+ const result = await readInput(dbkPath);
53
+ expect(result.markdownDoc).to.not.equal(null);
54
+ expect(result.markdownDoc.type).to.equal('document');
55
+ const doc = await result.container.readDocument();
56
+ expect(doc).to.include('# From DBK');
57
+ });
58
+ it('reads a folder with markdown and media', async () => {
59
+ const folderPath = join(tempDir, 'mydoc');
60
+ await mkdir(folderPath, { recursive: true });
61
+ await writeFile(join(folderPath, 'index.md'), '# Folder Doc\n\nContent here.');
62
+ await writeFile(join(folderPath, 'image.txt'), 'placeholder');
63
+ const result = await readInput(folderPath);
64
+ expect(result.markdownDoc).to.not.equal(null);
65
+ expect(result.markdownDoc.type).to.equal('document');
66
+ const doc = await result.container.readDocument();
67
+ expect(doc).to.include('# Folder Doc');
68
+ // Media files are also in the container
69
+ const files = await result.container.listFiles();
70
+ const paths = files.map((f) => f.path);
71
+ expect(paths).to.include('image.txt');
72
+ });
73
+ it('throws on folder with no markdown file', async () => {
74
+ const folderPath = join(tempDir, 'empty');
75
+ await mkdir(folderPath, { recursive: true });
76
+ await writeFile(join(folderPath, 'image.png'), Buffer.from([0x89, 0x50]));
77
+ try {
78
+ await readInput(folderPath);
79
+ expect.fail('Expected an error');
80
+ }
81
+ catch (err) {
82
+ expect(err).to.be.instanceOf(Error);
83
+ expect(err.message).to.include('No markdown document');
84
+ }
85
+ });
86
+ it('reads a .json file as Doc JSON', async () => {
87
+ const doc = {
88
+ articleId: 'test-article',
89
+ duration: 30,
90
+ blocks: [{ id: 'b1', startTime: 0, duration: 30, audioSegment: 0 }],
91
+ audio: { segments: [] },
92
+ };
93
+ const jsonPath = join(tempDir, 'doc.json');
94
+ await writeFile(jsonPath, JSON.stringify(doc));
95
+ const result = await readInput(jsonPath);
96
+ expect(result.markdownDoc).to.equal(null);
97
+ expect(result.doc).to.deep.include({ articleId: 'test-article', duration: 30 });
98
+ expect(result.doc.blocks).to.have.length(1);
99
+ });
100
+ it('reads a folder with doc.json instead of markdown', async () => {
101
+ const folderPath = join(tempDir, 'jsondoc');
102
+ await mkdir(folderPath, { recursive: true });
103
+ const doc = {
104
+ articleId: 'folder-doc',
105
+ duration: 20,
106
+ blocks: [],
107
+ audio: { segments: [] },
108
+ };
109
+ await writeFile(join(folderPath, 'doc.json'), JSON.stringify(doc));
110
+ await writeFile(join(folderPath, 'image.txt'), 'placeholder');
111
+ const result = await readInput(folderPath);
112
+ expect(result.markdownDoc).to.equal(null);
113
+ expect(result.doc).to.deep.include({ articleId: 'folder-doc' });
114
+ // Media files are still in the container
115
+ const files = await result.container.listFiles();
116
+ const paths = files.map((f) => f.path);
117
+ expect(paths).to.include('image.txt');
118
+ });
119
+ });
120
+ //# sourceMappingURL=readInput.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readInput.test.js","sourceRoot":"","sources":["../../src/__tests__/readInput.test.ts"],"names":[],"mappings":"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"}
package/dist/api.d.ts ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Programmatic Video API
3
+ *
4
+ * Provides a library-style entry point for rendering Squisq documents to MP4
5
+ * from Node.js callers (e.g., Qualla's pipeline). This avoids the need to shell
6
+ * out to the `squisq video` CLI and gives callers full control over the Doc,
7
+ * MemoryContentContainer, and encoding options.
8
+ *
9
+ * Orchestrates the full pipeline: Doc → render HTML → Playwright frame capture → FFmpeg encode.
10
+ *
11
+ * Usage:
12
+ * import { renderDocToMp4 } from '@bendyline/squisq-cli/api';
13
+ *
14
+ * await renderDocToMp4(doc, container, {
15
+ * outputPath: '/tmp/output.mp4',
16
+ * fps: 30,
17
+ * quality: 'normal',
18
+ * orientation: 'landscape',
19
+ * });
20
+ */
21
+ import type { Doc } from '@bendyline/squisq/schemas';
22
+ import type { MemoryContentContainer } from '@bendyline/squisq/storage';
23
+ import type { VideoQuality, VideoOrientation } from '@bendyline/squisq-video';
24
+ export type { VideoQuality, VideoOrientation } from '@bendyline/squisq-video';
25
+ export { MemoryContentContainer } from '@bendyline/squisq/storage';
26
+ export { readInput } from './util/readInput.js';
27
+ export type { ReadInputResult } from './util/readInput.js';
28
+ /** Options for renderDocToMp4. */
29
+ export interface RenderDocToMp4Options {
30
+ /** Output file path for the MP4. */
31
+ outputPath: string;
32
+ /** Frames per second (default: 30). */
33
+ fps?: number;
34
+ /** Encoding quality preset (default: 'normal'). */
35
+ quality?: VideoQuality;
36
+ /** Video orientation (default: 'landscape'). */
37
+ orientation?: VideoOrientation;
38
+ /** Override video width in pixels. */
39
+ width?: number;
40
+ /** Override video height in pixels. */
41
+ height?: number;
42
+ /** Caption style to bake into the video (default: none). */
43
+ captionStyle?: 'standard' | 'social';
44
+ /** Seconds of cover-slide pre-roll before the story starts (default: 0). */
45
+ coverPreRoll?: number;
46
+ /**
47
+ * Progress callback. Called with a phase name and 0-100 percentage.
48
+ */
49
+ onProgress?: (phase: string, percent: number) => void;
50
+ }
51
+ /** Result returned by renderDocToMp4. */
52
+ export interface RenderDocToMp4Result {
53
+ /** Duration of the rendered video in seconds (including pre-roll). */
54
+ duration: number;
55
+ /** Number of frames captured. */
56
+ frameCount: number;
57
+ /** Output file path. */
58
+ outputPath: string;
59
+ }
60
+ /**
61
+ * Render a Doc + media container to an MP4 video file.
62
+ *
63
+ * The container should contain audio and image files referenced by the Doc's
64
+ * audio.segments[].src and block image paths. Files are embedded as base64
65
+ * data URIs in a self-contained render HTML page.
66
+ *
67
+ * Requires:
68
+ * - Playwright (chromium) — for headless frame capture
69
+ * - FFmpeg — for video encoding (must be on PATH)
70
+ *
71
+ * @param doc - The Doc structure to render
72
+ * @param container - MemoryContentContainer with audio/image files
73
+ * @param options - Rendering and encoding options
74
+ * @returns Result with duration and frame count
75
+ */
76
+ export declare function renderDocToMp4(doc: Doc, container: MemoryContentContainer, options: RenderDocToMp4Options): Promise<RenderDocToMp4Result>;
77
+ /** A thumbnail size specification. */
78
+ export interface ThumbnailSpec {
79
+ /** Label for the thumbnail (used in filename: `{slug}-{width}x{height}.jpg`). */
80
+ name: string;
81
+ /** Output width in pixels. */
82
+ width: number;
83
+ /** Output height in pixels. */
84
+ height: number;
85
+ /** FFmpeg video filter string (e.g., 'scale=1280:720'). */
86
+ filter: string;
87
+ }
88
+ /** Options for extractThumbnails. */
89
+ export interface ExtractThumbnailsOptions {
90
+ /** Path to the source MP4 video. */
91
+ videoPath: string;
92
+ /** Directory to write thumbnails into. */
93
+ outputDir: string;
94
+ /** Base slug for filenames (produces `{slug}-{width}x{height}.jpg`). */
95
+ slug: string;
96
+ /** Thumbnail sizes to generate. */
97
+ sizes: ThumbnailSpec[];
98
+ /** Overwrite existing thumbnails (default: false). */
99
+ force?: boolean;
100
+ }
101
+ /**
102
+ * Extract thumbnail images from the first frame of an MP4 video.
103
+ * Produces JPEG files at each specified size using FFmpeg video filters.
104
+ */
105
+ export declare function extractThumbnails(options: ExtractThumbnailsOptions): Promise<void>;
106
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAM9E,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,kCAAkC;AAClC,MAAM,WAAW,qBAAqB;IACpC,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IAEnB,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,mDAAmD;IACnD,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,gDAAgD;IAChD,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;IAErC,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAED,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IAEjB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IAEnB,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,sBAAsB,EACjC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CA8L/B;AAmID,sCAAsC;AACtC,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qCAAqC;AACrC,MAAM,WAAW,wBAAwB;IACvC,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,sDAAsD;IACtD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiCxF"}