Search code examples
javascriptnode.jsdiscord.jsyoutube-javascript-apiytdl

Promise Rejection when fetching video url in discord.js and using the Youtube Api


Promises with Youtube API Requests

I'm currently making a discord bot in discord.js, and I am trying to make a music function. Now I know this function works as I have tested it by giving it a set video URL. I recently tried implementing support for user input (e.g. $play vfx artists react, using the youtube API, but I have been met with error messages when I try to retrieve the url.

I know the error is related to promises, as the API hasn't actually retrieved the data when I try to retrieve the title and URL. I am not very good with promises and my attempts so far have been futile, so any help would be greatly appreciated.

Code ->

Relevant npm modules.

const YouTube = require('youtube-node');
var youTube = new YouTube();
youTube.setKey('MyKey');

This is the function where I get the error, I have heard of promises before, but every time I use one I either log a 'pending promise' or I am met with an error.

function addVideo(term) {
    youTube.search(term, 1,
        function (error, result) {
            return [result.items[0].snippet.title, result.items[0].id.videoId];
        });
}

I then call this function here,

searchResult = addVideo(args.join(' '))
song = {
    title: searchResult[0],
    url: searchResult[1],
};

Error is found on line title: searchResult[0]

(node:4) UnhandledPromiseRejectionWarning: TypeError: Cannot read property '1' of undefined

For those interested, code can be found here I am aware that it is a trainwreck atm, plan on converting it to modules using export / import stuff later.


Solution

  • returning from youTube.search()'s callback does not do what you think it does. Because Javascript is asynchronous. addVideo returns to its caller long before the search is complete.

    You need to do something like this instead to handle the result from inside your callback function.

    function addVideo(term) {
        youTube.search(term, 1,
            function (error, result) {
                if (error) throw new Error (error);
                song = {
                  title: result[0],
                    url: result[1],
                };
                /* do whatever you wanted to do with song */
            }
        );
    }
    

    If you figure out how to wrap the youTube API in a promise, you'll then be able to use async functions. That will make your logic easier to read. But explaining all that is beyond the scope of a Stack Overflow answer.