onYouTubeIframeAPIReady called once but multiple videos needed on a page - youtube-api

I am using a server-side method to drop in YouTube videos with playlists and functioning buttons (think of a website widget that can be called anyway on a page, and potentially more than once on the page).
I am using the IFrame API. I can get a single video to render by creating a new instance of YT.Player inside the onYouTubeIframeAPIReady() method. This makes sense to me - waiting for the library to be loaded. However when I want to have more than one video players on a page I don't know how to trigger the launch of the second, third, forth, etc.
I can't define another onYouTubeIframeAPIReady() method because it will overwrite the first. How is it possible to add more players to the page? It seems strange that there isn't a way to create more videos after this initial method has fired...
Documention on the above method is here:
https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player
Thanks in advance.
Edit: (for clarification following the first answer from Miha Lampret)
I can't declare additional players inside the onYouTubeIframeAPIReady() method because this code is introduced via a server side called e.g. a "widget". So rather than:
function onYouTubeIframeAPIReady() {
ytplayer1 = new YT.Player('player-youtube-1', {
width: '640',
height: '480',
videoId: 'M7lc1UVf-VE'
});
ytplayer2 = new YT.Player('player-youtube-2', {
width: '640',
height: '480',
videoId: 'smEqnnklfYs'
});
}
my code would equivalent to:
function onYouTubeIframeAPIReady() {
ytplayer1 = new YT.Player('player-youtube-1', {
width: '640',
height: '480',
videoId: 'M7lc1UVf-VE'
});
}
function onYouTubeIframeAPIReady() {
ytplayer2 = new YT.Player('player-youtube-2', {
width: '640',
height: '480',
videoId: 'smEqnnklfYs'
});
}
The onYouTubeIframeAPIReady() is only executed once. What I need to check is the whether is has already been executed once.

onYouTubeIframeAPIReady() is executed after YouTube API is ready to be used, that is after API's Javascript file http://www.youtube.com/iframe_api is loaded.
You can create more players inside onYouTubeIframeAPIReady()
var ytplayer1 = undef;
var ytplayer2 = undef;
function onYouTubeIframeAPIReady() {
ytplayer1 = new YT.Player('player-youtube-1', {
width: '640',
height: '480',
videoId: 'M7lc1UVf-VE'
});
ytplayer2 = new YT.Player('player-youtube-2', {
width: '640',
height: '480',
videoId: 'smEqnnklfYs'
});
}
Note that you need to declare ytplayer1 and ytplayer2 outside of onYouTubeIframeAPIReady() so you can use them later:
ytplayer1.pauseVideo();
ytplayer2.playVideo();

The way I solved this in the end was by allowing each server side widget included on the page to add the information to a global javascript array. They I used the onYouTubeIframeAPIReady() function to loop over that array to produce instantiate the YT players in turn.
/* the Global array to hold all my stuff */
var new_player_attributes = new Array();
function onYouTubeIframeAPIReady() {
for(key in new_player_attributes) {
var player = new YT.Player(key, new_player_attributes[key]);
}
}
How one goes about formatting this array is a trivial point. It is populated from javascript output to the document from the server side include. This function above and all the other generic utility functions that control the button etc are included only once. Only the definitions of the video parameters/playlist are the only bits included per interaction of the server side loop.

I've implemented enqueueOnYoutubeIframeAPIReady function for adding callbacks to queue, so you can add as many callbacks as you want. It will fire the callback immediately if API is ready.
(function () {
var isReady = false
var callbacks = []
window.enqueueOnYoutubeIframeAPIReady = function (callback) {
if (isReady) {
callback()
} else {
callbacks.push(callback)
}
}
window.onYouTubeIframeAPIReady = function () {
isReady = true
callbacks.forEach(function (callback) {
callback()
})
callbacks.splice(0)
}
})()
Usage:
enqueueOnYoutubeIframeAPIReady(function () {
var player = new YT.Player('player1', { ... })
})
// Second player
enqueueOnYoutubeIframeAPIReady(function () {
var player = new YT.Player('player2', { ... })
})

Related

An error occurred please try again later playback id, go to next video?

I have a simple Youtube API code to play videos in a playlist. All of a sudden I start getting the error like: an error occurred please try again later playback id: 2yVtrSo5yT1rs1EY
I did some searching and mainly found solutions for the PC user, like flushing cache/dns etc (I am on a windows laptop by the way).
Question: I was wondering however if it is possible to create a solution for this error(code), in the script in order to make it go to the next song? Or is this only a user based problem? I have an onPlayerError function that just makes the player go to the next song, whatever error occurs. However for the error mentioned above, it does nothing and just shows the error.
<?php
$yt_id='PLFgquLnL59anYA8FwzqNFMp3KMcbKwMaT';
$mymaxcounter = 100;
?>
<div id="player"></div>
<script>
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
var numPl = Math.floor((Math.random() * <?php echo $mymaxcounter;?>) + 1);
var playlistId = "<?php echo $yt_id; ?>";
// 3. This function creates an <iframe> (and YouTube player)
function onYouTubeIframeAPIReady() {
player = new YT.Player("player", {
height: '390',
width: '640',
playerVars: {
autoplay: 1,
loop: 1
},
events: {
'onReady': onPlayerReady,
'onError': onPlayerError
}
});
}
// onPlayerReady
function onPlayerReady(event){
//More player vars
player.loadPlaylist( {
listType: 'playlist',
list: playlistId,
index: numPl
} );
//Set shuffle
setTimeout(function() {
player.setShuffle({'shufflePlaylist' : true});
}, 1000);
}
// onPlayerError
function onPlayerError(){
player.nextVideo();
}
</script>

