1

I wanna make an automatic video generation thingy. The current way I'm doing that is using the editly node library, which needs an object. Now I wanna get music/audio from a folder, then I wanna get the duration of each file so I can dynamically clip it together.

The problem is I wanna return an object to the Array.map function, that object is edited inside a callback. BUT when I return the object (that should in my head be edited) it returns the "default".

Here is my code so far:

var editly = require("editly");
var duration = require("mp3d");
var fs = require("fs");

var idx = 0;

var editObj = {
    enableFfmpegLog: true,
    outPath: "./vids/"+idx+".mp4",
    width: 1280,
    height: 720,
    fps: 24,
    defaults: {
        transition: null
    },
    clips: [
        {
            layers: [
                {
                    type: "image",
                    path: "./bgs/"+idx+".jpg"
                }
            ]
        }
    ],
    audioTracks: fs.readdirSync("./audio/"+idx+"/").map((song, index, arr) => {
        if(index == 0) {
            var obj = { path: "./audio/"+idx+"/"+song, start: 0 };
            return obj;
        } else {
            var obj = { path: "./audio/"+idx+"/"+song };
            duration("./audio/"+idx+"/"+arr[index-1], (err, result) => {
                obj.start = Number((result / 1000).toFixed(0));
            });
            return obj;
        }
    })
};

(async function() {
    console.log(editObj);
    // await editly(editObj).then(console.log).catch(console.error);
})();

UPDATE: Here's the new and working code:

var editly = require("editly");
var duration = require("mp3d");
var fs = require("fs");

(async function() {
    var idx = 0;

    var editObj = {
        enableFfmpegLog: true,
        outPath: "./vids/"+idx+".mp4",
        width: 1280,
        height: 720,
        fps: 24,
        defaults: {
            transition: null
        },
        clips: [
            {
                layers: [
                    {
                        type: "image",
                        path: "./bgs/"+idx+".jpg"
                    }
                ]
            }
        ],
        audioTracks: await Promise.all(fs.readdirSync("./audio/"+idx+"/").map(async(song, index, arr) => {
            if(index == 0) {
                var obj = { path: "./audio/"+idx+"/"+song, start: 0 };
                return obj;
            } else {
                var obj = { path: "./audio/"+idx+"/"+song };
                await duration("./audio/"+idx+"/"+arr[index-1], (err, result) => {
                    obj.start = Number((result / 1000).toFixed(0));
                });
                return obj;
            }
        }))
    };

    console.log(editObj);
    // await editly(editObj).then(console.log).catch(console.error);
})();
  • Jay Garzon's got the idea: your call to the `duration` function in the else branch is *asynchronous* -- it doesn't finish in time and modify `obj.start` before it returns, though his solution won't quite work. See https://stackoverflow.com/questions/40140149/use-async-await-with-array-map -- You'll need to call `Promise.all` as well to get a plain array back. – Andrew Li Dec 16 '20 at 01:57
  • Thanks, @AndrewLi -- This worked! :) – Spark Accounts Dec 16 '20 at 11:37

1 Answers1

0

I believe what is going on is that in your else branch where you are mutating the object inside the call back. It is not executing before you return it. Since js is async you may want to use a promise and await the result of the mutation before you return.

Jay Garzon
  • 176
  • 1
  • 7