YouTube

Building a Sample Application

This page walks you through the steps of building an application that uses several different APIs to chart viewing statistics for a user's YouTube videos. The application performs the following tasks:

  • It uses the YouTube Data API to retrieve a list of the currently authenticated user's uploaded videos and then displays a list of video titles.
  • When the user clicks on a particular video, the application calls the YouTube Analytics API to retrieve analytics data for that video.
  • The application uses the Google Visualization API to chart the analytics data.

The following steps describe the process of building the application. In step 1, you create the application's HTML and CSS files. Steps 2 through 5 describe different parts of the JavaScript that the application uses. The complete sample code is also included at the end of the document.

  1. Step 1: Build your HTML page and CSS file
  2. Step 2: Enable OAuth 2.0 authentication
  3. Step 3: Retrieve data for the currently logged-in user
  4. Step 4: Request Analytics data for a video
  5. Step 5: Display Analytics data in a chart

Important: You need to register your application with Google to obtain an OAuth 2.0 client ID for your application.

Step 1: Build your HTML page and CSS file

In this step, you'll create an HTML page that loads the JavaScript libraries that the application will use. The HTML below shows the code for the page:


<!doctype html>
<html>
<head>
  <title>Sample YouTube Analytics API Implementation</title>
  <link type="text/css" rel="stylesheet" href="index.css">
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
  <script type="text/javascript" src="//www.google.com/jsapi"></script>
  <script type="text/javascript" src="index.js"></script>
  <script type="text/javascript" src="https://apis.google.com/js/client.js?onload=onJSClientLoad"></script>
</head>
<body>
  <div id="login-container" class="pre-auth">This application requires access to your YouTube account.
    Please <a href="#" id="login-link">authorize</a> to continue.
  </div>
  <div class="post-auth">
    <div id="message"></div>
    <div id="chart"></div>
    <div>Choose a Video:</div>
    <ul id="video-list"></ul>
  </div>
</body>
</html>

As shown in the <head> tag of the sample page, the application uses the following libraries:

  • jQuery provides helper methods to simplify HTML document traversing, event handling, animating and Ajax interactions.
  • The Google API loader (www.google.com/jsapi) lets you easily import one or more Google APIs. This sample application uses the API loader to load the Google Visualization API, which is used to chart the retrieved Analytics data.
  • The index.js library contains functions specific to the sample application. This tutorial walks you through the steps to create those functions.
  • The Google APIs Client Library for JavaScript helps you to implement OAuth 2.0 authentication and to call the YouTube Analytics API.

The sample application also includes the index.css file. A sample CSS file, which you could save in the same directory as your HTML page, is shown below:


body {
  font-family: Helvetica, sans-serif;
}

.pre-auth {
  display: none;
}

.post-auth {
  display: none;
}

#chart {
  width: 500px;
  height: 300px;
  margin-bottom: 1em;
}

#video-list {
  padding-left: 1em;
  list-style-type: none;
}
#video-list > li {
  cursor: pointer;
}
#video-list > li:hover {
  color: blue;
}

Step 2: Enable OAuth 2.0 authentication

In this step, you'll start building the index.js file that's being called by your HTML page. With that in mind, create a file named index.js in the same directory as your HTML page and insert the following code in that file. Replace the string YOUR_CLIENT_ID with the client ID for your registered application.

