יצירת אפליקציה לדוגמה

בדף הזה ננחה אתכם בשלבים של בניית אפליקציה שמשתמשת בכמה ממשקי API שונים כדי ליצור תרשים של נתונים סטטיסטיים של צפייה בסרטוני YouTube של משתמש. האפליקציה מבצעת את המשימות הבאות:

  • הוא משתמש ב-YouTube Data API כדי לאחזר רשימה של סרטונים שהועלו על ידי המשתמש המאומת ולאחר מכן מציג רשימה של כותרות סרטונים.
  • כאשר משתמש לוחץ על סרטון מסוים, האפליקציה קוראת ל-YouTube Analytics API כדי לאחזר נתוני ניתוח עבור סרטון זה.
  • האפליקציה משתמשת ב-Google Visualization API כדי ליצור תרשים של נתוני הניתוח.

השלבים הבאים מתארים את התהליך של בניית האפליקציה. בשלב 1, יוצרים את קובצי ה-HTML וה-CSS של האפליקציה. שלבים 2 עד 5 מתארים חלקים שונים של ה-JavaScript שבהם האפליקציה משתמשת. גם הקוד המלא לדוגמה מופיע בסוף המסמך.

  1. שלב 1: יצירת דף HTML וקובץ CSS
  2. שלב 2: הפעלת אימות OAuth 2.0
  3. שלב 3: אחזור נתונים עבור המשתמש המחובר כעת
  4. שלב 4: בקשת נתונים מ-Analytics לגבי סרטון
  5. שלב 5: מציגים נתוני Analytics בתרשים

חשוב: עליך לרשום את הבקשה ב-Google כדי לקבל מזהה לקוח OAuth 2.0 עבור האפליקציה שלך.

שלב 1: בניית דף ה-HTML וקובץ ה-CSS

בשלב הזה יוצרים דף HTML שטוען את ספריות JavaScript שבהן ישתמש האפליקציה. קוד ה-HTML הבא מציג את הקוד של הדף:


<!doctype html>
<html>
<head>
  <title>Google I/O YouTube Codelab</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>

כפי שמוצג בתג <head> של הדף לדוגמה, האפליקציה משתמשת בספריות הבאות:

  • jQuery מספק שיטות סיוע שנועדו לפשט מעבר של מסמכי HTML, טיפול באירועים, אנימציות ואינטראקציות Ajax.
  • הכלי לטעינת Google API (www.google.com/jsapi) מאפשר לייבא בקלות ממשק API אחד או יותר של Google. אפליקציה לדוגמה זו משתמשת בטוען ה-API כדי לטעון את ממשק ה-API של Google Visualization, המשמש לפילוח נתוני Analytics המאוחזרים.
  • הספרייה index.js מכילה פונקציות ספציפיות לאפליקציה לדוגמה. במדריך הזה נסביר איך יוצרים את הפונקציות האלה.
  • ספריית הלקוחות של Google APIs ל-JavaScript עוזרת להטמיע אימות OAuth 2.0 ולהפעיל את YouTube Analytics API.

האפליקציה לדוגמה כוללת גם את הקובץ index.css. קובץ CSS לדוגמה, שאותו אפשר לשמור באותה ספרייה כמו דף ה-HTML, מוצג בהמשך:


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;
}

שלב 2: מפעילים אימות OAuth 2.0

בשלב זה, תתחילו לבנות את קובץ ה-index.js שאליו מפנה דף ה-HTML. לכן, כדאי ליצור קובץ בשם index.js באותה ספרייה כמו דף ה-HTML, ולהוסיף את הקוד הבא לקובץ. מחליפים את המחרוזת YOUR_CLIENT_ID במזהה הלקוח של האפליקציה הרשומה.

(function() {

  // Retrieve your client ID from the Google API Console at
  // https://console.cloud.google.com/.
  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'
  ];

  // Upon loading, the Google APIs JS client automatically invokes this callback.
  // See https://developers.google.com/api-client-library/javascript/features/authentication 
  window.onJSClientLoad = function() {
    gapi.auth.init(function() {
      window.setTimeout(checkAuth, 1);
    });
  };

  // Attempt the immediate OAuth 2.0 client flow as soon as the page loads.
  // If the currently logged-in Google Account has previously authorized
  // the client specified as the OAUTH2_CLIENT_ID, then the authorization
  // succeeds with no user intervention. Otherwise, it fails and the
  // user interface that prompts for authorization needs to display.
  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) {
      // Authorization was successful. Hide authorization prompts and show
      // content that should be visible after authorization succeeds.
      $('.pre-auth').hide();
      $('.post-auth').show();

      loadAPIClientInterfaces();
    } else {
      // Authorization was unsuccessful. Show content related to prompting for
      // authorization and hide content that should be visible if authorization
      // succeeds.
      $('.post-auth').hide();
      $('.pre-auth').show();

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

  // This helper method displays a message on the page.
  function displayMessage(message) {
    $('#message').text(message).show();
  }

  // This helper method hides a previously displayed message on the page.
  function hideMessage() {
    $('#message').hide();
  }
  /* In later steps, add additional functions above this line. */
})();

