@bendyline/squisq-video 2.2.12 → 2.3.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.
- package/dist/index.d.ts +138 -2
- package/dist/index.js +91 -3
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Doc, Theme, VideoPresentation, VideoPipSize, VideoPipShape, VideoPipPosition } from '@bendyline/squisq/schemas';
|
|
1
|
+
import { Doc, Theme, VideoPresentation, VideoPipSize, VideoPipShape, VideoPipPosition, ViewportPreset } from '@bendyline/squisq/schemas';
|
|
2
2
|
export { fetchFile } from '@ffmpeg/util';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -260,6 +260,26 @@ interface RenderHtmlOptions {
|
|
|
260
260
|
* Defaults to true. Embedded/timed media and document timing are unaffected.
|
|
261
261
|
*/
|
|
262
262
|
animationsEnabled?: boolean;
|
|
263
|
+
/**
|
|
264
|
+
* Player rendition mounted in the capture page (default 'slideshow').
|
|
265
|
+
* 'dashboard' renders the one-canvas dashboard projection for
|
|
266
|
+
* single-frame image capture.
|
|
267
|
+
*/
|
|
268
|
+
displayMode?: 'slideshow' | 'dashboard';
|
|
269
|
+
/** Dashboard-mode options (ignored unless `displayMode` is 'dashboard'). */
|
|
270
|
+
dashboard?: {
|
|
271
|
+
/** Layout id or 'auto'. Overrides doc frontmatter. */
|
|
272
|
+
layout?: string;
|
|
273
|
+
/** Title-band override. Overrides doc frontmatter. */
|
|
274
|
+
title?: boolean;
|
|
275
|
+
/**
|
|
276
|
+
* Cell style variant ('basic' | 'card' | 'panel' | 'accent'). Overrides
|
|
277
|
+
* doc frontmatter; colors always come from the active theme.
|
|
278
|
+
*/
|
|
279
|
+
style?: string;
|
|
280
|
+
/** Host-supplied title fallback (typically the file name). */
|
|
281
|
+
documentTitle?: string;
|
|
282
|
+
};
|
|
263
283
|
}
|
|
264
284
|
/**
|
|
265
285
|
* Generate a self-contained HTML document for headless video frame capture.
|
|
@@ -273,6 +293,122 @@ interface RenderHtmlOptions {
|
|
|
273
293
|
*/
|
|
274
294
|
declare function generateRenderHtml(doc: Doc, options: RenderHtmlOptions): string;
|
|
275
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Dashboard image export presets and dimension helpers.
|
|
298
|
+
*
|
|
299
|
+
* Lives in @bendyline/squisq-video (browser-pure, depended on by both the
|
|
300
|
+
* CLI and the browser export UI) so the `squisq image` command and the
|
|
301
|
+
* DashboardImageExportModal share one resolution table and one validator.
|
|
302
|
+
*
|
|
303
|
+
* These are EXPORT resolutions (physical pixels), distinct from core's
|
|
304
|
+
* `VIEWPORT_PRESETS` (virtual design-space viewports) — each preset names
|
|
305
|
+
* the viewport family its aspect ratio implies so layouts pick the right
|
|
306
|
+
* orientation variant. Validation follows the cover-image ruleset (whole
|
|
307
|
+
* pixels, sane bounds, megapixel cap), NOT the H.264 even-dimension rule:
|
|
308
|
+
* that constraint exists for yuv420p video encoding and has no meaning
|
|
309
|
+
* for a PNG.
|
|
310
|
+
*/
|
|
311
|
+
|
|
312
|
+
interface DashboardResolutionPreset {
|
|
313
|
+
id: string;
|
|
314
|
+
label: string;
|
|
315
|
+
width: number;
|
|
316
|
+
height: number;
|
|
317
|
+
/** The `VIEWPORT_PRESETS` family this aspect ratio implies. */
|
|
318
|
+
family: ViewportPreset;
|
|
319
|
+
}
|
|
320
|
+
/** Named export resolutions offered by the CLI and the export dialog. */
|
|
321
|
+
declare const DASHBOARD_RESOLUTIONS: readonly [{
|
|
322
|
+
readonly id: "hd";
|
|
323
|
+
readonly label: "HD 1280×720";
|
|
324
|
+
readonly width: 1280;
|
|
325
|
+
readonly height: 720;
|
|
326
|
+
readonly family: "landscape";
|
|
327
|
+
}, {
|
|
328
|
+
readonly id: "fhd";
|
|
329
|
+
readonly label: "Full HD 1920×1080";
|
|
330
|
+
readonly width: 1920;
|
|
331
|
+
readonly height: 1080;
|
|
332
|
+
readonly family: "landscape";
|
|
333
|
+
}, {
|
|
334
|
+
readonly id: "4k";
|
|
335
|
+
readonly label: "4K UHD 3840×2160";
|
|
336
|
+
readonly width: 3840;
|
|
337
|
+
readonly height: 2160;
|
|
338
|
+
readonly family: "landscape";
|
|
339
|
+
}, {
|
|
340
|
+
readonly id: "square";
|
|
341
|
+
readonly label: "Square 1080×1080";
|
|
342
|
+
readonly width: 1080;
|
|
343
|
+
readonly height: 1080;
|
|
344
|
+
readonly family: "square";
|
|
345
|
+
}, {
|
|
346
|
+
readonly id: "square-2k";
|
|
347
|
+
readonly label: "Square 2160×2160";
|
|
348
|
+
readonly width: 2160;
|
|
349
|
+
readonly height: 2160;
|
|
350
|
+
readonly family: "square";
|
|
351
|
+
}, {
|
|
352
|
+
readonly id: "portrait";
|
|
353
|
+
readonly label: "Portrait 1080×1920";
|
|
354
|
+
readonly width: 1080;
|
|
355
|
+
readonly height: 1920;
|
|
356
|
+
readonly family: "portrait";
|
|
357
|
+
}, {
|
|
358
|
+
readonly id: "portrait-4k";
|
|
359
|
+
readonly label: "Portrait 4K 2160×3840";
|
|
360
|
+
readonly width: 2160;
|
|
361
|
+
readonly height: 3840;
|
|
362
|
+
readonly family: "portrait";
|
|
363
|
+
}, {
|
|
364
|
+
readonly id: "standard";
|
|
365
|
+
readonly label: "4:3 1440×1080";
|
|
366
|
+
readonly width: 1440;
|
|
367
|
+
readonly height: 1080;
|
|
368
|
+
readonly family: "standard";
|
|
369
|
+
}];
|
|
370
|
+
type DashboardResolutionId = (typeof DASHBOARD_RESOLUTIONS)[number]['id'];
|
|
371
|
+
declare const DEFAULT_DASHBOARD_RESOLUTION: DashboardResolutionId;
|
|
372
|
+
/** Minimum edge length for a dashboard image, in pixels. */
|
|
373
|
+
declare const MIN_DASHBOARD_IMAGE_DIMENSION = 64;
|
|
374
|
+
/** Maximum edge length for a dashboard image, in pixels (8K width). */
|
|
375
|
+
declare const MAX_DASHBOARD_IMAGE_DIMENSION = 7680;
|
|
376
|
+
/** Maximum total pixels (4× the 1080p-based cover-image budget: 7680×4320). */
|
|
377
|
+
declare const MAX_DASHBOARD_IMAGE_PIXELS = 33177600;
|
|
378
|
+
/**
|
|
379
|
+
* Validate custom dashboard-image dimensions. Returns a human-readable
|
|
380
|
+
* problem description, or null when the dimensions are acceptable.
|
|
381
|
+
*/
|
|
382
|
+
declare function validateDashboardImageDimensions(width: number, height: number): string | null;
|
|
383
|
+
/**
|
|
384
|
+
* The viewport family whose aspect ratio is nearest to `width`/`height` —
|
|
385
|
+
* a uniform log-ratio comparison over all four `VIEWPORT_PRESETS`
|
|
386
|
+
* families (core's `getViewportOrientation` has no 4:3 notion, so 1440×1080
|
|
387
|
+
* would misreport as plain landscape there).
|
|
388
|
+
*/
|
|
389
|
+
declare function dashboardFamilyForDimensions(width: number, height: number): ViewportPreset;
|
|
390
|
+
interface ResolveDashboardDimensionsInput {
|
|
391
|
+
/** Named preset id (default {@link DEFAULT_DASHBOARD_RESOLUTION}). */
|
|
392
|
+
resolution?: string;
|
|
393
|
+
/** Custom width; requires `height` and excludes `resolution`. */
|
|
394
|
+
width?: number;
|
|
395
|
+
/** Custom height; requires `width` and excludes `resolution`. */
|
|
396
|
+
height?: number;
|
|
397
|
+
}
|
|
398
|
+
interface ResolvedDashboardDimensions {
|
|
399
|
+
width: number;
|
|
400
|
+
height: number;
|
|
401
|
+
family: ViewportPreset;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Resolve the export dimensions from a preset id or explicit pixels — the
|
|
405
|
+
* single resolution-logic implementation shared by the programmatic API,
|
|
406
|
+
* the `png` format, and the `squisq image` command. Throws `RangeError`
|
|
407
|
+
* on contradictory or invalid input so callers fail before any expensive
|
|
408
|
+
* rendering starts.
|
|
409
|
+
*/
|
|
410
|
+
declare function resolveDashboardDimensions(input?: ResolveDashboardDimensionsInput): ResolvedDashboardDimensions;
|
|
411
|
+
|
|
276
412
|
/**
|
|
277
413
|
* FFmpeg argument builders — the single source of truth for translating a
|
|
278
414
|
* {@link VideoQuality} into ffmpeg CLI flags. Shared verbatim by every
|
|
@@ -358,4 +494,4 @@ declare function ffmpegGifOutputArgs(options: GifOutputOptions): string[];
|
|
|
358
494
|
*/
|
|
359
495
|
declare function framesToMp4Wasm(frames: Uint8Array[], audio: Uint8Array | null, options?: VideoExportOptions): Promise<EncoderResult>;
|
|
360
496
|
|
|
361
|
-
export { type AudioTimelineClip, type ComputeAudioTimelineOptions, type EncoderResult, FFMPEG_WASM_SETUP_HINT, type FfmpegWasmLoadConfig, type GifDither, type GifFilterOptions, type GifOutputOptions, ORIENTATION_DIMENSIONS, QUALITY_PRESETS, type QualityPreset, type RenderHtmlOptions, type VideoExportOptions, type VideoOrientation, type VideoQuality, audioBitrateArg, bitrateForQuality, computeAudioTimeline, ffmpegAudioMuxArgs, ffmpegGifFilterGraph, ffmpegGifOutputArgs, ffmpegGifPaletteApplicationArgs, ffmpegGifPaletteApplicationGraph, ffmpegGifPaletteGenerationFilter, ffmpegVideoQualityArgs, framesToMp4Wasm, generateRenderHtml, resolveDimensions, resolveFfmpegWasmLoad, validateVideoExportOptions };
|
|
497
|
+
export { type AudioTimelineClip, type ComputeAudioTimelineOptions, DASHBOARD_RESOLUTIONS, DEFAULT_DASHBOARD_RESOLUTION, type DashboardResolutionId, type DashboardResolutionPreset, type EncoderResult, FFMPEG_WASM_SETUP_HINT, type FfmpegWasmLoadConfig, type GifDither, type GifFilterOptions, type GifOutputOptions, MAX_DASHBOARD_IMAGE_DIMENSION, MAX_DASHBOARD_IMAGE_PIXELS, MIN_DASHBOARD_IMAGE_DIMENSION, ORIENTATION_DIMENSIONS, QUALITY_PRESETS, type QualityPreset, type RenderHtmlOptions, type ResolveDashboardDimensionsInput, type ResolvedDashboardDimensions, type VideoExportOptions, type VideoOrientation, type VideoQuality, audioBitrateArg, bitrateForQuality, computeAudioTimeline, dashboardFamilyForDimensions, ffmpegAudioMuxArgs, ffmpegGifFilterGraph, ffmpegGifOutputArgs, ffmpegGifPaletteApplicationArgs, ffmpegGifPaletteApplicationGraph, ffmpegGifPaletteGenerationFilter, ffmpegVideoQualityArgs, framesToMp4Wasm, generateRenderHtml, resolveDashboardDimensions, resolveDimensions, resolveFfmpegWasmLoad, validateDashboardImageDimensions, validateVideoExportOptions };
|
package/dist/index.js
CHANGED
|
@@ -180,7 +180,9 @@ function generateRenderHtml(doc, options) {
|
|
|
180
180
|
pipSize,
|
|
181
181
|
pipShape,
|
|
182
182
|
pipPosition,
|
|
183
|
-
animationsEnabled = true
|
|
183
|
+
animationsEnabled = true,
|
|
184
|
+
displayMode = "slideshow",
|
|
185
|
+
dashboard
|
|
184
186
|
} = options;
|
|
185
187
|
const imageMap = {};
|
|
186
188
|
if (images) {
|
|
@@ -206,7 +208,8 @@ function generateRenderHtml(doc, options) {
|
|
|
206
208
|
videoPresentation ? ` videoPresentation: ${JSON.stringify(videoPresentation)}` : null,
|
|
207
209
|
pipSize ? ` pipSize: ${JSON.stringify(pipSize)}` : null,
|
|
208
210
|
pipShape ? ` pipShape: ${JSON.stringify(pipShape)}` : null,
|
|
209
|
-
pipPosition ? ` pipPosition: ${JSON.stringify(pipPosition)}` : null
|
|
211
|
+
pipPosition ? ` pipPosition: ${JSON.stringify(pipPosition)}` : null,
|
|
212
|
+
displayMode === "dashboard" ? ` dashboard: ${escapeForScript(JSON.stringify(dashboard ?? {}))}` : null
|
|
210
213
|
].filter((line) => line !== null);
|
|
211
214
|
return `<!DOCTYPE html>
|
|
212
215
|
<html lang="en">
|
|
@@ -230,7 +233,7 @@ html,body{margin:0;padding:0;width:${width}px;height:${height}px;overflow:hidden
|
|
|
230
233
|
var audio = ${audioMapJson === "null" ? "null" : "JSON.parse(" + JSON.stringify(audioMapJson) + ")"};
|
|
231
234
|
var root = document.getElementById("squisq-root");
|
|
232
235
|
SquisqPlayer.mount(root, doc, {
|
|
233
|
-
mode: "slideshow",
|
|
236
|
+
mode: ${JSON.stringify(displayMode === "dashboard" ? "dashboard" : "slideshow")},
|
|
234
237
|
images: images,
|
|
235
238
|
audio: audio,
|
|
236
239
|
autoPlay: false,
|
|
@@ -244,6 +247,83 @@ ${playerOptionLines.join(",\n")}
|
|
|
244
247
|
</html>`;
|
|
245
248
|
}
|
|
246
249
|
|
|
250
|
+
// src/dashboardImage.ts
|
|
251
|
+
import { VIEWPORT_PRESETS } from "@bendyline/squisq/schemas";
|
|
252
|
+
var DASHBOARD_RESOLUTIONS = [
|
|
253
|
+
{ id: "hd", label: "HD 1280\xD7720", width: 1280, height: 720, family: "landscape" },
|
|
254
|
+
{ id: "fhd", label: "Full HD 1920\xD71080", width: 1920, height: 1080, family: "landscape" },
|
|
255
|
+
{ id: "4k", label: "4K UHD 3840\xD72160", width: 3840, height: 2160, family: "landscape" },
|
|
256
|
+
{ id: "square", label: "Square 1080\xD71080", width: 1080, height: 1080, family: "square" },
|
|
257
|
+
{ id: "square-2k", label: "Square 2160\xD72160", width: 2160, height: 2160, family: "square" },
|
|
258
|
+
{ id: "portrait", label: "Portrait 1080\xD71920", width: 1080, height: 1920, family: "portrait" },
|
|
259
|
+
{
|
|
260
|
+
id: "portrait-4k",
|
|
261
|
+
label: "Portrait 4K 2160\xD73840",
|
|
262
|
+
width: 2160,
|
|
263
|
+
height: 3840,
|
|
264
|
+
family: "portrait"
|
|
265
|
+
},
|
|
266
|
+
{ id: "standard", label: "4:3 1440\xD71080", width: 1440, height: 1080, family: "standard" }
|
|
267
|
+
];
|
|
268
|
+
var DEFAULT_DASHBOARD_RESOLUTION = "fhd";
|
|
269
|
+
var MIN_DASHBOARD_IMAGE_DIMENSION = 64;
|
|
270
|
+
var MAX_DASHBOARD_IMAGE_DIMENSION = 7680;
|
|
271
|
+
var MAX_DASHBOARD_IMAGE_PIXELS = 33177600;
|
|
272
|
+
function validateDashboardImageDimensions(width, height) {
|
|
273
|
+
if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height)) {
|
|
274
|
+
return "Width and height must be whole pixel counts.";
|
|
275
|
+
}
|
|
276
|
+
if (width < MIN_DASHBOARD_IMAGE_DIMENSION || height < MIN_DASHBOARD_IMAGE_DIMENSION) {
|
|
277
|
+
return `Each dimension must be at least ${MIN_DASHBOARD_IMAGE_DIMENSION} pixels.`;
|
|
278
|
+
}
|
|
279
|
+
if (width > MAX_DASHBOARD_IMAGE_DIMENSION || height > MAX_DASHBOARD_IMAGE_DIMENSION) {
|
|
280
|
+
return `Each dimension must be at most ${MAX_DASHBOARD_IMAGE_DIMENSION} pixels.`;
|
|
281
|
+
}
|
|
282
|
+
if (width * height > MAX_DASHBOARD_IMAGE_PIXELS) {
|
|
283
|
+
return `The image may not exceed ${MAX_DASHBOARD_IMAGE_PIXELS.toLocaleString("en-US")} total pixels.`;
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
function dashboardFamilyForDimensions(width, height) {
|
|
288
|
+
const aspect = width / Math.max(1, height);
|
|
289
|
+
let best = "landscape";
|
|
290
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
291
|
+
for (const family of Object.keys(VIEWPORT_PRESETS)) {
|
|
292
|
+
const preset = VIEWPORT_PRESETS[family];
|
|
293
|
+
const distance = Math.abs(Math.log(aspect / (preset.width / preset.height)));
|
|
294
|
+
if (distance < bestDistance) {
|
|
295
|
+
bestDistance = distance;
|
|
296
|
+
best = family;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return best;
|
|
300
|
+
}
|
|
301
|
+
function resolveDashboardDimensions(input = {}) {
|
|
302
|
+
const hasCustom = input.width !== void 0 || input.height !== void 0;
|
|
303
|
+
if (hasCustom && input.resolution !== void 0) {
|
|
304
|
+
throw new RangeError("Pass either a resolution preset or custom width/height, not both.");
|
|
305
|
+
}
|
|
306
|
+
if (hasCustom) {
|
|
307
|
+
if (input.width === void 0 || input.height === void 0) {
|
|
308
|
+
throw new RangeError("Custom dimensions require both width and height.");
|
|
309
|
+
}
|
|
310
|
+
const problem = validateDashboardImageDimensions(input.width, input.height);
|
|
311
|
+
if (problem) throw new RangeError(problem);
|
|
312
|
+
return {
|
|
313
|
+
width: input.width,
|
|
314
|
+
height: input.height,
|
|
315
|
+
family: dashboardFamilyForDimensions(input.width, input.height)
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
const id = input.resolution ?? DEFAULT_DASHBOARD_RESOLUTION;
|
|
319
|
+
const preset = DASHBOARD_RESOLUTIONS.find((entry) => entry.id === id);
|
|
320
|
+
if (!preset) {
|
|
321
|
+
const known = DASHBOARD_RESOLUTIONS.map((entry) => entry.id).join(", ");
|
|
322
|
+
throw new RangeError(`Unknown resolution preset "${id}". Valid presets: ${known}.`);
|
|
323
|
+
}
|
|
324
|
+
return { width: preset.width, height: preset.height, family: preset.family };
|
|
325
|
+
}
|
|
326
|
+
|
|
247
327
|
// src/ffmpegArgs.ts
|
|
248
328
|
function ffmpegVideoQualityArgs(quality) {
|
|
249
329
|
const preset = QUALITY_PRESETS[quality] ?? QUALITY_PRESETS.normal;
|
|
@@ -413,12 +493,18 @@ async function framesToMp4Wasm(frames, audio, options = {}) {
|
|
|
413
493
|
}
|
|
414
494
|
}
|
|
415
495
|
export {
|
|
496
|
+
DASHBOARD_RESOLUTIONS,
|
|
497
|
+
DEFAULT_DASHBOARD_RESOLUTION,
|
|
416
498
|
FFMPEG_WASM_SETUP_HINT,
|
|
499
|
+
MAX_DASHBOARD_IMAGE_DIMENSION,
|
|
500
|
+
MAX_DASHBOARD_IMAGE_PIXELS,
|
|
501
|
+
MIN_DASHBOARD_IMAGE_DIMENSION,
|
|
417
502
|
ORIENTATION_DIMENSIONS,
|
|
418
503
|
QUALITY_PRESETS,
|
|
419
504
|
audioBitrateArg,
|
|
420
505
|
bitrateForQuality,
|
|
421
506
|
computeAudioTimeline,
|
|
507
|
+
dashboardFamilyForDimensions,
|
|
422
508
|
fetchFile,
|
|
423
509
|
ffmpegAudioMuxArgs,
|
|
424
510
|
ffmpegGifFilterGraph,
|
|
@@ -429,7 +515,9 @@ export {
|
|
|
429
515
|
ffmpegVideoQualityArgs,
|
|
430
516
|
framesToMp4Wasm,
|
|
431
517
|
generateRenderHtml,
|
|
518
|
+
resolveDashboardDimensions,
|
|
432
519
|
resolveDimensions,
|
|
433
520
|
resolveFfmpegWasmLoad,
|
|
521
|
+
validateDashboardImageDimensions,
|
|
434
522
|
validateVideoExportOptions
|
|
435
523
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-video",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Cross-runtime video and animated-GIF helpers with browser-based ffmpeg.wasm encoding for Squisq documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"typecheck": "tsc --noEmit"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@bendyline/squisq": "2.
|
|
51
|
+
"@bendyline/squisq": "2.8.0",
|
|
52
52
|
"@ffmpeg/ffmpeg": "0.12.15",
|
|
53
53
|
"@ffmpeg/util": "0.12.2"
|
|
54
54
|
},
|