|
| 1 | +(function() { |
| 2 | + // Retrieve your client ID from the {{ Google Cloud Console }} at |
| 3 | + // {{ https://cloud.google.com/console }}. |
| 4 | + var OAUTH2_CLIENT_ID = 'YOUR_CLIENT_ID'; |
| 5 | + var OAUTH2_SCOPES = [ |
| 6 | + 'https://www.googleapis.com/auth/yt-analytics.readonly', |
| 7 | + 'https://www.googleapis.com/auth/youtube.readonly' |
| 8 | + ]; |
| 9 | + |
| 10 | + var ONE_MONTH_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 30; |
| 11 | + |
| 12 | + // Keep track of the currently authenticated user's YouTube channel ID. |
| 13 | + var channelId; |
| 14 | + |
| 15 | + // For information about the Google Chart Tools API, see: |
| 16 | + // https://developers.google.com/chart/interactive/docs/quick_start |
| 17 | + google.load('visualization', '1.0', {'packages': ['corechart']}); |
| 18 | + |
| 19 | + // Upon loading, the Google APIs JS client automatically invokes this callback. |
| 20 | + // See http://code.google.com/p/google-api-javascript-client/wiki/Authentication |
| 21 | + window.onJSClientLoad = function() { |
| 22 | + gapi.auth.init(function() { |
| 23 | + window.setTimeout(checkAuth, 1); |
| 24 | + }); |
| 25 | + }; |
| 26 | + |
| 27 | + // Attempt the immediate OAuth 2.0 client flow as soon as the page loads. |
| 28 | + // If the currently logged-in Google Account has previously authorized |
| 29 | + // the client specified as the OAUTH2_CLIENT_ID, then the authorization |
| 30 | + // succeeds with no user intervention. Otherwise, it fails and the |
| 31 | + // user interface that prompts for authorization needs to display. |
| 32 | + function checkAuth() { |
| 33 | + gapi.auth.authorize({ |
| 34 | + client_id: OAUTH2_CLIENT_ID, |
| 35 | + scope: OAUTH2_SCOPES, |
| 36 | + immediate: true |
| 37 | + }, handleAuthResult); |
| 38 | + } |
| 39 | + |
| 40 | + // Handle the result of a gapi.auth.authorize() call. |
| 41 | + function handleAuthResult(authResult) { |
| 42 | + if (authResult) { |
| 43 | + // Authorization was successful. Hide authorization prompts and show |
| 44 | + // content that should be visible after authorization succeeds. |
| 45 | + $('.pre-auth').hide(); |
| 46 | + $('.post-auth').show(); |
| 47 | + |
| 48 | + loadAPIClientInterfaces(); |
| 49 | + } else { |
| 50 | + // Authorization was unsuccessful. Show content related to prompting for |
| 51 | + // authorization and hide content that should be visible if authorization |
| 52 | + // succeeds. |
| 53 | + $('.post-auth').hide(); |
| 54 | + $('.pre-auth').show(); |
| 55 | + |
| 56 | + // Make the #login-link clickable. Attempt a non-immediate OAuth 2.0 |
| 57 | + // client flow. The current function is called when that flow completes. |
| 58 | + $('#login-link').click(function() { |
| 59 | + gapi.auth.authorize({ |
| 60 | + client_id: OAUTH2_CLIENT_ID, |
| 61 | + scope: OAUTH2_SCOPES, |
| 62 | + immediate: false |
| 63 | + }, handleAuthResult); |
| 64 | + }); |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + // Load the client interfaces for the YouTube Analytics and Data APIs, which |
| 69 | + // are required to use the Google APIs JS client. More info is available at |
| 70 | + // http://code.google.com/p/google-api-javascript-client/wiki/GettingStarted#Loading_the_Client |
| 71 | + function loadAPIClientInterfaces() { |
| 72 | + gapi.client.load('youtube', 'v3', function() { |
| 73 | + gapi.client.load('youtubeAnalytics', 'v1', function() { |
| 74 | + // After both client interfaces load, use the Data API to request |
| 75 | + // information about the authenticated user's channel. |
| 76 | + getUserChannel(); |
| 77 | + }); |
| 78 | + }); |
| 79 | + } |
| 80 | + |
| 81 | + // Call the Data API to retrieve information about the currently |
| 82 | + // authenticated user's YouTube channel. |
| 83 | + function getUserChannel() { |
| 84 | + // Also see: https://developers.google.com/youtube/v3/docs/channels/list |
| 85 | + var request = gapi.client.youtube.channels.list({ |
| 86 | + // Setting the "mine" request parameter's value to "true" indicates that |
| 87 | + // you want to retrieve the currently authenticated user's channel. |
| 88 | + mine: true, |
| 89 | + part: 'id,contentDetails' |
| 90 | + }); |
| 91 | + |
| 92 | + request.execute(function(response) { |
| 93 | + if ('error' in response) { |
| 94 | + displayMessage(response.error.message); |
| 95 | + } else { |
| 96 | + // We need the channel's channel ID to make calls to the Analytics API. |
| 97 | + // The channel ID value has the form "UCdLFeWKpkLhkguiMZUp8lWA". |
| 98 | + channelId = response.items[0].id; |
| 99 | + // Retrieve the playlist ID that uniquely identifies the playlist of |
| 100 | + // videos uploaded to the authenticated user's channel. This value has |
| 101 | + // the form "UUdLFeWKpkLhkguiMZUp8lWA". |
| 102 | + var uploadsListId = response.items[0].contentDetails.relatedPlaylists.uploads; |
| 103 | + // Use the playlist ID to retrieve the list of uploaded videos. |
| 104 | + getPlaylistItems(uploadsListId); |
| 105 | + } |
| 106 | + }); |
| 107 | + } |
| 108 | + |
| 109 | + // Call the Data API to retrieve the items in a particular playlist. In this |
| 110 | + // example, we are retrieving a playlist of the currently authenticated user's |
| 111 | + // uploaded videos. By default, the list returns the most recent videos first. |
| 112 | + function getPlaylistItems(listId) { |
| 113 | + // See https://developers.google.com/youtube/v3/docs/playlistitems/list |
| 114 | + var request = gapi.client.youtube.playlistItems.list({ |
| 115 | + playlistId: listId, |
| 116 | + part: 'snippet' |
| 117 | + }); |
| 118 | + |
| 119 | + request.execute(function(response) { |
| 120 | + if ('error' in response) { |
| 121 | + displayMessage(response.error.message); |
| 122 | + } else { |
| 123 | + if ('items' in response) { |
| 124 | + // The jQuery.map() function iterates through all of the items in |
| 125 | + // the response and creates a new array that only contains the |
| 126 | + // specific property we're looking for: videoId. |
| 127 | + var videoIds = $.map(response.items, function(item) { |
| 128 | + return item.snippet.resourceId.videoId; |
| 129 | + }); |
| 130 | + |
| 131 | + // Now that we know the IDs of all the videos in the uploads list, |
| 132 | + // we can retrieve information about each video. |
| 133 | + getVideoMetadata(videoIds); |
| 134 | + } else { |
| 135 | + displayMessage('There are no videos in your channel.'); |
| 136 | + } |
| 137 | + } |
| 138 | + }); |
| 139 | + } |
| 140 | + |
| 141 | + // Given an array of video IDs, this function obtains metadata about each |
| 142 | + // video and then uses that metadata to display a list of videos. |
| 143 | + function getVideoMetadata(videoIds) { |
| 144 | + // https://developers.google.com/youtube/v3/docs/videos/list |
| 145 | + var request = gapi.client.youtube.videos.list({ |
| 146 | + // The 'id' property's value is a comma-separated string of video IDs. |
| 147 | + id: videoIds.join(','), |
| 148 | + part: 'id,snippet,statistics' |
| 149 | + }); |
| 150 | + |
| 151 | + request.execute(function(response) { |
| 152 | + if ('error' in response) { |
| 153 | + displayMessage(response.error.message); |
| 154 | + } else { |
| 155 | + // Get the jQuery wrapper for the #video-list element before starting |
| 156 | + // the loop. |
| 157 | + var videoList = $('#video-list'); |
| 158 | + $.each(response.items, function() { |
| 159 | + // Exclude videos that do not have any views, since those videos |
| 160 | + // will not have any interesting viewcount Analytics data. |
| 161 | + if (this.statistics.viewCount == 0) { |
| 162 | + return; |
| 163 | + } |
| 164 | + |
| 165 | + var title = this.snippet.title; |
| 166 | + var videoId = this.id; |
| 167 | + |
| 168 | + // Create a new <li> element that contains an <a> element. |
| 169 | + // Set the <a> element's text content to the video's title, and |
| 170 | + // add a click handler that will display Analytics data when invoked. |
| 171 | + var liElement = $('<li>'); |
| 172 | + var aElement = $('<a>'); |
| 173 | + // Setting the href value to '#' ensures that the browser renders the |
| 174 | + // <a> element as a clickable link. |
| 175 | + aElement.attr('href', '#'); |
| 176 | + aElement.text(title); |
| 177 | + aElement.click(function() { |
| 178 | + displayVideoAnalytics(videoId); |
| 179 | + }); |
| 180 | + |
| 181 | + // Call the jQuery.append() method to add the new <a> element to |
| 182 | + // the <li> element, and the <li> element to the parent |
| 183 | + // list, which is identified by the 'videoList' variable. |
| 184 | + liElement.append(aElement); |
| 185 | + videoList.append(liElement); |
| 186 | + }); |
| 187 | + |
| 188 | + if (videoList.children().length == 0) { |
| 189 | + // Display a message if the channel does not have any viewed videos. |
| 190 | + displayMessage('Your channel does not have any videos that have been viewed.'); |
| 191 | + } |
| 192 | + } |
| 193 | + }); |
| 194 | + } |
| 195 | + |
| 196 | + // This function requests YouTube Analytics data for a video and displays |
| 197 | + // the results in a chart. |
| 198 | + function displayVideoAnalytics(videoId) { |
| 199 | + if (channelId) { |
| 200 | + // To use a different date range, modify the ONE_MONTH_IN_MILLISECONDS |
| 201 | + // variable to a different millisecond delta as desired. |
| 202 | + var today = new Date(); |
| 203 | + var lastMonth = new Date(today.getTime() - ONE_MONTH_IN_MILLISECONDS); |
| 204 | + |
| 205 | + var request = gapi.client.youtubeAnalytics.reports.query({ |
| 206 | + // The start-date and end-date parameters must be YYYY-MM-DD strings. |
| 207 | + 'start-date': formatDateString(lastMonth), |
| 208 | + 'end-date': formatDateString(today), |
| 209 | + // At this time, you need to explicitly specify channel==channelId. |
| 210 | + // See https://developers.google.com/youtube/analytics/v1/#ids |
| 211 | + ids: 'channel==' + channelId, |
| 212 | + dimensions: 'day', |
| 213 | + sort: 'day', |
| 214 | + // See https://developers.google.com/youtube/analytics/v1/available_reports |
| 215 | + // for details about the different filters and metrics you can request |
| 216 | + // if the "dimensions" parameter value is "day". |
| 217 | + metrics: 'views', |
| 218 | + filters: 'video==' + videoId |
| 219 | + }); |
| 220 | + |
| 221 | + request.execute(function(response) { |
| 222 | + // This function is called regardless of whether the request succeeds. |
| 223 | + // The response contains YouTube Analytics data or an error message. |
| 224 | + if ('error' in response) { |
| 225 | + displayMessage(response.error.message); |
| 226 | + } else { |
| 227 | + displayChart(videoId, response); |
| 228 | + } |
| 229 | + }); |
| 230 | + } else { |
| 231 | + // The currently authenticated user's channel ID is not available. |
| 232 | + displayMessage('The YouTube channel ID for the current user is not available.'); |
| 233 | + } |
| 234 | + } |
| 235 | + |
| 236 | + // This boilerplate code takes a Date object and returns a YYYY-MM-DD string. |
| 237 | + function formatDateString(date) { |
| 238 | + var yyyy = date.getFullYear().toString(); |
| 239 | + var mm = padToTwoCharacters(date.getMonth() + 1); |
| 240 | + var dd = padToTwoCharacters(date.getDate()); |
| 241 | + |
| 242 | + return yyyy + '-' + mm + '-' + dd; |
| 243 | + } |
| 244 | + |
| 245 | + // If number is a single digit, prepend a '0'. Otherwise, return the number |
| 246 | + // as a string. |
| 247 | + function padToTwoCharacters(number) { |
| 248 | + if (number < 10) { |
| 249 | + return '0' + number; |
| 250 | + } else { |
| 251 | + return number.toString(); |
| 252 | + } |
| 253 | + } |
| 254 | + |
| 255 | + // Call the Google Chart Tools API to generate a chart of Analytics data. |
| 256 | + function displayChart(videoId, response) { |
| 257 | + if ('rows' in response) { |
| 258 | + hideMessage(); |
| 259 | + |
| 260 | + // The columnHeaders property contains an array of objects representing |
| 261 | + // each column's title -- e.g.: [{name:"day"},{name:"views"}] |
| 262 | + // We need these column titles as a simple array, so we call jQuery.map() |
| 263 | + // to get each element's "name" property and create a new array that only |
| 264 | + // contains those values. |
| 265 | + var columns = $.map(response.columnHeaders, function(item) { |
| 266 | + return item.name; |
| 267 | + }); |
| 268 | + // The google.visualization.arrayToDataTable() function wants an array |
| 269 | + // of arrays. The first element is an array of column titles, calculated |
| 270 | + // above as "columns". The remaining elements are arrays that each |
| 271 | + // represent a row of data. Fortunately, response.rows is already in |
| 272 | + // this format, so it can just be concatenated. |
| 273 | + // See https://developers.google.com/chart/interactive/docs/datatables_dataviews#arraytodatatable |
| 274 | + var chartDataArray = [columns].concat(response.rows); |
| 275 | + var chartDataTable = google.visualization.arrayToDataTable(chartDataArray); |
| 276 | + |
| 277 | + var chart = new google.visualization.LineChart(document.getElementById('chart')); |
| 278 | + chart.draw(chartDataTable, { |
| 279 | + // Additional options can be set if desired as described at: |
| 280 | + // https://developers.google.com/chart/interactive/docs/reference#visdraw |
| 281 | + title: 'Views per Day of Video ' + videoId |
| 282 | + }); |
| 283 | + } else { |
| 284 | + displayMessage('No data available for video ' + videoId); |
| 285 | + } |
| 286 | + } |
| 287 | + |
| 288 | + // This helper method displays a message on the page. |
| 289 | + function displayMessage(message) { |
| 290 | + $('#message').text(message).show(); |
| 291 | + } |
| 292 | + |
| 293 | + // This helper method hides a previously displayed message on the page. |
| 294 | + function hideMessage() { |
| 295 | + $('#message').hide(); |
| 296 | + } |
| 297 | +})(); |
0 commit comments