(function() {

  // Retrieve your client ID from the Google APIs Console at
  // https://code.google.com/apis/console.
  var OAUTH2_CLIENT_ID = 'YOUR_CLIENT_ID';
  var OAUTH2_SCOPES = [
    'https://www.googleapis.com/auth/yt-analytics.readonly',
    'https://www.googleapis.com/auth/youtube.readonly'
  ];


  // The Google APIs JS client invokes this callback automatically after loading.
  // See http://code.google.com/p/google-api-javascript-client/wiki/Authentication
  window.onJSClientLoad = function() {
    gapi.auth.init(function() {
      window.setTimeout(checkAuth, 1);
    });
  };


  // Attempt the immediate OAuth 2 client flow as soon as the page loads.
  // If the currently logged-in Google Account has previously authorized
  // OAUTH2_CLIENT_ID, then it will succeed with no user intervention.
  // Otherwise, it will fail and the user interface that prompts for
  // authorization will need to be displayed.
  function checkAuth() {
    gapi.auth.authorize({
      client_id: OAUTH2_CLIENT_ID,
      scope: OAUTH2_SCOPES,
      immediate: true
    }, handleAuthResult);
  }


  // Handle the result of a gapi.auth.authorize() call.
  function handleAuthResult(authResult) {
    if (authResult) {
      // Auth was successful. Hide auth prompts and show things
      // that should be visible after auth succeeds.
      $('.pre-auth').hide();
      $('.post-auth').show();

      loadAPIClientInterfaces();
    } else {
      // Auth was unsuccessful. Show things related to prompting for auth
      // and hide the things that should be visible after auth succeeds.
      $('.post-auth').hide();
      $('.pre-auth').show();

      // Make the #login-link clickable. Attempt a non-immediate OAuth 2 client
      // flow. The current function will be called when that flow completes.
      $('#login-link').click(function() {
        gapi.auth.authorize({
          client_id: OAUTH2_CLIENT_ID,
          scope: OAUTH2_SCOPES,
          immediate: false
        }, handleAuthResult);
      });
    }
  }


  // Helper method to display a message on the page.
  function displayMessage(message) {
    $('#message').text(message).show();
  }

  // Helper method to hide a previously displayed message on the page.
  function hideMessage() {
    $('#message').hide();
  }

  /* In later steps, add additional functions above this line. */
})();

Step 3: Retrieve data for the currently logged-in user

In this step, you'll add a function to your index.js file that retrieves the currently logged-in user's uploaded videos feed using the YouTube Data API (v2.0). That feed will specify the user's YouTube channel ID, which you will need when calling the YouTube Analytics API. In addition, the sample app will list the user's uploaded videos so that the user can retrieve Analytics data for any individual video.

Make the following changes to your index.js file:

  1. Add a function that loads the client interface for the YouTube Analytics and Data APIs. This is a prerequisite to using the Google APIs JavaScript client.

    Once both API client interfaces are loaded, the function calls the getUserChannel function.

    
      // Load the client interface for the YouTube Analytics and Data APIs, which
      // is required to use the Google APIs JS client. More info is available at
      // http://code.google.com/p/google-api-javascript-client/wiki/GettingStarted#Loading_the_Client
      function loadAPIClientInterfaces() {
        gapi.client.load('youtube', 'v3', function() {
          gapi.client.load('youtubeAnalytics', 'v1', function() {
            // After both client interfaces load, use the Data API to request
            // information about the authenticated user's channel.
            getUserChannel();
          });
        });
      }
    
    
  2. Add the channelId variable as well as the getUserChannel function. The function calls the YouTube Data API (v3) and includes the mine parameter, which indicates that the request is for the currently authenticated user's channel information. The channelId will be sent to the Analytics API to identify the channel for whom you are retrieving Analytics data.

    
      // Keeps track of the currently authenticated user's YouTube user ID.
      var channelId;
    
    
      // Calls the Data API to retrieve info about the currently authenticated
      // user's YouTube channel.
      function getUserChannel() {
        // https://developers.google.com/youtube/v3/docs/channels/list
        var request = gapi.client.youtube.channels.list({
          // "mine: true" indicates that you want to retrieve the authenticated user's channel.
          mine: true,
          part: 'id,contentDetails'
        });
    
        request.execute(function(response) {
          if ('error' in response) {
            displayMessage(response.error.message);
          } else {
            // We will need the channel's channel ID to make calls to the
            // Analytics API. The channel ID looks like "UCdLFeWKpkLhkguiMZUp8lWA".
            channelId = response.items[0].id;
            // This string, of the form "UUdLFeWKpkLhkguiMZUp8lWA", is a unique ID
            // for a playlist of videos uploaded to the authenticated user's channel.
            var uploadsListId = response.items[0].contentDetails.relatedPlaylists.uploads;
            // Use the uploads playlist ID to retrieve the list of uploaded videos.
            getPlaylistItems(uploadsListId);
          }
        });
      }
    
    
  3. Add the getPlaylistItems function, which retrieves the items in a specified playlist. In this case, the playlist lists the videos uploaded to the user's channel. (Note that the sample function below only retrieves the first 50 items in that feed, and you would need to implement pagination to fetch additional items.)

    After retrieving the list of playlist items, the function calls the getVideoMetadata() function. That function then obtains metadata about each video in the list and adds each video to the list that the user sees.

    
      // Calls the Data API to retrieve the items in a particular playlist. In this
      // example, we are retrieving a playlist of the currently authenticated user's
      // uploaded videos. By default, the list returns the most recent videos first.
      function getPlaylistItems(listId) {
        // https://developers.google.com/youtube/v3/docs/playlistItems/list
        var request = gapi.client.youtube.playlistItems.list({
          playlistId: listId,
          part: 'snippet'
        });
    
        request.execute(function(response) {
          if ('error' in response) {
            displayMessage(response.error.message);
          } else {
            if ('items' in response) {
              // jQuery.map() iterates through all of the items in the response and
              // creates a new array that only contains the specific property we're
              // looking for: videoId.
              var videoIds = $.map(response.items, function(item) {
                return item.snippet.resourceId.videoId;
              });
    
              // Now that we know the IDs of all the videos in the uploads list,
              // we can retrieve info about each video.
              getVideoMetadata(videoIds);
            } else {
              displayMessage('There are no videos in your channel.');
            }
          }
        });
      }
    
    
      // Given an array of video ids, obtains metadata about each video and then
      // uses that metadata to display a list of videos to the user.
      function getVideoMetadata(videoIds) {
        // https://developers.google.com/youtube/v3/docs/videos/list
        var request = gapi.client.youtube.videos.list({
          // The 'id' property value is a comma-separated string of video IDs.
          id: videoIds.join(','),
          part: 'id,snippet,statistics'
        });
    
        request.execute(function(response) {
          if ('error' in response) {
            displayMessage(response.error.message);
          } else {
            // Get the jQuery wrapper for #video-list once outside the loop.
            var videoList = $('#video-list');
            $.each(response.items, function() {
              // Exclude videos that don't have any views, since those videos
              // will not have any interesting viewcount analytics data.
              if (this.statistics.viewCount == 0) {
                return;
              }
    
              var title = this.snippet.title;
              var videoId = this.id;
    
              // Create a new <li> element that contains an <a> element.
              // Set the <a> element's text content to the video's title, and
              // add a click handler that will display Analytics data when invoked.
              var liElement = $('<li>');
              var aElement = $('<a>');
              // The dummy href value of '#' ensures that the browser renders the
              // <a> element as a clickable link.
              aElement.attr('href', '#');
              aElement.text(title);
              aElement.click(function() {
                displayVideoAnalytics(videoId);
              });
    
              // Call the jQuery.append() method to add the new <a> element to
              // the <li> element, and the <li> element to the parent
              // list, which is identified by the 'videoList' variable.
              liElement.append(aElement);
              videoList.append(liElement);
            });
    
            if (videoList.children().length == 0) {
              displayMessage('Your channel does not have any videos that have been viewed.');
            }
          }
        });
      }
    
    

