javascriptnode.jsdiscord.js

Store video title as variable in YTDL-core?


How can I store the video title as a global variable?

ytdl.getBasicInfo(videoURL, function(err, info) {
  console.log(info.title)
});

I've looked through other questions/answers here, but I seem to always print the title successfully to console, but no matter how I define a variable, it is undefined.

Thanks so much for your help!

EDIT: This is current method I'm trying, as an example found from another user here:

var vidtitle;
function getTitleVideo (videoUrl){
   return new Promise ((resolve, reject) => {
      ytdl.getBasicInfo (videoUrl, (err, info) => {
            resolve (info.title)
      })
   })
}
vidtitle = getTitleVideo(`https://www.youtube.com/watch?v=${youtube_video_id}`);

Solution

  • getBasicInfo can be used with a callback or a promise.

    Using a callback:

    ytdl.getBasicInfo(videoURL, (err, info) => {
      // log any error
      if (err) return console.error(err);
      const {title} = info;
      /*
       * you must put all your code that uses the title here
       * if you try to make a global variable it may be undefined when you
       * run the rest of the synchronous code
       */
    });
    

    The const {title} = info; is a shorthand for const title = info.title;. You can read more about destructuring here.

    Using a promise with then:

    ytdl.getBasicInfo(videoURL)
      .then(({title}) => {
        // same as above you can only use the title here
      })
      // log any error
      .catch(console.error);
    

    ({title}) => {...} is also destructuring.

    Using a promise with await (can only be used in an async function):

    (async () => {
      try {
        const {title} = await ytdl.getBasicInfo(videoURL);
        // use title here
      } catch (err) {
        console.error(err);
      }
    })();
    

    Explanation

    getBasicInfo is an example of an asynchronous function. That is, it takes time for it to execute (it needs to fetch the video info online).

    Before ES2015 (ES6), people used callbacks to work with async functions. (Some people also used promise libraries like bluebird, but I won’t go into that here.) The callback function would execute once the async function has completed or has an error.

    For example, Node.js’ require('http').get still uses callbacks:

    const {get} = require('http');
    get('http://example.com', function (res) {
      console.log(`The status code was ${res.statusCode}.`;
    }).on('error', console.error);
    

    In this example, the callback does not take an error and instead get returns a ClientRequest, which has an error event.

    Another example using the deprecated request library (taken from the readme):

    const request = require('request');
    request('http://www.google.com', function (error, response, body) {
      console.error('error:', error); // Print the error if one occurred
      console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
      console.log('body:', body); // Print the HTML for the Google homepage.
    });
    

    In ES2015, promises were added (as well as arrow functions). For example, you could create functions that return a promise like this:

    // resolves after 500 ms
    const doSomething = () => new Promise(resolve => setTimeout(resolve, 500));
    
    // resolves with 'Hello, world!' after 500 ms
    const getString = () =>
      new Promise(resolve => setTimeout(resolve, 500, 'Hello, world!'));
    
    // rejects with the error 'Error!' after 500 ms
    const rejectingPromise = () =>
      new Promise((resolve, reject) => setTimeout(reject, 500, new Error('Error!')))
    
    // has a 50% chance to resolve and a 50% chance to reject
    const mayReject = () => Math.random() > 0.5
      ? Promise.resolve('it worked!')
      : Promise.reject(new Error("it didn't work"));
    
    // using with then and catch
    doSomething()
      .then(() => {
        console.log('done');
        return getString();
      })
      .then(str => {
        console.log(str);
        return mayReject();
      })
      .then(str => {
        console.log(str)
        return rejectingPromise()
      })
      .catch(console.error);

    You don’t have to reject with an Error but it’s good practice to do so.

    In ES2016 (ES7), await and async functions were added. await is another way to ‘unwrap’ a promise (pauses execution until the promise is resolved). await is only available in a function that has been marked as async.

    // same stuff as before
    const doSomething = () => new Promise(resolve => setTimeout(resolve, 500));
    const getString = () => new Promise(resolve => setTimeout(resolve, 500, 'Hello, world!'));
    const rejectingPromise = () => new Promise((resolve, reject) => setTimeout(reject, 500, new Error('Error!')));
    const mayReject = () => Math.random() > 0.5 ? Promise.resolve('it worked!') : Promise.reject(new Error("it didn't work"));
    
    // an async function
    async function f() {}
    
    // an async arrow function
    const g = async () => {};
    
    // an immediately invoked arrow function expression
    // this does the same thing as the previous example with then/catch
    (async () => {
      try {
        await doSomething();
        console.log('done');
        const str = await getString();
        console.log(str);
        // you don't have to assign it to a variable:
        console.log(await mayReject());
        await rejectingPromise();
      } catch (err) {
        console.error(err);
      }
    })();

    I recommend reading MDN’s article on asynchronous JS.