שלב 3: אחזור נתונים עבור המשתמש המחובר כעת

בשלב הזה צריך להוסיף פונקציה לקובץ index.js שמאחזר את פיד הסרטונים שהועלה על ידי המשתמש המחובר כעת באמצעות ממשק ה-API של נתונים של YouTube (גרסה 2.0). העדכון הזה יציין את מזהה ערוץ YouTube של המשתמש. המזהה הזה יהיה בשימוש בעת הפעלת ה-API של YouTube Analytics. בנוסף, האפליקציה לדוגמה תפרט את הסרטונים שהועלו על ידי המשתמש, כך שהמשתמש יוכל לאחזר נתונים מ-Analytics לגבי כל סרטון בנפרד.

מבצעים את השינויים הבאים בקובץ index.js:

  1. הוספת פונקציה שטוענת את ממשק הלקוח עבור YouTube Analytics וממשקי Data API. חייבים להשתמש בלקוח JavaScript של Google APIs.

    לאחר הטעינה של שני ממשקי הלקוח של ה-API, הפונקציה קוראת לפונקציה getUserChannel.

    
      // Load the client interfaces for the YouTube Analytics and Data APIs, which
      // are required to use the Google APIs JS client. More info is available at
      // https://developers.google.com/api-client-library/javascript/dev/dev_jscript#loading-the-client-library-and-the-api
      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. צריך להוסיף את המשתנה channelId וגם את הפונקציה getUserChannel. הפונקציה קוראת ל-YouTube Data API (v3) וכוללת את הפרמטר mine, שמציין שהבקשה מתייחסת לפרטי הערוץ של המשתמש המאומת כרגע. channelId יישלח אל ממשק ה-API של Analytics כדי לזהות את הערוץ שעבורו אתה מאחזר את נתוני Analytics.

    
      // Keep track of the currently authenticated user's YouTube channel ID.
      var channelId;
    
      // Call the Data API to retrieve information about the currently
      // authenticated user's YouTube channel.
      function getUserChannel() {
        // Also see: https://developers.google.com/youtube/v3/docs/channels/list
        var request = gapi.client.youtube.channels.list({
          // Setting the "mine" request parameter's value to "true" indicates that
          // you want to retrieve the currently authenticated user's channel.
          mine: true,
          part: 'id,contentDetails'
        });
    
        request.execute(function(response) {
          if ('error' in response) {
            displayMessage(response.error.message);
          } else {
            // We need the channel's channel ID to make calls to the Analytics API.
            // The channel ID value has the form "UCdLFeWKpkLhkguiMZUp8lWA".
            channelId = response.items[0].id;
            // Retrieve the playlist ID that uniquely identifies the playlist of
            // videos uploaded to the authenticated user's channel. This value has
            // the form "UUdLFeWKpkLhkguiMZUp8lWA".
            var uploadsListId = response.items[0].contentDetails.relatedPlaylists.uploads;
            // Use the playlist ID to retrieve the list of uploaded videos.
            getPlaylistItems(uploadsListId);
          }
        });
      }
    
  3. אפשר להוסיף את הפונקציה getPlaylistItems שמאחזרת את הפריטים בפלייליסט ספציפי. במקרה הזה, הפלייליסט מציג את הסרטונים שהועלו לערוץ של המשתמש. (חשוב לזכור שהפונקציה לדוגמה שלמטה מאחזרת רק את 50 הפריטים הראשונים באותו פיד, וצריך להטמיע עימוד כדי לאחזר פריטים נוספים).

    אחרי אחזור רשימת פריטי הפלייליסט, הפונקציה קוראת לפונקציה getVideoMetadata(). פונקציה זו מקבלת מטא-נתונים לגבי כל סרטון ברשימה ומוסיפה כל סרטון לרשימה שהמשתמש רואה.

    
      // Call 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) {
        // See 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) {
              // The jQuery.map() function 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 information about each video.
              getVideoMetadata(videoIds);
            } else {
              displayMessage('There are no videos in your channel.');
            }
          }
        });
      }
    
      // Given an array of video IDs, this function obtains metadata about each
      // video and then uses that metadata to display a list of videos.
      function getVideoMetadata(videoIds) {
        // https://developers.google.com/youtube/v3/docs/videos/list
        var request = gapi.client.youtube.videos.list({
          // The 'id' property's 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 the #video-list element before starting
            // the loop.
            var videoList = $('#video-list');
            $.each(response.items, function() {
              // Exclude videos that do not 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>');
              // Setting the href value to '#' 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) {
              // Display a message if the channel does not have any viewed videos.
              displayMessage('Your channel does not have any videos that have been viewed.');
            }
          }
        });
      }
    