Step 4: Request Analytics data for a video

In this step, you'll modify the sample application so that when you click on a video's title, the application calls the YouTube Analytics API to retrieve Analytics data for that video. To do so, make the following changes to the sample application:

  1. Add a variable that specifies the default date range for the retrieved Analytics report data.

    
      var ONE_MONTH_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 30;
    
    
  2. Add code that creates a YYYY-MM-DD string for a date object and that pads day and month numbers in dates to two digits:

    
      // Boilerplate code to take a Date object and return a YYYY-MM-DD string.
      function formatDateString(date) {
        var yyyy = date.getFullYear().toString();
        var mm = padToTwoCharacters(date.getMonth() + 1);
        var dd = padToTwoCharacters(date.getDate());
    
        return yyyy + '-' + mm + '-' + dd;
      }
    
    
      // If number is a single digit, prepend a '0'. Otherwise, return it as a string.
      function padToTwoCharacters(number) {
        if (number < 10) {
          return '0' + number;
        } else {
          return number.toString();
        }
      }
    
    
  3. Define the displayVideoAnalytics function, which retrieves YouTube Analytics data for a video. This function will execute when the user clicks on a video in the list. The getVideoMetadata function, which prints the list of videos and was defined in step 3, defines the click event handler.

    
      // Requests YouTube Analytics for a video, and displays results in a chart.
      function displayVideoAnalytics(videoId) {
        if (channelId) {
          // To use a different date range, modify the ONE_MONTH_IN_MILLISECONDS
          // variable to a different millisecond delta as desired.
          var today = new Date();
          var lastMonth = new Date(today.getTime() - ONE_MONTH_IN_MILLISECONDS);
    
          var request = gapi.client.youtubeAnalytics.reports.query({
            // The start-date and end-date parameters need to be YYYY-MM-DD strings.
            'start-date': formatDateString(lastMonth),
            'end-date': formatDateString(today),
            // A future YouTube Analytics API release should support channel==default.
            // In the meantime, you need to explicitly specify channel==channelId.
            // See https://devsite.googleplex.com/youtube/analytics/v1/#ids
            ids: 'channel==' + channelId,
            dimensions: 'day',
            // See https://developers.google.com/youtube/analytics/v1/available_reports for details
            // on different filters and metrics you can request when dimensions=day.
            metrics: 'views',
            filters: 'video==' + videoId
          });
    
          request.execute(function(response) {
            // This function is called regardless of whether the request succeeds.
            // The response either has valid analytics data or an error message.
            if ('error' in response) {
              displayMessage(response.error.message);
            } else {
              displayChart(videoId, response);
            }
          });
        } else {
          displayMessage('The YouTube user id for the current user is not available.');
        }
      }
    
    

    See the API documentation's available reports page for more information about the data that could be retrieved and valid value combinations for the metrics, dimensions, and filters parameters.

