polotno-node 2.9.38 → 2.10.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/package.json +1 -1
- package/video-parallel.js +87 -52
package/package.json
CHANGED
package/video-parallel.js
CHANGED
|
@@ -116,6 +116,20 @@ module.exports.jsonToVideo = async function jsonToVideo(
|
|
|
116
116
|
})
|
|
117
117
|
);
|
|
118
118
|
|
|
119
|
+
const audios = [];
|
|
120
|
+
// download all audio tracks
|
|
121
|
+
await Promise.all(
|
|
122
|
+
(json.audios || []).map(async (audio) => {
|
|
123
|
+
const filename = Math.random().toString(36).substring(7);
|
|
124
|
+
const destination = path.join(tempFolder.name, filename);
|
|
125
|
+
await downloadVideo(audio.src, destination);
|
|
126
|
+
audios.push({
|
|
127
|
+
...audio,
|
|
128
|
+
file: destination,
|
|
129
|
+
});
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
|
|
119
133
|
const duration = json.pages.reduce((acc, page) => {
|
|
120
134
|
return acc + (page.duration ?? 5000);
|
|
121
135
|
}, 0);
|
|
@@ -276,64 +290,85 @@ module.exports.jsonToVideo = async function jsonToVideo(
|
|
|
276
290
|
.inputFPS(fps)
|
|
277
291
|
.videoCodec('libx264')
|
|
278
292
|
.outputOptions('-pix_fmt yuv420p')
|
|
279
|
-
.videoFilters('scale=trunc(iw/2)*2:trunc(ih/2)*2');
|
|
293
|
+
.videoFilters('scale=trunc(iw/2)*2:trunc(ih/2)*2');
|
|
280
294
|
|
|
281
295
|
const audioInputs = [];
|
|
282
296
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
297
|
+
// Use Promise.all to handle async ffprobe calls
|
|
298
|
+
Promise.all(
|
|
299
|
+
inputs.map((input, index) => {
|
|
300
|
+
return new Promise((resolveProbe) => {
|
|
301
|
+
const inputStartSec = (input.inputStartTime / 1000).toFixed(3);
|
|
302
|
+
const inputDurationSec = (
|
|
303
|
+
(input.inputEndTime - input.inputStartTime) /
|
|
304
|
+
1000
|
|
305
|
+
).toFixed(3);
|
|
306
|
+
const outputOffsetMs = Math.round(input.outputStartTime);
|
|
307
|
+
const volume = input.volume !== undefined ? input.volume : 1;
|
|
308
|
+
|
|
309
|
+
ffmpegCmd
|
|
310
|
+
.input(input.mp4File)
|
|
311
|
+
.inputOptions([`-ss ${inputStartSec}`, `-t ${inputDurationSec}`]);
|
|
312
|
+
|
|
313
|
+
ffmpeg.ffprobe(input.mp4File, (err, metadata) => {
|
|
314
|
+
if (
|
|
315
|
+
!err &&
|
|
316
|
+
metadata.streams.some((stream) => stream.codec_type === 'audio')
|
|
317
|
+
) {
|
|
318
|
+
audioInputs.push({
|
|
319
|
+
index: index + 1,
|
|
320
|
+
delay: outputOffsetMs,
|
|
321
|
+
volume: volume,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
resolveProbe();
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
})
|
|
328
|
+
).then(() => {
|
|
329
|
+
// Map the video stream from the image sequence
|
|
330
|
+
ffmpegCmd.outputOptions(['-map 0:v']);
|
|
331
|
+
|
|
332
|
+
// Prepare audio filter complex for both video and pure audio inputs
|
|
333
|
+
const allAudioInputs = [...audioInputs, ...audios];
|
|
334
|
+
if (allAudioInputs.length > 0) {
|
|
335
|
+
const audioFilters = allAudioInputs.map((input, i) => {
|
|
336
|
+
const inputIndex = audioInputs.includes(input)
|
|
337
|
+
? input.index
|
|
338
|
+
: audioInputs.length + i + 1;
|
|
339
|
+
return `[${inputIndex}:a]adelay=${input.delay}|${input.delay},volume=${input.volume},apad[a${i}]`;
|
|
340
|
+
});
|
|
341
|
+
const audioMerge = allAudioInputs.map((_, i) => `[a${i}]`).join('');
|
|
342
|
+
const filterComplex = `${audioFilters.join(
|
|
343
|
+
';'
|
|
344
|
+
)};${audioMerge}amix=inputs=${
|
|
345
|
+
allAudioInputs.length
|
|
346
|
+
}:dropout_transition=0,aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo,apad,atrim=0:${
|
|
347
|
+
duration / 1000
|
|
348
|
+
}[aout]`;
|
|
349
|
+
|
|
350
|
+
ffmpegCmd.complexFilter(filterComplex);
|
|
351
|
+
ffmpegCmd.outputOptions(['-map [aout]']);
|
|
352
|
+
|
|
353
|
+
// Add pure audio inputs
|
|
354
|
+
audios.forEach((audio) => {
|
|
355
|
+
ffmpegCmd.input(audio.file);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
293
358
|
|
|
294
359
|
ffmpegCmd
|
|
295
|
-
.
|
|
296
|
-
.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
360
|
+
.format(format)
|
|
361
|
+
.audioCodec('aac')
|
|
362
|
+
.on('start', (command) => console.log('FFmpeg command:', command))
|
|
363
|
+
.on('end', () => resolve())
|
|
364
|
+
.on('error', (err, stdout, stderr) => {
|
|
365
|
+
console.error('Error:', err.message);
|
|
366
|
+
console.error('stdout:', stdout);
|
|
367
|
+
console.error('stderr:', stderr);
|
|
368
|
+
reject(err);
|
|
369
|
+
})
|
|
370
|
+
.save(attrs.out);
|
|
303
371
|
});
|
|
304
|
-
|
|
305
|
-
// Map the video stream from the image sequence
|
|
306
|
-
ffmpegCmd.outputOptions(['-map 0:v']);
|
|
307
|
-
|
|
308
|
-
// Prepare audio filter complex
|
|
309
|
-
if (audioInputs.length > 0) {
|
|
310
|
-
const audioFilters = audioInputs.map(
|
|
311
|
-
(input, i) =>
|
|
312
|
-
`[${input.index}:a]adelay=${input.delay}|${input.delay},volume=${input.volume}[a${i}]`
|
|
313
|
-
);
|
|
314
|
-
const audioMerge = audioInputs.map((_, i) => `[a${i}]`).join('');
|
|
315
|
-
const filterComplex = `${audioFilters.join(
|
|
316
|
-
';'
|
|
317
|
-
)};${audioMerge}amix=inputs=${
|
|
318
|
-
audioInputs.length
|
|
319
|
-
}:duration=longest[aout]`;
|
|
320
|
-
|
|
321
|
-
ffmpegCmd.complexFilter(filterComplex);
|
|
322
|
-
ffmpegCmd.outputOptions(['-map [aout]']);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
ffmpegCmd
|
|
326
|
-
.format(format)
|
|
327
|
-
.audioCodec('aac')
|
|
328
|
-
.on('start', (command) => console.log('FFmpeg command:', command))
|
|
329
|
-
.on('end', () => resolve())
|
|
330
|
-
.on('error', (err, stdout, stderr) => {
|
|
331
|
-
console.error('Error:', err.message);
|
|
332
|
-
console.error('stdout:', stdout);
|
|
333
|
-
console.error('stderr:', stderr);
|
|
334
|
-
reject(err);
|
|
335
|
-
})
|
|
336
|
-
.save(attrs.out);
|
|
337
372
|
});
|
|
338
373
|
} catch (e) {
|
|
339
374
|
throw e;
|