I've spent the day banging my head on this. I've made some good progress, but this last part has me stuck.
I'm using YouTube's API. I have a listener event configured, and the video autoplays, and autocloses on completion. All of that works great.
What I want to do is have more than one video on a single page (5, actually) assigned by DIV. Is there a way to do that with this code?
// create youtube player
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
height: '488',
width: '800',
videoId: '49QKXdRTX-A',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// autoplay video
function onPlayerReady(event) {
event.target.playVideo();
}
// when video ends
function onPlayerStateChange(event) {
if(event.data === 0) {
$("#player").hide();
}
}
Additionally, it's worth mentioning that I will have a couple of these videos auto-show another DIV on completion, so something like this:
function onPlayerStateChange(event) {
if(event.data === 0) {
$("#player").hide();
$("#gallery-main").show();
ANY help would be greatly appreciated. Thanks!
Related
When I create an instance of YT.Player(HTMLIFrameElement, { options }) I get back an object that has:
destroy
setSize
getIframe
addEventListener
getVideoEmbedCode
getOptions
getOption
But not playVideo, pauseVideo etc as described in the documentation.
I got a demo here: http://siesta-annotations.surge.sh/Siesta_webviewer_test/?page=3
I am creating the iframes via the DOM in trait.playable.youtube.js and adding the iframe to a documentFragment that eventually will be added to a div:
const element = document.createElement('iframe')
element.src = `https://www.youtube.com/embed/${id}?rel=0;&autoplay=${this.options.autostart ? 1 : 0};&enablejsapi=1;&origin=${location.hostname};`
element.style = this.inlineStyle
I then create an instance of YT.Player:
// nasty initialization because we're outside webpack and this is a demo
if (global.YT.loaded) {
this.player = new global.YT.Player(element, {
events: {
'onStateChange': this.stateHandler,
'onReady': onPlayerReady
}
})
} else {
const oldHandler = global.onYouTubeIframeAPIReady
global.onYouTubeIframeAPIReady = () => {
if (oldHandler) oldHandler()
this.player = new global.YT.Player(element, {
events: {
'onStateChange': this.stateHandler,
'onReady': onPlayerReady
}
})
}
}
The instance of YT.Player looks something like this:
It looks like the minification process went very wrong. What am I doing wrong - how should I initialise YT.Player for my use-case?
This is my second post, I hope that I can solve your problem.
I am not 100% sure about how your code works, but I suspect ...
It seems like a double instantiation of the YT.Player object, which will cause the YT.Player object unstable.
I myself also struggled for a long time (ps: I am an amateur programmer.😅)
As an example, the following snippets is modifed from Youtube Iframe API website and an example of how the second instantiation will break the player object try it! JS Bin:
<!DOCTYPE html>
<html>
<body>
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>
</br>
<button onclick="instantiateOneMore()">
Call
<pre>new YT.Player('player')</pre>
</button>
<button onclick="pauseVideo()">
pauseVideo();
</button>
<script>
// 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() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
let callTime = 0;
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.playVideo();
callTime ++;
console.log(callTime);
//document.querySelector('#dd').innerHTML = player.pauseVideo;
}
function pauseVideo(){
player.pauseVideo();
}
instantiateOneMore = () => {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
//document.querySelector('#dd').innerHTML = player.pauseVideo;
}
// 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();
} */
</script>
</body>
</html>
Furthermore, the second onPlayerReady(), won't even be called. So if you intend to put some counter to see how many times the new YT.Player(...) is called, you will suspect it only be called once. Which makes the effect ghost away from debugging.
Strangely enough you do not get access to the YTPlayer API until 'onReady' is called in the constructor player options object. https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player
function onYouTubeIframeAPIReady() {
new YT.Player('player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': function (event) {
onPlayerReady(event.target)
},
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(player) {
player.playVideo();
}
This of course only works for one player, so you need to roll out your own YouTube player manager if you want to handle multiple players. (I know the API is insane)
A quick solution could be the following if you have iframes with youtube videos (untested):
const YTManager = {
videos: [],
youtubeApiReady = false,
initialize () {
const matchId = /[^/]+$/
this.youtubeApiReady = true
document.querySelectorAll('iframe[src*="youtube"]')
.forEach(iframe => this.videos.push({
dom: iframe,
player: null,
videoId: iframe.src.exec(matchId) ||Â iframe.src.exec(matchId)[0]
}))
},
createPlayers () {
if (this.youtubeApiReady === false) return
this.videos.forEach(v => {
new YT.Player(v.dom.id, {
videoId: v.videoId,
events: {
'onReady': function (event) {
v.player = event.target
}
}
})
})
},
find (id) {
return this.videos.find(v => v.id === id)
},
play (id) {
const video = this.find(id)
if (this.youtubeApiReady && video && video.player) {
video.playVideo()
}
},
pause (id) {
const video = this.find(id)
if (this.youtubeApiReady && video && video.player) {
video.pauseVideo()
}
},
}
window.onYouTubeIframeAPIReady = function onYouTubeIframeAPIReady() {
// YouTube client side script has been loaded
YTManager.youtubeApiReady = true
YTManager.createPlayers()
}
document.addEventListener("DOMContentLoaded", function() {
// all iframes has been parsed by the browser
YTManager.initialize()
});
Of course you can not call YTManager.play or YTManager.pause without having the ID and wait for the YouTube script to load and then the video initialization to finish. All in all, a real mess. I'm sure you can come up with a better manager. I have written one at work that is better but also works with completely different objects and requirements, so it's not a good fit for general purpose YouTube video manager. But the one above is the gist of my current player.
I recommend using iframes from the beginning - if anything goes wrong in your script, the videos will still be playable, you will just not have any control of them.
Iframed youtube videos looks like this:
<iframe
id="uniqueDOMElementID1"
width="560"
height="315"
src="https://www.youtube.com/embed/bHQqvYy5KYo"
frameborder="0"
allow="autoplay; encrypted-media"
allowfullscreen></iframe>
I have a list of videos in an array and I want to be able to make the player play another video from the list after the current one ends. The problem I'm having is that it repeats the same video once its ends instead of selecting a new video to play. I've been looking for similar questions and havent been able to find anything to make it work. Any advice would be appreciated!
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.
function rotateYT() {
var videos = [
'be0T_owA1PU',
'Kp7eSUU9oy8',
'Th0V-fxo9CE',
];
var index=Math.floor(Math.random() * videos.length);
return videos[index];
}
var player;
function onYouTubeIframeAPIReady() {
var videoID = rotateYT();
player = new YT.Player('player', {
height: '300',
width: '300',
videoId: videoID,
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
player.setPlaybackRate(1);
event.target.playVideo();
}
// 5. This should play another random video
function onPlayerStateChange(event) {
if(event.data === 0) {
event.target.setShuffle(true);
event.target.playVideo();
}
}
I probably put the code in wrong so here is the jsfiddle ive been working on
https://jsfiddle.net/bbasham/L20k7x1o/2/
Your issue is that you are not telling the YT player to use a playlist of videos. Instead, you are only setting one video ID at a time, therefore, the shuffle method would not work. You need to update your method so that you swap the video ID before restarting:
// 5. This should play another random video
function onPlayerStateChange(event) {
if(event.data === 0) {
//event.target.setShuffle(true); //useless unless the player has a playlist
event.target.loadVideoById( rotateYT() );
event.target.playVideo();
}
}
That will cause the player to grab a new video and set it as the source before restarting the player. Alternatively you could provide a playlist and then use shuffle.
Documentation
This is a video that plays on my site but the user can't actually see it, they can only hear it. I want to create a button so that if the user want's to mute the video at anytime then they can just select that button and all sound on the site will stop but if they click it again then they should be able to hear it again. If your struggling to understand what I mean please post a comment as it would be really helpful. Thanks!
// create youtube player
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
height: '0',
width: '0',
videoId: 'a5SMyfbWYyE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// autoplay video
function onPlayerReady(event) {
event.target.playVideo();
}
// when video ends
function onPlayerStateChange(event) {
if(event.data === 0) {
//alert('done');
}
}
First create a button in your html with an ID :
<input id="mute-toggle" type="button"/>
And get the ID with JQuery :
$('#mute-toggle').on('click', function() {
var mute_toggle = $(this);
if(player.isMuted()){
player.unMute();
mute_toggle.text('volume_up');
}
else{
player.mute();
mute_toggle.text('volume_off');
}
});
I'm building a website for my client who wants YouTube embedded video with external control. This is the code.
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
height: '100%',
width: '100%',
playerVars: {rel: 0},
videoId: 'myvideoid',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerStateChange(event) {
if(event.data === 0) {
player.stopVideo();
}
$(document).ready(function() {
$( '.play-btn' ).on( 'click', function() {
if ($('.video').hasClass('mobile')) {
}
else {
player.seekTo(0, true);
player.playVideo();
}
});
This works fine on modern browsers, but on IE9 I have a problem: video starts playing when clicked. However, when video reaches it's end and I press external button again, I get "Error occurs" message and only video sound starts to play.
Any idea what might cause this?
EDIT
onError event gives error code 5, which is, as I've understood, HTML5 related. Error also occurs at the end of the video, not at the beginning, as I first thought.
Removing unnecessary stopVideo() function solved the problem. The video was already stopped at the end, when the function was called and apparently that caused the error.
This solved the problem in both IE9 and IE10.
I load and play a youtube video in a fancybox. It all works perfectly, except when I close the fancybox it starts the video again.
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
height: '480',
width: '853',
videoId: 'I76i-TCaUCY',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// autoplay video
function onPlayerReady(event) {
event.target.playVideo();
}
// when video ends
function onPlayerStateChange(event) {
if (event.data === 0) {
$.fancybox.close();
}
}
The user should be able to play it again if needed, so I can't delete it.
How do I play it for the first time only?
I don't know if there is an easier way, but this is how I have fixed it for my needs:
I added var played = false and:
// autoplay video
function onPlayerReady(event) {
//This ensures that it is autoplayed only once.
if (!played) {
event.target.playVideo();
played = true;
}
}
It now works as I was expecting.