Step 5: Display Analytics data in a chart

In this step, you'll add the displayChart function, which sends the YouTube Analytics data to the Google Visualization API. That API then charts the information.

  1. Load the Google Visualization API, which will display your data in a chart. See the Visualization API documentation for more details about charting options.

    google.load('visualization', '1.0', {'packages': ['corechart']});
    
  2. Define a new function named displayChart that uses the Google Visualization API to dynamically generate a chart showing the Analytics data.

    
      // Calls the Google Chart Tools API to generate a chart of analytics data.
      function displayChart(videoId, response) {
        if ('rows' in response) {
          hideMessage();
    
          // The columnHeaders property contains an array of objects representing
          // each column's title – e.g.: [{name:"day"},{name:"views"}]
          // We need these column titles as a simple array, so we call jQuery.map()
          // to get each element's "name" property and create a new array that only
          // contains those values.
          var columns = $.map(response.columnHeaders, function(item) {
            return item.name;
          });
          // The google.visualization.arrayToDataTable() wants an array of arrays.
          // The first element is an array of column titles, calculated above as
          // "columns". The remaining elements are arrays that each represent
          // a row of data. Fortunately, response.rows is already in this format,
          // so it can just be concatenated.
          // See https://developers.google.com/chart/interactive/docs/datatables_dataviews#arraytodatatable
          var chartDataArray = [columns].concat(response.rows);
          var chartDataTable = google.visualization.arrayToDataTable(chartDataArray);
    
          var chart = new google.visualization.LineChart(document.getElementById('chart'));
          chart.draw(chartDataTable, {
            // Additional options can be set if desired.
            // See https://developers.google.com/chart/interactive/docs/reference#visdraw
            title: 'Views per Day of Video ' + videoId
          });
        } else {
          displayMessage('No data available for video ' + videoId);
        }
      }
    
    

See the complete index.js file

The index.js file below incorporates all of the changes from the steps shown above. Again, remember that you need to replace the string YOUR_CLIENT_ID with the client ID for your registered application.


