1

I try to get the last video list uploaded of a specific channel ID of Youtube with statistics. I get the list but not statistics.

I think the result is returning before Object assign. How can I return the result after Object assign ?

This my code :

async function (inputs, exits) {

    var { google } = require('googleapis');
    var youtubeApiKey = 'API_KEY';
    var service = google.youtube('v3');

    // Get upload ID to list uploaded videos
    service.channels.list({
        part: 'contentDetails',
        id: inputs.ytbIdChannel,
        key: youtubeApiKey,
    })
        .then(res => {
            if (res.data.items[0].contentDetails.relatedPlaylists.uploads) {
                let uploadsId = res.data.items[0].contentDetails.relatedPlaylists.uploads;

                // With the uploadID, get all uploaded videos
                service.playlistItems.list({
                    part: 'snippet',
                    playlistId: uploadsId,
                    maxResults: '12',
                    key: youtubeApiKey,
                })
                    .then(res => {
                        this.youtbeVideos = res.data.items,

                        // For each videos in list, inject the statistics object and return all videos
                        this.youtbeVideos.forEach(video => {
                            let videoId = video.snippet.resourceId.videoId

                            service.videos.list({
                                part: 'statistics',
                                id: videoId,
                                key: youtubeApiKey,
                            })
                                .then(res => {
                                    Object.assign(video, res.data.items[0].statistics)
                                    return exits.success(
                                        this.youtbeVideos
                                    );
                                })
                                .catch(error => {
                                    console.error(error);
                                });
                        });



                    })
                    .catch(error => {
                        console.error(error);
                    });
            }
        })
        .catch(error => {
            console.error(error);
        });

}

Thanks for your help

3 Answers3

1

I have fixed your code and verified it is working with the YouTube API.

First, make an asynchronous function by replacing:

service.channels.list({
    part: 'contentDetails',
    id: inputs.ytbIdChannel,
    key: youtubeApiKey,
})
    .then(res => {

with:

service.channels.list({
    part: 'contentDetails',
    id: inputs.ytbIdChannel,
    key: youtubeApiKey,
})
    .then(async res => {

Then, in order to correct you need to replace the contents of the if (res.data.items[0].contentDetails.relatedPlaylists.uploads) block with this code instead:

let uploadsId = channels.data.items[0].contentDetails.relatedPlaylists.uploads;

// With the uploadID, get all uploaded videos
let videos = await service.playlistItems.list({
    part: 'snippet',
    playlistId: uploadsId,
    maxResults: '12',
    key: youtubeApiKey,
})
this.youtbeVideos = videos.data.items;

// For each videos in list, inject the statistics object and return all videos
for (let video of this.youtbeVideos) {
    let videoId = video.snippet.resourceId.videoId

    let stats = await service.videos.list({
        part: 'statistics',
        id: videoId,
        key: youtubeApiKey,
    })
    Object.assign(video, stats.data.items[0].statistics)
}

return exits.success(
    this.youtbeVideos
);

The two key changes I have made are:

  1. Using await so that the iteration over this.youtbeVideos pauses for the statistics to be retrieved before moving on
  2. Moving the return and call to exits.success outside of the loop so that it will return the data once after all statistics have been added

You can run my working copy online here - just add your API key for YouTube.

When the list returns it will contain all the statistics as expected.

Jacob Wrenn
  • 448
  • 5
  • 14
0

Maybe you can change .then for async/await.

For example:

service.getAll()
  .then(res => {
      service.getById(res.id)
        .then(res =>{
           .....
       })
    })

You can use await:

const res1 = await service.getAll();
const res2 = await service.getById(res1.id);

It's more clean code, and with await, node stop all steps and await it. When finish, will go to next step... and then again.. await, execute, await, execute.

0

probably you you should return or 'await' the promises. Not just call them. Otherwise nobody knows when the function is ready.

write await before every service.XXX.list, make your callback functions async and use for loop or promise.all instead of forEach for this.videos because otherwise you cant wait for every promise (await within forEach)

And maybe this is to fix:

  • you call return exits.success(this.youtbeVideos); for each video, shouldn't it be for all videos done?
  • Object.assign(video, res.data.items[0].statistics) probably assigns every time same keys (overwrites)

disclaimer: i don't know the api/your call environment, so this is only suggestions, that maybe will be useful.

ya_dimon
  • 3,483
  • 3
  • 31
  • 42