@bendyline/squisq-video 2.2.5 → 2.2.7
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 +19 -4
- package/dist/index.js +41 -3
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -168,8 +168,9 @@ declare function resolveFfmpegWasmLoad(config: FfmpegWasmLoadConfig | undefined,
|
|
|
168
168
|
* concatenates the segment files in order.
|
|
169
169
|
* - Timed media clips (`block.media` + `doc.documentMedia`) are placed at
|
|
170
170
|
* their **absolute** doc-timeline positions via the shared
|
|
171
|
-
* `resolveMediaSchedule()` helper
|
|
172
|
-
*
|
|
171
|
+
* `resolveMediaSchedule()` helper, honouring each clip's trim window
|
|
172
|
+
* (`sourceIn` / `absoluteEnd - absoluteStart`). This includes the audio
|
|
173
|
+
* stream carried by scheduled video clips unless explicitly disabled.
|
|
173
174
|
* - Every start time is shifted by `coverPreRoll` so a cover pre-roll padding
|
|
174
175
|
* (silent leading frames) keeps audio in sync.
|
|
175
176
|
*/
|
|
@@ -184,6 +185,20 @@ interface AudioTimelineClip {
|
|
|
184
185
|
sourceInSec: number;
|
|
185
186
|
/** Played length in seconds (the trimmed window of the source). */
|
|
186
187
|
durationSec: number;
|
|
188
|
+
/**
|
|
189
|
+
* Video sources may legitimately contain no audio stream. The browser mixer
|
|
190
|
+
* probes them independently and skips only the silent ones. Omitted means an
|
|
191
|
+
* authored audio/narration source, whose decode failure remains an error.
|
|
192
|
+
*/
|
|
193
|
+
sourceKind?: 'video';
|
|
194
|
+
}
|
|
195
|
+
interface ComputeAudioTimelineOptions {
|
|
196
|
+
/**
|
|
197
|
+
* Include the audio stream carried by scheduled video clips. Defaults to
|
|
198
|
+
* true for composed MP4 export. Consumers that cannot demux video audio may
|
|
199
|
+
* explicitly retain the legacy audio-only behavior.
|
|
200
|
+
*/
|
|
201
|
+
includeVideoAudio?: boolean;
|
|
187
202
|
}
|
|
188
203
|
/**
|
|
189
204
|
* Flatten a doc's narration + timed-media audio into absolute-timed clips.
|
|
@@ -196,7 +211,7 @@ interface AudioTimelineClip {
|
|
|
196
211
|
* @param coverPreRoll - Leading silent padding (seconds) added ahead of every
|
|
197
212
|
* clip, matching the cover-slide pre-roll frames. Default 0.
|
|
198
213
|
*/
|
|
199
|
-
declare function computeAudioTimeline(doc: Doc, coverPreRoll?: number): AudioTimelineClip[];
|
|
214
|
+
declare function computeAudioTimeline(doc: Doc, coverPreRoll?: number, options?: ComputeAudioTimelineOptions): AudioTimelineClip[];
|
|
200
215
|
|
|
201
216
|
/**
|
|
202
217
|
* Render HTML Generation for Video Frame Capture
|
|
@@ -343,4 +358,4 @@ declare function ffmpegGifOutputArgs(options: GifOutputOptions): string[];
|
|
|
343
358
|
*/
|
|
344
359
|
declare function framesToMp4Wasm(frames: Uint8Array[], audio: Uint8Array | null, options?: VideoExportOptions): Promise<EncoderResult>;
|
|
345
360
|
|
|
346
|
-
export { type AudioTimelineClip, 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -65,8 +65,10 @@ function resolveFfmpegWasmLoad(config, context, defaults) {
|
|
|
65
65
|
|
|
66
66
|
// src/audioTimeline.ts
|
|
67
67
|
import { resolveMediaSchedule } from "@bendyline/squisq/schemas";
|
|
68
|
-
|
|
68
|
+
import { flattenRenderableBlocks, materializeBlockLayers } from "@bendyline/squisq/doc";
|
|
69
|
+
function computeAudioTimeline(doc, coverPreRoll = 0, options = {}) {
|
|
69
70
|
const preRoll = safeSeconds(coverPreRoll);
|
|
71
|
+
const includeVideoAudio = options.includeVideoAudio ?? true;
|
|
70
72
|
const clips = [];
|
|
71
73
|
let cursor = 0;
|
|
72
74
|
for (const seg of doc.audio?.segments ?? []) {
|
|
@@ -77,7 +79,7 @@ function computeAudioTimeline(doc, coverPreRoll = 0) {
|
|
|
77
79
|
cursor += durationSec;
|
|
78
80
|
}
|
|
79
81
|
for (const clip of resolveMediaSchedule(doc)) {
|
|
80
|
-
if (clip.kind !== "audio") continue;
|
|
82
|
+
if (clip.kind !== "audio" && !includeVideoAudio) continue;
|
|
81
83
|
if (!Number.isFinite(clip.absoluteStart) || !Number.isFinite(clip.absoluteEnd)) continue;
|
|
82
84
|
const durationSec = Math.max(0, clip.absoluteEnd - clip.absoluteStart);
|
|
83
85
|
if (durationSec <= 0 || !clip.src) continue;
|
|
@@ -85,11 +87,47 @@ function computeAudioTimeline(doc, coverPreRoll = 0) {
|
|
|
85
87
|
src: clip.src,
|
|
86
88
|
startSec: safeSeconds(clip.absoluteStart) + preRoll,
|
|
87
89
|
sourceInSec: safeSeconds(clip.sourceIn),
|
|
88
|
-
durationSec
|
|
90
|
+
durationSec,
|
|
91
|
+
...clip.kind === "video" ? { sourceKind: "video" } : {}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (includeVideoAudio) {
|
|
95
|
+
const blocks = flattenRenderableBlocks(doc.blocks);
|
|
96
|
+
const existing = new Set(clips.map(timelineKey));
|
|
97
|
+
blocks.forEach((block, blockIndex) => {
|
|
98
|
+
const materialized = materializeBlockLayers(block, {
|
|
99
|
+
blockIndex,
|
|
100
|
+
totalBlocks: blocks.length,
|
|
101
|
+
customTemplates: doc.customTemplates,
|
|
102
|
+
persistentLayers: false
|
|
103
|
+
});
|
|
104
|
+
for (const layer of materialized.layers) {
|
|
105
|
+
if (layer.type !== "video" || !layer.content.src) continue;
|
|
106
|
+
const startAt = safeSeconds(layer.content.startAt ?? 0);
|
|
107
|
+
const sourceInSec = safeSeconds(layer.content.clipStart);
|
|
108
|
+
const clipDuration = Math.max(0, layer.content.clipEnd - sourceInSec);
|
|
109
|
+
const blockRemainder = Math.max(0, safeSeconds(block.duration) - startAt);
|
|
110
|
+
const durationSec = layer.content.spillover ? clipDuration : Math.min(clipDuration, blockRemainder);
|
|
111
|
+
if (durationSec <= 0) continue;
|
|
112
|
+
const candidate = {
|
|
113
|
+
src: layer.content.src,
|
|
114
|
+
startSec: safeSeconds(block.startTime) + startAt + preRoll,
|
|
115
|
+
sourceInSec,
|
|
116
|
+
durationSec,
|
|
117
|
+
sourceKind: "video"
|
|
118
|
+
};
|
|
119
|
+
const key = timelineKey(candidate);
|
|
120
|
+
if (existing.has(key)) continue;
|
|
121
|
+
existing.add(key);
|
|
122
|
+
clips.push(candidate);
|
|
123
|
+
}
|
|
89
124
|
});
|
|
90
125
|
}
|
|
91
126
|
return clips;
|
|
92
127
|
}
|
|
128
|
+
function timelineKey(clip) {
|
|
129
|
+
return `${clip.src}\0${clip.startSec}\0${clip.sourceInSec}\0${clip.durationSec}`;
|
|
130
|
+
}
|
|
93
131
|
function safeSeconds(value) {
|
|
94
132
|
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
95
133
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-video",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.7",
|
|
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.4.
|
|
51
|
+
"@bendyline/squisq": "2.4.3",
|
|
52
52
|
"@ffmpeg/ffmpeg": "0.12.15",
|
|
53
53
|
"@ffmpeg/util": "0.12.2"
|
|
54
54
|
},
|