Using Youtube API V3, I can extract the thumbnails of videos from a user's activity feed (using the activities list from the api).
What I am trying to achieve is, when the user clicks on the video, the video should be played. I have looked at iframes. however the activities list on the api, does not show how to get the url for the video, but a different the Videos resource shows a player.embedHtml field, however I am confused, how to integrate it to my code.
var activityId, nextPageToken, prevPageToken, videoSnippet;
// Once the api loads call a function to get the uploads playlist id.
function handleAPILoaded() {
requestUserUploadsactivityId();
}
//Retrieve the uploads playlist id.
function requestUserUploadsactivityId() {
// https://developers.google.com/youtube/v3/docs/channels/list
var request = gapi.client.youtube.activities.list({
// mine: '' indicates that we want to retrieve the channel for the authenticated user.
home: 'true',
part: 'snippet'
});
request.execute(function(response) {
//structure of content.details
//https://developers.google.com/youtube/v3/docs/channels#resource
console.log(response);
activityId = response.items[0].id;
requestVideoPlaylist(activityId);
});
}
// Retrieve a playist of videos.
function requestVideoPlaylist(home, pageToken) {
$('#video-container').html('');
var requestOptions = {
home: 'true',
part: 'snippet',
maxResults: 12
};
if (pageToken) {
requestOptions.pageToken = pageToken;
}
var request = gapi.client.youtube.activities.list(requestOptions);
request.execute(function(response) {
var activityItems = response.result.items;
if (activityItems) {
// For each result lets show a thumbnail.
jQuery.each(activityItems, function(index, item) {
createDisplayThumbnail(item.snippet);
});
} else {
$('#video-container').html('Sorry you have no activities on your feed');
}
});
}
// Create a thumbnail for a video snippet.
function createDisplayThumbnail(videoSnippet) {
var titleEl = $('<h4>');
titleEl.addClass('video-title');
$(titleEl).html(videoSnippet.title);
var thumbnailUrl = videoSnippet.thumbnails.default.url;
console.log(videoSnippet);
var div = $('<div>');
div.addClass('video-content');
div.css('backgroundImage', 'url("' + thumbnailUrl + '")');
div.append(titleEl);
$('#video-container').append(div);
}
The activities list includes several activity types :
upload, like, favorite, comment, subscription, playlistItem, recommendation, bulletin, social ..
and only some kind of activities are related to a video. Then you can only retrieve the videoId from the contentDetails when the type of activity is related to a video. You can use the videoId to show the video.
https://developers.google.com/youtube/v3/docs/activities
You have a good example in "YouTube Topic Explorer". In this App you can retrieve the social activities and retrieve the videoId from this kind of activities
https://code.google.com/p/yt-topic-explorer/source/browse/app/scripts/controllers/logged-in.js
$scope.social = function() {
$rootScope.topicResults = [];
$rootScope.spinner.spin($('#spinner')[0]);
youtube({
method: 'GET',
service: 'activities',
params: {
part: 'id,snippet,contentDetails',
home: true,
maxResults: constants.YOUTUBE_API_MAX_RESULTS
},
callback: function(response) {
if ('items' in response) {
$scope.videoIds = [];
$scope.personalizedTopics = [];
angular.forEach(response.items, function(activity) {
if ((activity.snippet.type == constants.SOCIAL_TYPE)&&(activity.contentDetails.social.resourceId.videoId)){
$scope.videoIds.push(activity.contentDetails.social.resourceId.videoId);
}
});
}
getTopicsForVideoIds();
}
});
}
and you have an example of how to show the video:
https://code.google.com/p/yt-topic-explorer/source/browse/app/scripts/controllers/main.js
function playVideo(container, videoId) {
var width = container.offsetWidth;
var height = container.offsetHeight;
new YT.Player(container, {
videoId: videoId,
width: width,
height: height,
playerVars: {
autoplay: 1,
controls: 2,
modestbranding: 1,
rel: 0,
showInfo: 0
}
});
Related
I'm currently trying to use the Youtube V3 API to generate a feed of the most recently uploaded videos in a channel. Currently I've got this mostly working, however I can't for the life of me get two important bits of data pulled through - video views and video duration. I understand that these bits of data belong to the "videos" endpoint, but i'm not sure how to integrate this into the Javascript API example that was provided on Google's Youtube API website. Here is my code below - any help would be greatly appreciated:
// Define some variables used to remember state.
var playlistId, nextPageToken, prevPageToken;
// After the API loads, call a function to get the uploads playlist ID.
function handleAPILoaded() {
requestUserUploadsPlaylistId();
}
// Call the Data API to retrieve the playlist ID that uniquely identifies the
// list of videos uploaded to the currently authenticated user's channel.
function requestUserUploadsPlaylistId() {
// See https://developers.google.com/youtube/v3/docs/channels/list
var request = gapi.client.youtube.channels.list({
mine: true,
part: 'contentDetails, statistics'
});
request.execute(function(response) {
playlistId = response.result.items[0].contentDetails.relatedPlaylists.uploads;
requestVideoPlaylist(playlistId);
});
}
// Retrieve the list of videos in the specified playlist.
function requestVideoPlaylist(playlistId, pageToken) {
$('#video-container').html('');
var requestOptions = {
playlistId: playlistId,
part: 'snippet, contentDetails',
maxResults: 10
};
if (pageToken) {
requestOptions.pageToken = pageToken;
}
var request = gapi.client.youtube.playlistItems.list(requestOptions);
request.execute(function(response) {
// Only show pagination buttons if there is a pagination token for the
// next or previous page of results.
nextPageToken = response.result.nextPageToken;
var nextVis = nextPageToken ? 'visible' : 'hidden';
$('#next-button').css('visibility', nextVis);
prevPageToken = response.result.prevPageToken
var prevVis = prevPageToken ? 'visible' : 'hidden';
$('#prev-button').css('visibility', prevVis);
var playlistItems = response.result.items;
if (playlistItems) {
$.each(playlistItems, function(index, item) {
displayResult(item.snippet);
displayContentDetails(item.contentDetails);
displayStats(item.statistics);
});
} else {
$('#video-container').html('Sorry you have no uploaded videos');
}
});
}
// Create a listing for a video.
function displayResult(videoSnippet) {
var title = videoSnippet.title;
var videoId = videoSnippet.resourceId.videoId;
var publishedAt = videoSnippet.publishedAt;
var videoThumbURL = videoSnippet.thumbnails.high.url;
var publishedUTC = new Date(publishedAt);
var publishedDay = publishedUTC.getUTCDay();
var publishedMonth = publishedUTC.getUTCMonth();
var publishedYear = publishedUTC.getUTCFullYear();
var a = moment(publishedUTC);
var b = moment(Date.now());
var timeFrom = a.from(b);
$('#video-container').append('<li><img src='+videoThumbURL+'><br><p>' + title + ' - ' + timeFrom + '</p></li>');
}
// Create a listing for a video.
function displayContentDetails(stuff) {
var videoEndsAt = stuff.duration;
$('#video-container li').append('<span class="endsAt">'+ videoEndsAt +'</span>');
}
function displayStats(vidStats) {
var videoStats = vidStats.viewCount;
$('#video-container li').append('<span class="videoStats">'+ videoStats +'</span>');
}
// Retrieve the next page of videos in the playlist.
function nextPage() {
requestVideoPlaylist(playlistId, nextPageToken);
}
// Retrieve the previous page of videos in the playlist.
function previousPage() {
requestVideoPlaylist(playlistId, prevPageToken);
}
Although you said channel the code shows you pulling from a playlist, which does not have the data you want.
For each video from the playlist, you'll need to save the videoID, then make call(s) to the Videos: list endpoint (up to 50 ids at a time) with the 'parts' parameter of 'snippet,contentDetails,statistics'. Those API results will have the data you want.
Thanks #Johnh10, you were right, this is what I ended up doing.
var videoStatsEndpoint = "https://www.googleapis.com/youtube/v3/videos?part=statistics";
var videoToQuery = "&id="+videoId+"&key=";
var apiKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
var videoViews = 0;
jQuery.get(videoStatsEndpoint + videoToQuery + apiKey, function(data){
videoViews = data.items[0].statistics.viewCount;
console.log(videoViews);
});
To load a youtube player, I use the API in Jquery like this :
var youtube_player = document.createElement("div");
youtube_player.id = "youtube_player";
youtube_player.className = "youtube_player";
document.getElementById('media_container').appendChild(youtube_player);
var player = {
playVideo: function (container, videoId) {
if (typeof (YT) == 'undefined' || typeof (YT.Player) == 'undefined') {
window.onYouTubeIframeAPIReady = function () {
player.loadPlayer(container, videoId);
};
$.getScript('//www.youtube.com/iframe_api');
} else {
player.loadPlayer(container, videoId);
}
},
loadPlayer: function (container, videoId) {
new YT.Player(container, {
videoId: videoId,
width: 356,
height: 200,
playerVars: {
autoplay: 1,
controls: 1,
modestbranding: 0,
rel: 0,
showInfo: 0
}
});
}
};
var id_youtube = get_id_youtube_by_url(url_media);
player.playVideo(youtube_player.id, id_youtube);
And this code is working, a youtube player is created in the div 'media_container'.
But now, if I want to load a youtube player from an iframe in the parent page, it doesn't work.
I think that the problem comes from the attribute "container" in load_player.
But I don't know how to make this. Have you any idea ?
Thanks a lot for your help !
Not sure if this is helpful but try using the sample in this Github repo. It includes searching and playing youtube videos. You can try this live demo and compare with your project.
I embed Youtube videos in my angular app using two directives which make use of the YouTube Iframe API. The first loads the library async
angular.module('myApp')
.service('youTubeService', function($rootScope, $window) {
var self = this;
self.ready = false;
$window.onYouTubeIframeAPIReady = function () {
self.ready = true;
console.log("Youtube service ready");
$rootScope.$broadcast('youTubeServiceReady', true);
};
var tag = document.createElement('script');
tag.src = '//www.youtube.com/iframe_api';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
});
I then embed the video using the javascript library
angular.module('myApp')
.directive('youtube', function (youTubeService) {
return {
link: function (scope, element, attrs) {
var player;
var playerReady = false;
var playerState;
var callback;
var carouselScope = element.parent().parent().scope();
function createPlayer() {
player = new YT.Player(element[0], {
height: attrs.height,
width: attrs.width,
videoId: attrs.youtube,
playerVars: { 'start' : attrs.starttime, 'end' : attrs.endtime, 'origin': 'https://', showinfo: 0, modestbranding: 1 },
events: {
onReady: function () {
playerReady = true;
// if (callback !== null) {
// callback();
// }
},
onStateChange: function (event) {
//console.log("Time:" + getCurrentTime() + ", Duration:" + getDuration() );
playerState = event.data;
if (playerState === YT.PlayerState.PAUSED) {
carouselScope.play();
}
}
}
});
}
if (youTubeService.ready) {
createPlayer();
} else {
scope.$on('youTubeServiceReady', function () {
createPlayer();
});
}
...
This was working for months up until yesterday but now I get the following video as my embed in all desktop browsers as documented here
https://support.google.com/youtube/answer/6098135?hl=en-GB
My problem is I can't figure out what I should be changing because as far as I understand the iframe api is the correct one. Does anyone know what I should be changing?
So we were having the exact same issue with our site.
It turns out that our client, which uses code very similar to yours above is functioning correctly. Our problem ended up being the way in which we were adding videos and video meta data to our database.
This might not be your issue, but we were using
http://gdata.youtube.com/feeds/api/videos/<video id>?v=2&alt=json
to add videos to our system. As this turns out to be a deprecated endpoint, we had to upgrade to the v3 system which is explained here: https://developers.google.com/youtube/v3/docs/videos/list
I've followed this API reference :
https://developers.google.com/youtube/analytics/v1/sample-application
And created "index.html","index.css","index.js" files. I've inserted a proper CLIENT_ID but when I run "index.html" file it shows the following o/p :
I am new to web development and trying to understand API's. Can anyone please tell me how to make this code work?
Code for index.js is like this :
(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();
}
})();
I've got the same issue and found a possible solution at stackoverflow:
before: gapi.auth.authorize
put: gapi.client.setApiKey(API_KEY);
For me the error message dissappears then but now I get a blank website. Does anyone know how to solve this issue?
Thanks,
Martin
EDIT: this solved my problem: Youtube Analytics API 403 error
I have already modified some metrics but basically this should work:
(function() {
// Retrieve your client ID from the Google APIs Console at
// https://code.google.com/apis/console.
var OAUTH2_CLIENT_ID = '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: false
}, 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',
maxResults: 30
});
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': '2014-11-01',
'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: 'elapsedVideoTimeRatio',
// See https://developers.google.com/youtube/analytics/v1/available_reports for details
// on different filters and metrics you can request when dimensions=day.
metrics: 'audienceWatchRatio',
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
width: 800, height: 300, is3D: false,
title: 'Audience Retention per 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();
}
})();
I hope this helps.
Martin
Put your Api key in url like
"https://www.googleapis.com/qpxExpress/v1/trips/search?key=Your_Api_Key"
instead in header
try and run, this work in my case.....
I would like to always show the latest video from a playlist. So, only show one video on the page, but always the most recent of a playlist. When a user has uploaded a new video on YouTube, that latest video has to be shown on the webpage.
What I have so far:
HTML
<div id="yt-player"></div>
JS
<script src="http://www.youtube.com/player_api"></script>
<script>
// create youtube player
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player("yt-player", {
height: "480",
width: "853",
videoId: "br6xOdlyRbM"
});
}
</script>
However, this will only post a video with a specific ID and not from a playlist. I then tried the following JS.
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player("yt-player", {
height: "480",
width: "853",
playerVars: {
listType: "playlist",
list: "PLiXK3ub3Pc8_Tk0WiPpVTVmuzoZs8_SaY",
color: "white",
modestbranding: 1,
theme: "light"
},
events: {
"onStateChange": onPlayerStateChange
}
});
}
Unfortunately, this does not work either. The YouTube player is shown, but the first video is shown, and not the last. Live example here.
Getting the "last element" of a playlist might not always be what you want - it basically depends on the ordering of videos in the playlist. "Recent Uploads" is (obviously) ordered by upload date descending (newest first), others are by date ascending (oldest first).
In the latter case you have to iterate through all the pages of the playlist until you get to the last item.
There's an example that takes you almost to your target on https://developers.google.com/youtube/v3/code_samples/javascript (code excerpt copied from there):
// Retrieve the list of videos in the specified playlist.
function requestVideoPlaylist(playlistId, pageToken) {
$('#video-container').html('');
var requestOptions = {
playlistId: playlistId,
part: 'snippet',
maxResults: 10
};
if (pageToken) {
requestOptions.pageToken = pageToken;
}
var request = gapi.client.youtube.playlistItems.list(requestOptions);
request.execute(function(response) {
// Only show pagination buttons if there is a pagination token for the
// next or previous page of results.
nextPageToken = response.result.nextPageToken;
var nextVis = nextPageToken ? 'visible' : 'hidden';
$('#next-button').css('visibility', nextVis);
prevPageToken = response.result.prevPageToken
var prevVis = prevPageToken ? 'visible' : 'hidden';
$('#prev-button').css('visibility', prevVis);
var playlistItems = response.result.items;
if (playlistItems) {
$.each(playlistItems, function(index, item) {
displayResult(item.snippet);
});
} else {
$('#video-container').html('Sorry you have no uploaded videos');
}
});
}
The result value nextPageToken is the most interesting one. You have to fetch all pages in order until you get to the last one - in this example you'd have to call requestVideoPlaylist multiple times until response.result.nextPageToken is empty (as this indicates that you reached the last page). The last video in the result list response.result.items is the last video of the playlist (i.e. the most recent one if its ordered by date descending).
To reduce the number of requests (they tend to take some time...) you should increase maxResults in requestOptions to 50 (this is the highest value).
This leads to code like this:
function requestLastVideo(playlistId, callback, pageToken) {
var requestOptions = {
playlistId: playlistId,
part: 'snippet',
maxResults: 50
};
if (pageToken) {
requestOptions.pageToken = pageToken;
}
var request = gapi.client.youtube.playlistItems.list(requestOptions);
request.execute(function(response) {
var nextPageToken = response.result.nextPageToken;
if (nextPageToken) {
// we didn't reach the last page yet, fetch next one
requestLastVideo(playlistId, callback, nextPageToken);
return;
}
var playlistItems = response.result.items;
if (playlistItems) {
var lastPlaylistItem = playlistItems[playlistItems.length - 1];
callback(lastPlaylistItem.snippet);
} else {
alert('There are no videos');
}
});
}
And you'd call this like so:
requestLastVideo("PL91BF7E9AD6889246", function(snippet) {
console.log('Last video id was ', snippet.resourceId.videoId);
});
You can play around with requestOptions.part to reduce the footprint of your call. With this parameter you can control which fields are present in the response. You can find more information detailling possible values in the YouTube API docs for this call.
You need to get the latest video via API then plug it into your second solution like
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player("yt-player", {
height: "480",
width: "853",
videoId: {lastVideoIDinPLAYLIST},
playerVars: {
listType: "playlist",
list: "PLiXK3ub3Pc8_Tk0WiPpVTVmuzoZs8_SaY",
color: "white",
modestbranding: 1,
theme: "light"
},
events: {
"onStateChange": onPlayerStateChange
}
});
}