שלב 4: בקשת נתונים מ-Analytics לגבי סרטון

בשלב זה, משנים את האפליקציה לדוגמה כך שכשלוחצים על כותרת של סרטון, האפליקציה שולחת קריאה ל-YouTube Analytics API כדי לאחזר נתונים מ-Analytics לגבי הסרטון הזה. כדי לעשות זאת, עליך לבצע את השינויים הבאים באפליקציה לדוגמה:

  1. מוסיפים משתנה שמציין את טווח התאריכים המשמש כברירת מחדל לנתוני הדוח ב-Analytics שאוחזרו.

    
      var ONE_MONTH_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 30;
    
  2. צריך להוסיף קוד שיוצר מחרוזת YYYY-MM-DD לאובייקט תאריך, ואחריו מספרי ימים וחודשים בשתי ספרות:

    
      // This boilerplate code takes a Date object and returns 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 the number
      //  as a string.
      function padToTwoCharacters(number) {
        if (number < 10) {
          return '0' + number;
        } else {
          return number.toString();
        }
      }
    
  3. מגדירים את הפונקציה displayVideoAnalytics שמאחזרת נתוני YouTube Analytics לסרטון. הפונקציה הזו תופעל כשהמשתמש ילחץ על סרטון ברשימה. הפונקציה getVideoMetadata, שמדפיסה את רשימת הסרטונים והוגדרה בשלב 3, מגדירה את הגורם המטפל באירועים של קליקים.

    
      // This function requests YouTube Analytics data for a video and displays
      // the 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 must be YYYY-MM-DD strings.
            'start-date': formatDateString(lastMonth),
            'end-date': formatDateString(today),
            // At this time, you need to explicitly specify channel==channelId.
            // See https://developers.google.com/youtube/analytics/v1/#ids
            ids: 'channel==' + channelId,
            dimensions: 'day',
            sort: 'day',
            // See https://developers.google.com/youtube/analytics/v1/available_reports
            // for details about the different filters and metrics you can request
            // if the "dimensions" parameter value is "day".
            metrics: 'views',
            filters: 'video==' + videoId
          });
    
          request.execute(function(response) {
            // This function is called regardless of whether the request succeeds.
            // The response contains YouTube Analytics data or an error message.
            if ('error' in response) {
              displayMessage(response.error.message);
            } else {
              displayChart(videoId, response);
            }
          });
        } else {
          // The currently authenticated user's channel ID is not available.
          displayMessage('The YouTube channel ID for the current user is not available.');
        }
      }
    

    אפשר למצוא מידע נוסף על הנתונים שניתן לאחזר ועל שילובי ערכים חוקיים של הפרמטרים metrics, dimensions ו-filters בדף הדוחות הזמינים במסמכי ה-API.

שלב 5: הצגת הנתונים מ-Analytics בתרשים

בשלב הזה מוסיפים את הפונקציה displayChart, ששולחת את הנתונים מ-YouTube Analytics ל-Google Visualization API. לאחר מכן, ה-API מציג את המידע בתרשים.

  1. טעינת ה-Google Visualization API, שמציג את הנתונים בתרשים. מידע נוסף על אפשרויות לתרשימים זמין במסמכי התיעוד בנושא API להצגה חזותית.

    google.load('visualization', '1.0', {'packages': ['corechart']});
    
  2. מגדירים פונקציה חדשה בשם displayChart, שמשתמשת ב-Google Visualization API כדי ליצור באופן דינמי תרשים שיציג את הנתונים ב-Analytics.

    
      // Call 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() function 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 as described at:
            // https://developers.google.com/chart/interactive/docs/reference#visdraw
            title: 'Views per Day of Video ' + videoId
          });
        } else {
          displayMessage('No data available for video ' + videoId);
        }
      }
    

הצגת הקובץ index.js המלא

הקובץ index.js כולל את כל השינויים מהשלבים שמפורטים למעלה. שוב, זכור כי עליך להחליף את המחרוזת YOUR_CLIENT_ID במזהה הלקוח של הבקשה הרשומה.


