یک نمونه برنامه بسازید

این صفحه شما را در مراحل ساخت اپلیکیشنی راهنمایی می کند که از چندین API مختلف برای نمودار آمار مشاهده ویدیوهای یوتیوب کاربر استفاده می کند. برنامه وظایف زیر را انجام می دهد:

  • از YouTube Data API برای بازیابی لیستی از ویدیوهای آپلود شده کاربر تأیید شده فعلی استفاده می کند و سپس لیستی از عناوین ویدیو را نمایش می دهد.
  • وقتی کاربر روی یک ویدیوی خاص کلیک می‌کند، برنامه YouTube Analytics API را برای بازیابی داده‌های تحلیلی آن ویدیو فراخوانی می‌کند.
  • این برنامه از Google Visualization API برای نمودار داده های تجزیه و تحلیل استفاده می کند.

مراحل زیر روند ساخت اپلیکیشن را شرح می دهد. در مرحله 1، فایل های HTML و CSS برنامه را ایجاد می کنید. مراحل 2 تا 5 بخش های مختلف جاوا اسکریپت را که برنامه از آن استفاده می کند، توضیح می دهد. کد نمونه کامل نیز در انتهای سند درج شده است.

  1. مرحله 1: صفحه HTML و فایل CSS خود را بسازید
  2. مرحله 2: احراز هویت OAuth 2.0 را فعال کنید
  3. مرحله 3: بازیابی اطلاعات برای کاربر وارد شده در حال حاضر
  4. مرحله 4: درخواست داده های Analytics برای یک ویدیو
  5. مرحله 5: نمایش داده های تجزیه و تحلیل در نمودار

مهم: برای دریافت شناسه مشتری OAuth 2.0 برای برنامه خود باید برنامه خود را در Google ثبت کنید .

مرحله 1: صفحه HTML و فایل CSS خود را بسازید

در این مرحله، یک صفحه HTML ایجاد می‌کنید که کتابخانه‌های جاوا اسکریپتی را که برنامه استفاده خواهد کرد، بارگیری می‌کند. 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 برای بارگیری Google Visualization API استفاده می کند که برای نمودار داده های تحلیلی بازیابی شده استفاده می شود.
  • کتابخانه index.js شامل توابع خاص برنامه نمونه است. این آموزش شما را از طریق مراحل ایجاد آن توابع راهنمایی می کند.
  • Google APIs Client Library برای جاوا اسکریپت به شما کمک می کند تا احراز هویت 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 خود اضافه می‌کنید که فید ویدیوهای آپلود شده کاربر فعلی را با استفاده از YouTube Data API (v2.0) بازیابی می‌کند. آن فید، شناسه کانال یوتیوب کاربر را مشخص می‌کند که هنگام تماس با YouTube Analytics API به آن نیاز خواهید داشت. علاوه بر این، برنامه نمونه ویدیوهای آپلود شده کاربر را فهرست می کند تا کاربر بتواند داده های Analytics را برای هر ویدیوی جداگانه بازیابی کند.

تغییرات زیر را در فایل index.js خود اعمال کنید:

  1. تابعی را اضافه کنید که رابط مشتری را برای YouTube Analytics و Data API بارگیری کند. این یک پیش نیاز برای استفاده از سرویس گیرنده جاوا اسکریپت 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. متغیری اضافه کنید که محدوده تاریخ پیش‌فرض را برای داده‌های گزارش تحلیلی بازیابی شده مشخص می‌کند.

    
      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: نمایش داده های تجزیه و تحلیل در نمودار

در این مرحله، تابع displayChart را اضافه می‌کنید که داده‌های YouTube Analytics را به Google Visualization API ارسال می‌کند. آن API سپس اطلاعات را نمودار می کند.

  1. Google Visualization API را بارگیری کنید، که داده های شما را در نمودار نمایش می دهد. برای جزئیات بیشتر در مورد گزینه های نمودار، به مستندات Visualization 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();
  }
})();