YouTube API not loading consistently - javascript

For the college I work for I am trying to get a youtube video to auto play in the banner of the webpage. The API is not loading every time properly. I am not sure what I could do to try to fix this.
I have tried:
Moving the adding of the youtube API script to only when the DOM is ready.
Placing the code on the top and bottom of the page.
Checking to see if the player object was created properly and if not try to recreate it which I learned breaks the API.
If I strip the page of everything and just have the banner the video loads more often but still not 100% of the time.
Here I provided the script on the page that is loading the video via the YouTube API.
Update:
From what I can tell by logging to the console the player object is not being fully initialized.
When it is not created correctly this is what the object print out looks like:
Y {b: null, a: null, h: null, closure_uid_165988549: 1, g: 1, …}
And here it is when is done correctly:
Y {b: Wa, a: iframe#ytplayer, h: div#ytplayer, closure_uid_16195269: 1, g: 1, …}
I was wrong about it not finding the videoId. The video is set every time before the function is called so it should not be an issue.
<div id="ytplayer"></div>
<script>
defer(function() {if(window.mobile === true) { $('#ytplayer').remove(); }});
var ready;
// Replace the 'ytplayer' element with an <iframe> and
// YouTube player after the API code downloads.
var player;
// var videoId = 'ZCESafUzSRo';
function onYouTubeIframeAPIReady() {
checkFunction();
}
function checkFunction() {
console.log("checkFunction called");
setYTready(ready);
}
function setYTready(ready) {
console.log("setYT ready called");
ready = true;
mtuPlayerCreate(player);
}
function mtuPlayerCreate(player) {
console.log("mtu create called");
if((player == undefined) && ( document.getElementById("play-yt") != null)) {
console.log("Creating player");
player = new YT.Player('ytplayer', {
height: '52.65%',
width: '100%',
playerVars: {
'controls': 0,
'showinfo': 0,
'rel': 0,
'iv_load_policy': 3
},
videoId: videoId,
events: {
'onReady': onPlayerReady,
// Removing the following if loop shouldn't be enabled
onStateChange:
function(e){
if (e.data === YT.PlayerState.ENDED) {
player.playVideo();
}
}
}
});
}
console.log("player redo below");
console.log(player);
if(player.A == false) {
//setTimeout(function() {location.reload();}, 2000);
} else {
return;
}
}
function onPlayerReady(event) { /* configuring default playing and mute settings */
console.log("on ready has been called")
var autoPlay = 1;
var autoMute = 1;
if(autoPlay === 1) { event.target.playVideo(); }
if(document.getElementById('play-yt') && autoPlay === 1) { document.getElementById('play-yt').className = 'pause'; }else if(document.getElementById('play-yt')) { document.getElementById('play-yt').className = 'play'; }
if(autoMute === 1) { event.target.mute(); }
if(document.getElementById('mute-yt') && autoMute === 1) { document.getElementById('mute-yt').className = 'mute'; }else if(document.getElementById('mute-yt')) { document.getElementById('mute-yt').className = 'loud'; }
if(document.getElementById('yt-yt')) { document.getElementById('yt-yt').className = ''; }
}
window.onload = function() { /* this handles what happens when the player and its buttons are clicked on */
document.getElementsByClassName('media-black')[0].onclick=function(e) {
if(e.target.id !== 'yt-yt' && e.target.id !== 'mute-yt') {
if(player.getPlayerState() === -1 || player.getPlayerState() === 2 || player.getPlayerState() === 5) {
player.playVideo();
if(document.getElementById('play-yt')) { document.getElementById('play-yt').className = 'pause'; }
if (window._gaq) _gaq.push(['_trackEvent', 'Wide Video', 'Played', window.location.href]);
}else{
player.pauseVideo();
if(document.getElementById('play-yt')) { document.getElementById('play-yt').className = 'play'; }
if (window._gaq) _gaq.push(['_trackEvent', 'Wide Video', 'Paused', window.location.href]);
}
}
else if(e.target.id === 'yt-yt' && e.target.id !== 'mute-yt') {
window.open('http://www.youtube.com/watch?v=' + videoId, '_blank');
player.pauseVideo();
if(document.getElementById('play-yt') && document.getElementById('play-yt').className === 'pause') { document.getElementById('play-yt').className = 'play'; }
if (window._gaq) _gaq.push(['_trackEvent', 'Wide Video', 'Went to YouTube', window.location.href]);
}else{
if(player.isMuted() || player.getVolume() === 0) { player.unMute(); player.setVolume(100); document.getElementById('mute-yt').className = 'loud'; }else{ player.mute(); document.getElementById('mute-yt').className = 'mute'; }
if (window._gaq) _gaq.push(['_trackEvent', 'Wide Video', 'Sound Adjusted', window.location.href]);
}
};
}
//
//This could be improved, test if it's mobile and load if not instead of loading and then removing if mobile
//
// Load the IFrame Player API code asynchronously.
document.addEventListener("DOMContentLoaded", function(event) {
console.log("dom ready!");
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
});
</script>
</div>
What it needs to do is:
Load the API
Load the player object with the given video ID
Add the iframe to the correct spot
Autoplay the video.
It needs to do this every time the page loads.

I'm not sure why you're loading the YouTube iframe player in that way, but, I made this jsfiddle - by modifying your code - and I get the YouTube Player iframe working.
These are the modifications I made - you can find more comments in the modified code:
I commented the line mtuPlayerCreate - are you trying to add another YouTube Iframe?
Add the onPlayerStateChange function for set the functionality for HTML elements - able to control the iframe - "play/pause, (un)mute the video".
The controls value was 0, I set it to 1 for check whether it was properly controlling the YouTube iframe by clicking the HTML elements.
This is the jsfiddle code:
// No need for replace the elemnt - YouTube Iframe Player will load in this object and HTML element:
var player;
// This is your videoId "you had it commented".
// it was the video is not available:
//var videoId = 'ZCESafUzSRo';
// Video: "Microsoft Windows Mixed Reality update | October 2018" - by Microsoft Hololens.
var videoId = '00vnln25HBg';
// Check whether iframe is mute.
var isUnMuted = false;
// Here, the YouTube Player API will build the iframe in the "ytplayer" HTML element
// and in the "player" variable.
function onYouTubeIframeAPIReady() {
player = new YT.Player('ytplayer', {
height: '52.65%',
width: '100%',
playerVars: {
'controls': 1,
'showinfo': 1,
'rel': 0,
'iv_load_policy': 3,
'autoplay': 1,
'loop': 1,
},
videoId: videoId,
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
// After loading the iframe, set the "playVideo" onclick event on "playButton" anchor element.
document.getElementById('play-yt').onclick = function() {
player.playVideo();
};
// Thanks to this answer, here you can check the status of "mute" - I culdn't make it work, in the "onPlayerStateChange" function though.
// Source: https://stackoverflow.com/a/36436765/4092887
document.getElementById('mute-yt').innerHTML = 'MUTE';
document.getElementById('mute-yt').onclick = function() {
player.mute();
};
document.getElementById('yt-yt').innerHTML = 'UNMUTE';
document.getElementById('yt-yt').onclick = function() {
player.unMute();
};
}
function checkFunction() {
console.log("checkFunction called");
setYTready(ready);
}
function setYTready(ready) {
console.log("setYT ready called");
// Not sure what you're doing here = do you want load another youtube iframe?
//mtuPlayerCreate(player);
}
function mtuPlayerCreate(player) {
console.log("mtu create called");
if ((player == undefined) && (document.getElementById("play-yt") != null)) {
}
console.log("player redo below");
console.log(player);
if (player.A == false) {
//setTimeout(function() {location.reload();}, 2000);
} else {
return;
}
}
/* configuring default playing and mute settings */
/* You only need set here the following line: */
function onPlayerReady(event) {
event.target.playVideo();
}
/* this handles what happens when the player and its buttons are clicked on */
/* You can do it in this way instead: */
function onPlayerStateChange(e) {
// Removing the following if loop shouldn't be enabled.
// This is for emulate "loop" functionality.
if (e.data === YT.PlayerState.ENDED) {
player.playVideo();
}
// If the video is PLAYING, set the onclick element for pause the video.
// Once the "playButton" is clicked, the video will be paused.
if (e.data == YT.PlayerState.PLAYING) {
document.getElementById('play-yt').innerHTML = 'PAUSE';
document.getElementById('play-yt').onclick = function() {
player.pauseVideo();
};
}
// If the video is PAUSED, set the onclick element for pause the video.
// Once the "playButton" is clicked, the video will resume the video = continue playing the video.
if (e.data == YT.PlayerState.PAUSED) {
document.getElementById('play-yt').innerHTML = 'PLAY';
document.getElementById('play-yt').onclick = function() {
player.playVideo();
};
}
}
//
//This could be improved, test if it's mobile and load if not instead of loading and then removing if mobile
//
// Load the IFrame Player API code asynchronously.
document.addEventListener("DOMContentLoaded", function(event) {
console.log("dom ready!");
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<div id="ytplayer"></div>
<p>
<div>
<span>Control your Youtube Iframe Player:</span>
</div>
<div id="play-yt">PLAY</div>
<div id="mute-yt">MUTE</div>
<div id="yt-yt">UNMUTE</div>
</p>

Related

Youtube iFrame API in ES6 / modules

I'm trying to load a YT video via the api. This official tutorial snippet works basically out of the box when I output it directly onto a page. It's a different story when I try and incorporate it into my JS workflow with modules / webpack.
Questions:
How do I make these methods (play / mute / setLoop / stop / etc) available globally?
How do I create the script tag first and then run the onYouTubeIframeAPIReady function?
Thanks all. Hope this isn't a completely useless question!
<script>
// 1. Load up the script tag
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 2. Creates an <iframe> (and YouTube player) after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('a-spot-video-background', {
width: '1280',
height: '720',
videoId: 'T8v-gQeQZJY',
playerVars: {
autoplay: 1,
controls: 0,
modestbranding: 1,
rel: 0,
showinfo: 0,
loop: 1,
disablekb: 1,
color: 'white'
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 3. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.mute();
event.target.playVideo();
event.target.setLoop();
}
// 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) {
if (event.data == YT.PlayerState.PLAYING && !done) {
//setTimeout(stopVideo, 10000);
player.playVideo();
done = true;
}
}
function stopVideo() {
player.stopVideo();
}
</script>
First question is answered in comment. This answers to second one:
function loadScript(scriptUrl, globalName) {
if (globalName && window[globalName])
return Promise.resolve();
return new Promise((resolve, reject) => {
let scr = document.createElement('script');
scr.type = "text/javascript";
scr.src = scriptUrl;
document.getElementsByTagName('head')[0].appendChild(scr);
scr.onload = (() => {
!globalName || window[globalName] ?
resolve() : reject(Error('window.' + globalName + ' undefined'));
});
scr.onerror = () => reject(Error('Error loading ' + globalName||scriptUrl));
});
}
var url = "https://code.jquery.com/jquery-3.3.1.min.js";
loadScript(url, "$")
.then(() => console.log('jQuery loaded. Now I can call any jQuery method.'));
or you can use just script.onload event listener without promise. I like promise version because it gives you some additional options and has clear flow (without nesting or referencing across whole file).

How to detect when Iframe video ends? No API

I have a iframe generated on my site. The src of the iframe is video id from database. I would like to know, how can I detect that the video finishes playing.
My embeded youtube video:
echo "<iframe width='50%' height='60%' id='cc' src='https://www.youtube.com/embed/" . $data_song[0] . "' frameborder='1' allowfullscreen></iframe><br>";
Thanks for any advice.
Okay, Since you are using the iframe to stream youtube videos, what you want can be achieved by this,
When Player.Status = 0 that means the video has stopped. I have commented those for you to understand easily.
<script type="text/javascript">
var player;
$(document).ready(function () {
onYouTubePlayerAPIReady();
});
function onYouTubePlayerAPIReady() {
player = new YT.Player('ytplayer', {
events: {
'onStateChange': getStatus
}
});
}
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
function getStatus() {
var sStatus;
sStatus = player.getPlayerState();
if (sStatus == -1) {} //Video has not started yet
else if (sStatus == 0) {
// video has stopped. This is the event that you want
}
else if (sStatus == 1) { } //Video is playing
else if (sStatus == 2) {} //Video is paused
else if (sStatus == 3) { } //video is buffering
else if (sStatus == 5) { } //Video is cued.
}
</script>
If you use a tag <video> is better.
For example:
Your HTML
<video id="videoplayer">.
Your JS
var myvideo = document.getElementById('videoplayer');
myvideo.addEventListener('ended',function() {
// The video's callback
} ,false);

YouTube API - Dynamically add event listeners

I'm using the Youtube API to restyle embedded videos which are dynamically added to the page.
I'm having trouble figuring out how to set up the custom controls. Each video embed has a custom control which needs to fire the playVideo() function on the relevant embed.
When each player is initialised it fires the onPlayerReady function. My problem is I don't know how to bind a click event to the new players custom button which will fire the playVideo() function for the correct player.
I've done a lot of searching on here and can't find reference to working with multiple embeds.
FINAL UPDATE
As jQuery is available, i'm using it in this solution...
function onPlayerReady(event) {
// bind events
var playButton = $(event.target.c).parent().find('.immersive-video__play');
playButton.on('click', function() {
event.target.playVideo();
});
}
UPDATE
This is my current working solution...
var buttonCount = 0;
function onPlayerReady(event) {
// bind events
var playButtons = document.getElementsByClassName("immersive-video__play");
playButtons[buttonCount].addEventListener('click', function() {
event.target.playVideo();
});
buttonCount++;
}
ORIGINAL
function onPlayerReady(event) {
// bind event
var playButtons = document.getElementsByClassName("immersive-video__play");
// I don't know how to link the button to the player?
playButtons[].addEventListener('click', function() {
player[].playVideo();
});
}
var player = [];
function checkYT() {
var check = setInterval(function() {
if (typeof YT !== 'undefined' && typeof YT.Player !== 'undefined') {
var tar = document.getElementsByClassName('immersive-video__embed');
for (var i = 0; i < tar.length; i++) {
var id = tar[i].dataset.video;
var container = document.getElementsByClassName('video-holder');
player[i] = new YT.Player(container[i], {
videoId: id,
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
clearInterval(check);
return;
} else {
console.log('not ready');
}
}, 20);
}
Make sure that the iframe src URL has ?enablejsapi=1 at the end like this:
<iframe src="//www.youtube.com/embed/FKWwdQu6_ok?enablejsapi=1" frameborder="0" allowfullscreen id="video"></iframe>
Setting the parameter's value to 1 enables the player to be controlled via IFrame or JavaScript Player API calls. The default value is 0, which means that the player cannot be controlled using those APIs.
Check your Player object which has the ability to control that video. Create it using the id attribute on that iframe. Here is an example from this tutorial:
var player;
function onYouTubePlayerAPIReady() {
// create the global player from the specific iframe (#video)
player = new YT.Player('video', {
events: {
// call this function when player is ready to use
'onReady': onPlayerReady
}
});
}
Then check the "player ready" callback and bind events. It will automatically be passed the event object, in which event.target is the player.
function onPlayerReady(event) {
// bind events
var playButton = document.getElementById("play-button");
playButton.addEventListener("click", function() {
player.playVideo();
});
var pauseButton = document.getElementById("pause-button");
pauseButton.addEventListener("click", function() {
player.pauseVideo();
});
}
Hope this helps!

Rails & Youtube API: Issues loading iframe

I'm trying to implement the Youtube Iframe API into my Rails app. I'm trying to replicate the exact example on the Youtube Iframe API instructions page.
Created a yt_player.js file:
$(function() {
alert('this gets called')
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
alert('this does not get called')
player = new YT.Player('player-wrapper', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.playVideo();
}
// 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) {
if (event.data == YT.PlayerState.PLAYING && !done) {
setTimeout(stopVideo, 6000);
done = true;
}
}
function stopVideo() {
player.stopVideo();
}
});
And my view:
<div id="player-wrapper"></div>
I was expecting this to work but no video is being loaded. No errors showing in console.
What am I doing wrong?
First of all, I'm pretty sure your window.player and window.onYouTubeIframeAPIReady are undefined when you check in the browser console.
The reason is the way youtube iframe api work, those above variables must be defined globally.
Let check this document
They say:
The onYouTubeIframeAPIReady function will execute as soon as the player API code downloads. This portion of the code defines a global variable, player, which refers to the video player you are embedding, and the function then constructs the video player object.
So the changes will be: (define youtube variables and callbacks globally)
// Your current code
window.player = null; // <-- change here
window.onYouTubeIframeAPIReady = function() { // <-- change here
alert('this does not get called')
player = new YT.Player('player-wrapper', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
window.onPlayerReady = function(event) { // <-- change here
event.target.playVideo();
}
window.done = false; // <-- change here
window.onPlayerStateChange = function(event) { // <-- change here
if (event.data == YT.PlayerState.PLAYING && !done) {
setTimeout(stopVideo, 6000);
done = true;
}
}
window.stopVideo = function() { // <-- change here
player.stopVideo();
}

How can I play two Youtube videos without linking to a playlist?

I'm very new to Javascript. I'm embedding a youtube iframe here:
http://www.heartlandpokertour.com/indexbeta.php
Using this documentation:
http://code.google.com/apis/youtube/iframe_api_reference.html
I would like a second video to play, looped, after the first one is finished. I'm unable to set specific video parameters if I try and link/set the video id to a Youtube playlist.
I'm assuming I need to setup an event listener and function to trigger when YT.PlayerState.ENDED (or 0) is broadcast, but NO IDEA how to do this.
Update: Here is the current code I'm using for the player:
<script>
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
height: '258',
width: '406',
videoId: 'MsTQNZmq5Pk',
playerVars: { 'autoplay': 0, 'rel': 0, 'showinfo': 0, 'egm': 0, 'showsearch': 0, },
events: {
'onStateChange': onPlayerStateChange,
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
player.stopVideo();
player.videoId = 'c5dTlLk5_x8';
player.playVideo();
}
}
</script>
In the first code listing on the api website, this event might help you:
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING && !done) {
setTimeout(stopVideo, 6000);
done = true;
}
}
You could change it to something like this (not tested I'm afraid):
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
player.stopVideo();
player.videoId = 'some id';
player.playVideo();
}
}
This would be the most logical in my opinion, but then again I'm not sure whether different videos can be played within one player instance. I've never come across your scenario I'm afraid.
Edit: You could try creating a new instance of the player. So basically you'd just create a new player, which will play the second video:
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
player.stopVideo();
player = new YT.Player('player', {
height: '258',
width: '406',
videoId: 'some id',
playerVars: { 'autoplay': 0, 'rel': 0, 'showinfo': 0, 'egm': 0, 'showsearch': 0, },
events: {
//'onStateChange': onPlayerStateChange, // not add this line because it would again create a new player after the second has stopped I guess!
}
});
player.playVideo();
}
}
I have got this working on my site, first you can stop the current video playing, then load in a new video with the loadVideoById() function from the Youtube API
function swapVideo() {
player.stopVideo();
player.loadVideoById(youtube_id);
}

Categories

Resources