(function() {
  // Retrieve your client ID from the Google API Console at
  // https://console.cloud.google.com/.
  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;

  // Keep track of the currently authenticated user's YouTube channel 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']});

  // Upon loading, the Google APIs JS client automatically invokes this callback.
  // See https://developers.google.com/api-client-library/javascript/features/authentication 
  window.onJSClientLoad = function() {
    gapi.auth.init(function() {
      window.setTimeout(checkAuth, 1);
    });
  };

  // Attempt the immediate OAuth 2.0 client flow as soon as the page loads.
  // If the currently logged-in Google Account has previously authorized
  // the client specified as the OAUTH2_CLIENT_ID, then the authorization
  // succeeds with no user intervention. Otherwise, it fails and the
  // user interface that prompts for authorization needs to display.
  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) {
      // Authorization was successful. Hide authorization prompts and show
      // content that should be visible after authorization succeeds.
      $('.pre-auth').hide();
      $('.post-auth').show();

      loadAPIClientInterfaces();
    } else {
      // Authorization was unsuccessful. Show content related to prompting for
      // authorization and hide content that should be visible if authorization
      // succeeds.
      $('.post-auth').hide();
      $('.pre-auth').show();

      // Make the #login-link clickable. Attempt a non-immediate OAuth 2.0
      // client flow. The current function is 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 interfaces for the YouTube Analytics and Data APIs, which
  // are required to use the Google APIs JS client. More info is available at
  // https://developers.google.com/api-client-library/javascript/dev/dev_jscript#loading-the-client-library-and-the-api
  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();
      });
    });
  }

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

    request.execute(function(response) {
      if ('error' in response) {
        displayMessage(response.error.message);
      } else {
        // We need the channel's channel ID to make calls to the Analytics API.
        // The channel ID value has the form "UCdLFeWKpkLhkguiMZUp8lWA".
        channelId = response.items[0].id;
        // Retrieve the playlist ID that uniquely identifies the playlist of
        // videos uploaded to the authenticated user's channel. This value has
        // the form "UUdLFeWKpkLhkguiMZUp8lWA".
        var uploadsListId = response.items[0].contentDetails.relatedPlaylists.uploads;
        // Use the playlist ID to retrieve the list of uploaded videos.
        getPlaylistItems(uploadsListId);
      }
    });
  }

  // Call 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) {
    // See 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) {
          // The jQuery.map() function 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 information about each video.
          getVideoMetadata(videoIds);
        } else {
          displayMessage('There are no videos in your channel.');
        }
      }
    });
  }

  // Given an array of video IDs, this function obtains metadata about each
  // video and then uses that metadata to display a list of videos.
  function getVideoMetadata(videoIds) {
    // https://developers.google.com/youtube/v3/docs/videos/list
    var request = gapi.client.youtube.videos.list({
      // The 'id' property's 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 the #video-list element before starting
        // the loop.
        var videoList = $('#video-list');
        $.each(response.items, function() {
          // Exclude videos that do not 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>');
          // Setting the href value to '#' 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) {
          // Display a message if the channel does not have any viewed videos.
          displayMessage('Your channel does not have any videos that have been viewed.');
        }
      }
    });
  }

  // This function requests YouTube Analytics data for a video and displays
  // the 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 must be YYYY-MM-DD strings.
        'start-date': formatDateString(lastMonth),
        'end-date': formatDateString(today),
        // At this time, you need to explicitly specify channel==channelId.
        // See https://developers.google.com/youtube/analytics/v1/#ids
        ids: 'channel==' + channelId,
        dimensions: 'day',
        sort: 'day',
        // See https://developers.google.com/youtube/analytics/v1/available_reports
        // for details about the different filters and metrics you can request
        // if the "dimensions" parameter value is "day".
        metrics: 'views',
        filters: 'video==' + videoId
      });

      request.execute(function(response) {
        // This function is called regardless of whether the request succeeds.
        // The response contains YouTube Analytics data or an error message.
        if ('error' in response) {
          displayMessage(response.error.message);
        } else {
          displayChart(videoId, response);
        }
      });
    } else {
      // The currently authenticated user's channel ID is not available.
      displayMessage('The YouTube channel ID for the current user is not available.');
    }
  }

  // This boilerplate code takes a Date object and returns 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 the number
  //  as a string.
  function padToTwoCharacters(number) {
    if (number < 10) {
      return '0' + number;
    } else {
      return number.toString();
    }
  }

  // Call 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() function 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 as described at:
        // https://developers.google.com/chart/interactive/docs/reference#visdraw
        title: 'Views per Day of Video ' + videoId
      });
    } else {
      displayMessage('No data available for video ' + videoId);
    }
  }

  // This helper method displays a message on the page.
  function displayMessage(message) {
    $('#message').text(message).show();
  }

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