@bendyline/squisq-cli 2.8.7 → 2.8.9
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/api.js
CHANGED
|
@@ -74,10 +74,15 @@ async function detectFfmpegDetailed(signal) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// src/util/audioMix.ts
|
|
77
|
+
import { buildMediaRenderIndex, mediaClipRenderKey } from "@bendyline/squisq/mediaEdit";
|
|
77
78
|
import { computeAudioTimeline } from "@bendyline/squisq-video";
|
|
78
79
|
async function buildMixedAudioTrack(doc, container, ffmpegPath, coverPreRoll, signal) {
|
|
79
80
|
signal?.throwIfAborted();
|
|
80
|
-
const
|
|
81
|
+
const processedAudio = await processedAudioLookup(container);
|
|
82
|
+
const timeline = computeAudioTimeline(doc, coverPreRoll, {
|
|
83
|
+
includeVideoAudio: false,
|
|
84
|
+
...processedAudio ? { processedAudio } : {}
|
|
85
|
+
});
|
|
81
86
|
if (timeline.length === 0) return null;
|
|
82
87
|
const bytesBySrc = /* @__PURE__ */ new Map();
|
|
83
88
|
const readSrc = async (src) => {
|
|
@@ -97,6 +102,38 @@ async function buildMixedAudioTrack(doc, container, ffmpegPath, coverPreRoll, si
|
|
|
97
102
|
if (usable.length === 0) return null;
|
|
98
103
|
return mixTimelineClips(ffmpegPath, usable, signal);
|
|
99
104
|
}
|
|
105
|
+
async function processedAudioLookup(container) {
|
|
106
|
+
const entries = await container.listFiles();
|
|
107
|
+
const index = buildMediaRenderIndex(entries.map((e) => ({ name: e.path, size: e.size })));
|
|
108
|
+
if (index.size === 0) return null;
|
|
109
|
+
return (clip) => {
|
|
110
|
+
const key = mediaClipRenderKey(clip);
|
|
111
|
+
return key ? index.get(key)?.path : void 0;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function clipFilterChain(clip) {
|
|
115
|
+
const start = Math.max(0, clip.sourceInSec);
|
|
116
|
+
const duration = Math.max(0, clip.durationSec);
|
|
117
|
+
const delayMs = Math.max(0, Math.round(clip.startSec * 1e3));
|
|
118
|
+
const parts = [`atrim=start=${start}:end=${start + duration}`, "asetpts=PTS-STARTPTS"];
|
|
119
|
+
if (clip.gainDb) parts.push(`volume=${clip.gainDb}dB`);
|
|
120
|
+
let fadeIn = Math.max(0, clip.fadeInSec ?? 0);
|
|
121
|
+
let fadeOut = Math.max(0, clip.fadeOutSec ?? 0);
|
|
122
|
+
if (fadeIn + fadeOut > duration && duration > 0) {
|
|
123
|
+
const scale = duration / (fadeIn + fadeOut);
|
|
124
|
+
fadeIn *= scale;
|
|
125
|
+
fadeOut *= scale;
|
|
126
|
+
}
|
|
127
|
+
if (fadeIn > 0) parts.push(`afade=t=in:st=0:d=${round3(fadeIn)}`);
|
|
128
|
+
if (fadeOut > 0) {
|
|
129
|
+
parts.push(`afade=t=out:st=${round3(duration - fadeOut)}:d=${round3(fadeOut)}`);
|
|
130
|
+
}
|
|
131
|
+
parts.push(`adelay=${delayMs}|${delayMs}`);
|
|
132
|
+
return parts.join(",");
|
|
133
|
+
}
|
|
134
|
+
function round3(n) {
|
|
135
|
+
return Math.round(n * 1e3) / 1e3;
|
|
136
|
+
}
|
|
100
137
|
async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
101
138
|
const { writeFile: writeFile2, readFile: readFile4, mkdir: mkdir2, rm: rm2 } = await import("fs/promises");
|
|
102
139
|
const { join: join3 } = await import("path");
|
|
@@ -105,7 +142,6 @@ async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
|
105
142
|
signal?.throwIfAborted();
|
|
106
143
|
const workDir = join3(tmpdir2(), `squisq-audio-mix-${randomBytes2(8).toString("hex")}`);
|
|
107
144
|
await mkdir2(workDir, { recursive: true });
|
|
108
|
-
const ms = (s) => Math.max(0, Math.round(s * 1e3));
|
|
109
145
|
try {
|
|
110
146
|
signal?.throwIfAborted();
|
|
111
147
|
const inputs = [];
|
|
@@ -117,12 +153,7 @@ async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
|
117
153
|
await writeFile2(p, new Uint8Array(buffer));
|
|
118
154
|
signal?.throwIfAborted();
|
|
119
155
|
const i = inputs.push(p) - 1;
|
|
120
|
-
|
|
121
|
-
const start = Math.max(0, clip.sourceInSec);
|
|
122
|
-
const end = start + Math.max(0, clip.durationSec);
|
|
123
|
-
filters.push(
|
|
124
|
-
`[${i}:a]atrim=start=${start}:end=${end},asetpts=PTS-STARTPTS,adelay=${delayMs}|${delayMs}[a${i}]`
|
|
125
|
-
);
|
|
156
|
+
filters.push(`[${i}:a]${clipFilterChain(clip)}[a${i}]`);
|
|
126
157
|
labels.push(`[a${i}]`);
|
|
127
158
|
}
|
|
128
159
|
const graph = `${filters.join(";")};${labels.join("")}amix=inputs=${labels.length}:normalize=0:dropout_transition=0[aout]`;
|
|
@@ -71,10 +71,15 @@ async function detectFfmpegDetailed(signal) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// src/util/audioMix.ts
|
|
74
|
+
import { buildMediaRenderIndex, mediaClipRenderKey } from "@bendyline/squisq/mediaEdit";
|
|
74
75
|
import { computeAudioTimeline } from "@bendyline/squisq-video";
|
|
75
76
|
async function buildMixedAudioTrack(doc, container, ffmpegPath, coverPreRoll, signal) {
|
|
76
77
|
signal?.throwIfAborted();
|
|
77
|
-
const
|
|
78
|
+
const processedAudio = await processedAudioLookup(container);
|
|
79
|
+
const timeline = computeAudioTimeline(doc, coverPreRoll, {
|
|
80
|
+
includeVideoAudio: false,
|
|
81
|
+
...processedAudio ? { processedAudio } : {}
|
|
82
|
+
});
|
|
78
83
|
if (timeline.length === 0) return null;
|
|
79
84
|
const bytesBySrc = /* @__PURE__ */ new Map();
|
|
80
85
|
const readSrc = async (src) => {
|
|
@@ -94,6 +99,38 @@ async function buildMixedAudioTrack(doc, container, ffmpegPath, coverPreRoll, si
|
|
|
94
99
|
if (usable.length === 0) return null;
|
|
95
100
|
return mixTimelineClips(ffmpegPath, usable, signal);
|
|
96
101
|
}
|
|
102
|
+
async function processedAudioLookup(container) {
|
|
103
|
+
const entries = await container.listFiles();
|
|
104
|
+
const index = buildMediaRenderIndex(entries.map((e) => ({ name: e.path, size: e.size })));
|
|
105
|
+
if (index.size === 0) return null;
|
|
106
|
+
return (clip) => {
|
|
107
|
+
const key = mediaClipRenderKey(clip);
|
|
108
|
+
return key ? index.get(key)?.path : void 0;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function clipFilterChain(clip) {
|
|
112
|
+
const start = Math.max(0, clip.sourceInSec);
|
|
113
|
+
const duration = Math.max(0, clip.durationSec);
|
|
114
|
+
const delayMs = Math.max(0, Math.round(clip.startSec * 1e3));
|
|
115
|
+
const parts = [`atrim=start=${start}:end=${start + duration}`, "asetpts=PTS-STARTPTS"];
|
|
116
|
+
if (clip.gainDb) parts.push(`volume=${clip.gainDb}dB`);
|
|
117
|
+
let fadeIn = Math.max(0, clip.fadeInSec ?? 0);
|
|
118
|
+
let fadeOut = Math.max(0, clip.fadeOutSec ?? 0);
|
|
119
|
+
if (fadeIn + fadeOut > duration && duration > 0) {
|
|
120
|
+
const scale = duration / (fadeIn + fadeOut);
|
|
121
|
+
fadeIn *= scale;
|
|
122
|
+
fadeOut *= scale;
|
|
123
|
+
}
|
|
124
|
+
if (fadeIn > 0) parts.push(`afade=t=in:st=0:d=${round3(fadeIn)}`);
|
|
125
|
+
if (fadeOut > 0) {
|
|
126
|
+
parts.push(`afade=t=out:st=${round3(duration - fadeOut)}:d=${round3(fadeOut)}`);
|
|
127
|
+
}
|
|
128
|
+
parts.push(`adelay=${delayMs}|${delayMs}`);
|
|
129
|
+
return parts.join(",");
|
|
130
|
+
}
|
|
131
|
+
function round3(n) {
|
|
132
|
+
return Math.round(n * 1e3) / 1e3;
|
|
133
|
+
}
|
|
97
134
|
async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
98
135
|
const { writeFile: writeFile2, readFile: readFile4, mkdir: mkdir2, rm: rm2 } = await import("fs/promises");
|
|
99
136
|
const { join: join3 } = await import("path");
|
|
@@ -102,7 +139,6 @@ async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
|
102
139
|
signal?.throwIfAborted();
|
|
103
140
|
const workDir = join3(tmpdir2(), `squisq-audio-mix-${randomBytes2(8).toString("hex")}`);
|
|
104
141
|
await mkdir2(workDir, { recursive: true });
|
|
105
|
-
const ms = (s) => Math.max(0, Math.round(s * 1e3));
|
|
106
142
|
try {
|
|
107
143
|
signal?.throwIfAborted();
|
|
108
144
|
const inputs = [];
|
|
@@ -114,12 +150,7 @@ async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
|
114
150
|
await writeFile2(p, new Uint8Array(buffer));
|
|
115
151
|
signal?.throwIfAborted();
|
|
116
152
|
const i = inputs.push(p) - 1;
|
|
117
|
-
|
|
118
|
-
const start = Math.max(0, clip.sourceInSec);
|
|
119
|
-
const end = start + Math.max(0, clip.durationSec);
|
|
120
|
-
filters.push(
|
|
121
|
-
`[${i}:a]atrim=start=${start}:end=${end},asetpts=PTS-STARTPTS,adelay=${delayMs}|${delayMs}[a${i}]`
|
|
122
|
-
);
|
|
153
|
+
filters.push(`[${i}:a]${clipFilterChain(clip)}[a${i}]`);
|
|
123
154
|
labels.push(`[a${i}]`);
|
|
124
155
|
}
|
|
125
156
|
const graph = `${filters.join(";")};${labels.join("")}amix=inputs=${labels.length}:normalize=0:dropout_transition=0[aout]`;
|
|
@@ -331,7 +362,7 @@ function mp4Format() {
|
|
|
331
362
|
const animationsEnabled = typeof mp4Opts.animationsEnabled === "boolean" ? mp4Opts.animationsEnabled : MP4_DEFAULTS.animationsEnabled;
|
|
332
363
|
const outputPath = join(tmpdir(), `squisq-mp4-${randomBytes(8).toString("hex")}.mp4`);
|
|
333
364
|
try {
|
|
334
|
-
const { renderDocToMp4: renderDocToMp42 } = await import("./api-
|
|
365
|
+
const { renderDocToMp4: renderDocToMp42 } = await import("./api-QLYK5AQE.js");
|
|
335
366
|
await renderDocToMp42(input.doc, input.container, {
|
|
336
367
|
outputPath,
|
|
337
368
|
fps,
|
|
@@ -372,7 +403,7 @@ function gifFormat() {
|
|
|
372
403
|
const defaultHeight = portrait ? GIF_DEFAULTS.width : GIF_DEFAULTS.height;
|
|
373
404
|
const outputPath = join(tmpdir(), `squisq-gif-${randomBytes(8).toString("hex")}.gif`);
|
|
374
405
|
try {
|
|
375
|
-
const { renderDocToGif: renderDocToGif2 } = await import("./api-
|
|
406
|
+
const { renderDocToGif: renderDocToGif2 } = await import("./api-QLYK5AQE.js");
|
|
376
407
|
const result = await renderDocToGif2(input.doc, input.container, {
|
|
377
408
|
outputPath,
|
|
378
409
|
fps: typeof gifOpts.fps === "number" ? gifOpts.fps : GIF_DEFAULTS.fps,
|
|
@@ -415,7 +446,7 @@ function pngFormat() {
|
|
|
415
446
|
async exportDoc(input, options) {
|
|
416
447
|
options.signal?.throwIfAborted();
|
|
417
448
|
const pngOpts = options.formatOptions?.png ?? {};
|
|
418
|
-
const { renderDocToDashboardPng: renderDocToDashboardPng2 } = await import("./api-
|
|
449
|
+
const { renderDocToDashboardPng: renderDocToDashboardPng2 } = await import("./api-QLYK5AQE.js");
|
|
419
450
|
const result = await renderDocToDashboardPng2(input.doc, input.container, {
|
|
420
451
|
resolution: pngOpts.resolution,
|
|
421
452
|
width: pngOpts.width,
|