(function() {
  // Retrieve your client ID from the Google APIs Console at
  // https://code.google.com/apis/console.
  var OAUTH2_CLIENT_ID = 'YOUR_CLIENT_ID';
  var OAUTH2_SCOPES = [
    'https://www.googleapis.com/auth/yt-analytics.readonly',
    'https://www.googleapis.com/auth/youtube.readonly'
  ];

  var ONE_MONTH_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 30;

  // Keeps track of the currently authenticated user's YouTube user ID.
  var channelId;

  // For information about the Google Chart Tools API, see
  // https://developers.google.com/chart/interactive/docs/quick_start
  google.load('visualization', '1.0', {'packages': ['corechart']});

  // The Google APIs JS client invokes this callback automatically after loading.
  // See http://code.google.com/p/google-api-javascript-client/wiki/Authentication
  window.onJSClientLoad = function() {
    gapi.auth.init(function() {
      window.setTimeout(checkAuth, 1);
    });
  };

  // Attempt the immediate OAuth 2 client flow as soon as the page loads.
  // If the currently logged-in Google Account has previously authorized
  // OAUTH2_CLIENT_ID, then it will succeed with no user intervention.
  // Otherwise, it will fail and the user interface that prompts for
  // authorization will need to be displayed.
  function checkAuth() {
    gapi.auth.authorize({
      client_id: OAUTH2_CLIENT_ID,
      scope: OAUTH2_SCOPES,
      immediate: true
    }, handleAuthResult);
  }

  // Handle the result of a gapi.auth.authorize() call.
  function handleAuthResult(authResult) {
    if (authResult) {
      // Auth was successful. Hide auth prompts and show things
      // that should be visible after auth succeeds.
      $('.pre-auth').hide();
      $('.post-auth').show();

      loadAPIClientInterfaces();
    } else {
      // Auth was unsuccessful. Show things related to prompting for auth
      // and hide the things that should be visible after auth succeeds.
      $('.post-auth').hide();
      $('.pre-auth').show();

      // Make the #login-link clickable. Attempt a non-immediate OAuth 2 client
      // flow. The current function will be called when that flow completes.
      $('#login-link').click(function() {
        gapi.auth.authorize({
          client_id: OAUTH2_CLIENT_ID,
          scope: OAUTH2_SCOPES,
          immediate: false
        }, handleAuthResult);
      });
    }
  }

  // Load the client interface for the YouTube Analytics and Data APIs, which
  // is required to use the Google APIs JS client. More info is available at
  // http://code.google.com/p/google-api-javascript-client/wiki/GettingStarted#Loading_the_Client
  function loadAPIClientInterfaces() {
    gapi.client.load('youtube', 'v3', function() {
      gapi.client.load('youtubeAnalytics', 'v1', function() {
        // After both client interfaces load, use the Data API to request
        // information about the authenticated user's channel.
        getUserChannel();
      });
    });
  }

  // Calls the Data API to retrieve info about the currently authenticated
  // user's YouTube channel.
  function getUserChannel() {
    // https://developers.google.com/youtube/v3/docs/channels/list
    var request = gapi.client.youtube.channels.list({
      // "mine: true" indicates that you want to retrieve the authenticated user's channel.
      mine: true,
      part: 'id,contentDetails'
    });

    request.execute(function(response) {
      if ('error' in response) {
        displayMessage(response.error.message);
      } else {
        // We will need the channel's channel ID to make calls to the
        // Analytics API. The channel ID looks like "UCdLFeWKpkLhkguiMZUp8lWA".
        channelId = response.items[0].id;
        // This string, of the form "UUdLFeWKpkLhkguiMZUp8lWA", is a unique ID
        // for a playlist of videos uploaded to the authenticated user's channel.
        var uploadsListId = response.items[0].contentDetails.relatedPlaylists.uploads;
        // Use the uploads playlist ID to retrieve the list of uploaded videos.
        getPlaylistItems(uploadsListId);
      }
    });
  }

  // Calls the Data API to retrieve the items in a particular playlist. In this
  // example, we are retrieving a playlist of the currently authenticated user's
  // uploaded videos. By default, the list returns the most recent videos first.
  function getPlaylistItems(listId) {
    // https://developers.google.com/youtube/v3/docs/playlistItems/list
    var request = gapi.client.youtube.playlistItems.list({
      playlistId: listId,
      part: 'snippet'
    });

    request.execute(function(response) {
      if ('error' in response) {
        displayMessage(response.error.message);
      } else {
        if ('items' in response) {
          // jQuery.map() iterates through all of the items in the response and
          // creates a new array that only contains the specific property we're
          // looking for: videoId.
          var videoIds = $.map(response.items, function(item) {
            return item.snippet.resourceId.videoId;
          });

          // Now that we know the IDs of all the videos in the uploads list,
          // we can retrieve info about each video.
          getVideoMetadata(videoIds);
        } else {
          displayMessage('There are no videos in your channel.');
        }
      }
    });
  }

  // Given an array of video ids, obtains metadata about each video and then
  // uses that metadata to display a list of videos to the user.
  function getVideoMetadata(videoIds) {
    // https://developers.google.com/youtube/v3/docs/videos/list
    var request = gapi.client.youtube.videos.list({
      // The 'id' property value is a comma-separated string of video IDs.
      id: videoIds.join(','),
      part: 'id,snippet,statistics'
    });

    request.execute(function(response) {
      if ('error' in response) {
        displayMessage(response.error.message);
      } else {
        // Get the jQuery wrapper for #video-list once outside the loop.
        var videoList = $('#video-list');
        $.each(response.items, function() {
          // Exclude videos that don't have any views, since those videos
          // will not have any interesting viewcount analytics data.
          if (this.statistics.viewCount == 0) {
            return;
          }

          var title = this.snippet.title;
          var videoId = this.id;

          // Create a new <li> element that contains an <a> element.
          // Set the <a> element's text content to the video's title, and
          // add a click handler that will display Analytics data when invoked.
          var liElement = $('<li>');
          var aElement = $('<a>');
          // The dummy href value of '#' ensures that the browser renders the
          // <a> element as a clickable link.
          aElement.attr('href', '#');
          aElement.text(title);
          aElement.click(function() {
            displayVideoAnalytics(videoId);
          });

          // Call the jQuery.append() method to add the new <a> element to
          // the <li> element, and the <li> element to the parent
          // list, which is identified by the 'videoList' variable.
          liElement.append(aElement);
          videoList.append(liElement);
        });

        if (videoList.children().length == 0) {
          displayMessage('Your channel does not have any videos that have been viewed.');
        }
      }
    });
  }

  // Requests YouTube Analytics for a video, and displays results in a chart.
  function displayVideoAnalytics(videoId) {
    if (channelId) {
      // To use a different date range, modify the ONE_MONTH_IN_MILLISECONDS
      // variable to a different millisecond delta as desired.
      var today = new Date();
      var lastMonth = new Date(today.getTime() - ONE_MONTH_IN_MILLISECONDS);

      var request = gapi.client.youtubeAnalytics.reports.query({
        // The start-date and end-date parameters need to be YYYY-MM-DD strings.
        'start-date': formatDateString(lastMonth),
        'end-date': formatDateString(today),
        // A future YouTube Analytics API release should support channel==default.
        // In the meantime, you need to explicitly specify channel==channelId.
        // See https://devsite.googleplex.com/youtube/analytics/v1/#ids
        ids: 'channel==' + channelId,
        dimensions: 'day',
        // See https://developers.google.com/youtube/analytics/v1/available_reports for details
        // on different filters and metrics you can request when dimensions=day.
        metrics: 'views',
        filters: 'video==' + videoId
      });

      request.execute(function(response) {
        // This function is called regardless of whether the request succeeds.
        // The response either has valid analytics data or an error message.
        if ('error' in response) {
          displayMessage(response.error.message);
        } else {
          displayChart(videoId, response);
        }
      });
    } else {
      displayMessage('The YouTube user id for the current user is not available.');
    }
  }

  // Boilerplate code to take a Date object and return a YYYY-MM-DD string.
  function formatDateString(date) {
    var yyyy = date.getFullYear().toString();
    var mm = padToTwoCharacters(date.getMonth() + 1);
    var dd = padToTwoCharacters(date.getDate());

    return yyyy + '-' + mm + '-' + dd;
  }

  // If number is a single digit, prepend a '0'. Otherwise, return it as a string.
  function padToTwoCharacters(number) {
    if (number < 10) {
      return '0' + number;
    } else {
      return number.toString();
    }
  }

  // Calls the Google Chart Tools API to generate a chart of analytics data.
  function displayChart(videoId, response) {
    if ('rows' in response) {
      hideMessage();

      // The columnHeaders property contains an array of objects representing
      // each column's title – e.g.: [{name:"day"},{name:"views"}]
      // We need these column titles as a simple array, so we call jQuery.map()
      // to get each element's "name" property and create a new array that only
      // contains those values.
      var columns = $.map(response.columnHeaders, function(item) {
        return item.name;
      });
      // The google.visualization.arrayToDataTable() wants an array of arrays.
      // The first element is an array of column titles, calculated above as
      // "columns". The remaining elements are arrays that each represent
      // a row of data. Fortunately, response.rows is already in this format,
      // so it can just be concatenated.
      // See https://developers.google.com/chart/interactive/docs/datatables_dataviews#arraytodatatable
      var chartDataArray = [columns].concat(response.rows);
      var chartDataTable = google.visualization.arrayToDataTable(chartDataArray);

      var chart = new google.visualization.LineChart(document.getElementById('chart'));
      chart.draw(chartDataTable, {
        // Additional options can be set if desired.
        // See https://developers.google.com/chart/interactive/docs/reference#visdraw
        title: 'Views per Day of Video ' + videoId
      });
    } else {
      displayMessage('No data available for video ' + videoId);
    }
  }

  // Helper method to display a message on the page.
  function displayMessage(message) {
    $('#message').text(message).show();
  }

  // Helper method to hide a previously displayed message on the page.
  function hideMessage() {
    $('#message').hide();
  }
})();

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.