Load Youtube video in parent page in jquery

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.

YouTube embed showing device support option

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

YouTube iframe API - onReady and onStateChanged events not firing

I'm really having a frustrating time handling YouTube's iFrame API. Everything was working fine until yesterday, when I noticed my .playVideo() and .pauseVideo() functions throw an "undefined is not a function" error. Now, I can see that none of my functions appear to work... the events "onready" and "onstatechange" don't appear to be firing either. Here's my code:
function addVideo(index, url){
$.getScript('https://www.youtube.com/iframe_api', function(){
processPlayer();
});
function processPlayer(){
var videos = document.getElementById("videos");
var individ = document.createElement("div");
individ.setAttribute("class", "individ");
var vid = document.createElement("div");
vid.setAttribute("id","vid"+index);
individ.appendChild(vid);
videos.appendChild(individ);
var player;
function onYouTubeIframeAPIReady() {
console.log("Are we here at least?");
player = new YT.Player('vid'+index, {
height: '165',
width: '100%',
videoId: url,
playerVars: { 'controls': 0, 'showinfo': 0, 'rel': 0},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
window.players.push(player);
//individ.innerHTML+="Added by "+namesList[index];
individ.innerHTML+="<div style='float: left;'><span class='sname'>Let it Burn</span><br/><span class='aname'>Dave Matthews Band</span></div><div style='position: relative;'><img class='s_user' src='http://i.imgur.com/1AmnCp4.png'/></div>";
window.players.push(player);
}
onYouTubeIframeAPIReady();
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
//event.target.playVideo();
console.log("We're ready");
}
// 5. The API calls this function when the player's state changes.
// The function indicates that when playing a video (state=1),
// the player should play for six seconds and then stop.
var done = false;
function onPlayerStateChange(event) {
console.log("HI?");
if(event.data === 0) {
if(window.currentIndex < window.players.length-1){
var videoID = window.players[window.currentIndex].getVideoUrl().split("v=")[1];
window.players[window.currentIndex].cueVideoById(videoID);
window.currentIndex++;
window.players[window.currentIndex].playVideo();
}
} else if(event.data === 2 ){
onYouTubeIframeAPIReady();
}
if(!window.playing){
//alert('playing');
window.playing = true;
} else {
//alert('stopping');
window.playing = false;
}
}
function stopVideo() {
player.stopVideo();
}
}
}
Any ideas why? I'd really appreciate some help on this. The video itself loads fine, and the YTPlayer object can be called from console... yet these functions don't work, and onready/onstatechange don't fire. The iframes by default have the "origin=" bit in there, so that fix didn't work either.
I see several problems in your code, but I'm not sure which one of them is the one that's bothering you.
First of all, you're not supposed to call onYouTubeIframeAPIReady directly.
Instead, you should execute the following and let the browser do it asynchronously:
var scriptElement = document.createElement("script");
scriptElement.src = "http://www.youtube.com/iframe_api";
var firstScriptElement = document.getElementsByTagName("script")[0];
firstScriptElement.parentNode.insertBefore(scriptElement,firstScriptElement);
Second, I believe that you should initialize at least the following player parameters:
playerVars:
{
"enablejsapi":1,
"origin":document.domain,
"rel":0
},
events:
{
"onReady":onPlayerReady,
"onError":onPlayerError,
"onStateChange":onPlayerStateChange
}
Here is the complete relevant piece of code that I have been using:
<body onload="LoadYouTubeIframeAPI()">
<div id="player">Loading Video Player...</div>
<script type="text/javascript">
var player = null;
function LoadYouTubeIframeAPI()
{
var scriptElement = document.createElement("script");
scriptElement.src = "http://www.youtube.com/iframe_api";
var firstScriptElement = document.getElementsByTagName("script")[0];
firstScriptElement.parentNode.insertBefore(scriptElement,firstScriptElement);
}
function onYouTubeIframeAPIReady()
{
var playerParams =
{
playerVars:
{
"enablejsapi":1,
"origin":document.domain,
"rel":0
},
events:
{
"onReady":onPlayerReady,
"onError":onPlayerError,
"onStateChange":onPlayerStateChange
}
};
player = new YT.Player("player",playerParams);
}
function onPlayerReady(event)
{
...
}
function onPlayerError(event)
{
...
}
function onPlayerStateChange(event)
{
...
}
</script>
</body>

Always get latest video in YouTube playlist

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

Resources