polotno-node 2.9.38 → 2.9.39

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 (2) hide show
  1. package/package.json +1 -1
  2. package/video-parallel.js +63 -52
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polotno-node",
3
- "version": "2.9.38",
3
+ "version": "2.9.39",
4
4
  "description": "Polotno workflow from NodeJS",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/video-parallel.js CHANGED
@@ -276,64 +276,75 @@ module.exports.jsonToVideo = async function jsonToVideo(
276
276
  .inputFPS(fps)
277
277
  .videoCodec('libx264')
278
278
  .outputOptions('-pix_fmt yuv420p')
279
- .videoFilters('scale=trunc(iw/2)*2:trunc(ih/2)*2'); // Ensure dimensions are divisible by 2
279
+ .videoFilters('scale=trunc(iw/2)*2:trunc(ih/2)*2');
280
280
 
281
281
  const audioInputs = [];
282
282
 
283
- // Add each audio segment as a separate input
284
- inputs.forEach((input, index) => {
285
- const inputStartSec = (input.inputStartTime / 1000).toFixed(3);
286
- const inputDurationSec = (
287
- (input.inputEndTime - input.inputStartTime) /
288
- 1000
289
- ).toFixed(3);
290
- const outputOffsetMs = Math.round(input.outputStartTime);
291
- console.log({ outputOffsetMs });
292
- const volume = input.volume !== undefined ? input.volume : 1;
283
+ // Use Promise.all to handle async ffprobe calls
284
+ Promise.all(
285
+ inputs.map((input, index) => {
286
+ return new Promise((resolveProbe) => {
287
+ const inputStartSec = (input.inputStartTime / 1000).toFixed(3);
288
+ const inputDurationSec = (
289
+ (input.inputEndTime - input.inputStartTime) /
290
+ 1000
291
+ ).toFixed(3);
292
+ const outputOffsetMs = Math.round(input.outputStartTime);
293
+ const volume = input.volume !== undefined ? input.volume : 1;
294
+
295
+ ffmpegCmd
296
+ .input(input.mp4File)
297
+ .inputOptions([`-ss ${inputStartSec}`, `-t ${inputDurationSec}`]);
298
+
299
+ ffmpeg.ffprobe(input.mp4File, (err, metadata) => {
300
+ if (
301
+ !err &&
302
+ metadata.streams.some((stream) => stream.codec_type === 'audio')
303
+ ) {
304
+ audioInputs.push({
305
+ index: index + 1,
306
+ delay: outputOffsetMs,
307
+ volume: volume,
308
+ });
309
+ }
310
+ resolveProbe();
311
+ });
312
+ });
313
+ })
314
+ ).then(() => {
315
+ // Map the video stream from the image sequence
316
+ ffmpegCmd.outputOptions(['-map 0:v']);
317
+
318
+ // Prepare audio filter complex only if there are audio inputs
319
+ if (audioInputs.length > 0) {
320
+ const audioFilters = audioInputs.map(
321
+ (input, i) =>
322
+ `[${input.index}:a]adelay=${input.delay}|${input.delay},volume=${input.volume}[a${i}]`
323
+ );
324
+ const audioMerge = audioInputs.map((_, i) => `[a${i}]`).join('');
325
+ const filterComplex = `${audioFilters.join(
326
+ ';'
327
+ )};${audioMerge}amix=inputs=${
328
+ audioInputs.length
329
+ }:duration=longest[aout]`;
330
+
331
+ ffmpegCmd.complexFilter(filterComplex);
332
+ ffmpegCmd.outputOptions(['-map [aout]']);
333
+ }
293
334
 
294
335
  ffmpegCmd
295
- .input(input.mp4File)
296
- .inputOptions([`-ss ${inputStartSec}`, `-t ${inputDurationSec}`]);
297
-
298
- audioInputs.push({
299
- index: index + 1, // +1 because the first input is the image sequence
300
- delay: outputOffsetMs,
301
- volume: volume,
302
- });
336
+ .format(format)
337
+ .audioCodec('aac')
338
+ .on('start', (command) => console.log('FFmpeg command:', command))
339
+ .on('end', () => resolve())
340
+ .on('error', (err, stdout, stderr) => {
341
+ console.error('Error:', err.message);
342
+ console.error('stdout:', stdout);
343
+ console.error('stderr:', stderr);
344
+ reject(err);
345
+ })
346
+ .save(attrs.out);
303
347
  });
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
348
  });
338
349
  } catch (e) {
339
350
  throw e;