I am using 'angualr-youtube-embed' directive to embed youtube player in my angular web app. In that I have to identify play and pause and volume change events. To listen play and pause events I am using the below given code.
$scope.$on('youtube.player.playing', function ($event, player) {
// to do functions when the video is playing.
});
$scope.$on('youtube.player.paused', function ($event, player) {
// to do functions when the video is paused.
});
Now my requirement is, I want to do some works while changing volume in youtube player, I need to identify that volume change event. But I have no idea about how to listen the volume change in youtube player. How can I solve this issue?
Thanks in advance.
Just in case if anyone has the same question, here is my full answer.
For today the code looks like:
setInterval(this.getChangedVolume, 250)
getChangedVolume () {
let currentYoutubeVolume = this.player.getVolume()
// Do some things, for example (will show Promise):
// console.log(currentYoutubeVolume)
// YouTube returns Promise, but we need actual data, so:
// Promise.resolve(currentYoutubeVolume).then(data => { this.volumeLv = data })
}
player.getVolume():Number
Returns the player's current volume, an integer between 0 and 100. Note that getVolume() will return the volume even if the player is muted.
For more Detail check this: YouTube Player Controls
Related
I'm building an online 'TV' which will use YouTube live-streams for multiple channels.
The channels are contained within tabs. The videos need to be stopped when changing tabs otherwise you can hear the audio in the background.
Here's a link to the JSFiddle: https://jsfiddle.net/matlow/08k4csuh/
I've managed to turn the 'Channel 1' off when changing to another channel with:
var iframe = document.getElementsByClassName("tvscreen")[0].contentWindow;
and
iframe.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*');
In the tab javascript for loop which also handles the tabcontent[i].style.display = "none";
I think I need to use the for loop to call each instance of the iframe... but I'm quite new to javascript so I'm not quite sure how to achieve this.
It will also help to use iframe.postMessage('{"event":"command","func":"playVideo","args":""}', '*'); so the video plays automatically again when clicking on the relevant tab... but again I'm not quite sure how to implement this.
I've been working on this for a few days so if anyone had any tips or pointers I would really appreciate it!
Thanks for reading! :)
You are not using YouTube's API properly. See https://developers.google.com/youtube/iframe_api_reference
In your fiddle, programmatic play is not possible, because you can't know when the YouTube player is ready, as you are not the one initialising it. Your attempts to play the video might take place too early.
Programmatic pause (you managed to pause the first video) is possible thanks to enablejsapi=1 in the iframe src and the fact that the player is ready at that point.
Here's a fork of your fiddle - https://jsfiddle.net/raven0us/ancr2fgz
I added a couple of comments. Check those out.
// load YouTube iframe API as soon as possible, taken from their docs
var tag = document.createElement('script');
tag.id = 'iframe-demo';
tag.src = 'https://www.youtube.com/iframe_api';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// initialised players are kept here so we don't have to destroy and reinit
var ytPlayers = {};
function mountChannel(channel) {
var player;
var iframeContainer = document.querySelectorAll('#' + channel + ' iframe');
// if the channel & iframe we want to "mount" exist, check for playing iframes before doing anything else
if (iframeContainer.length > 0) {
// Object.keys() is ECMA 5+, sorry about this, but no easy to check if an object is empty
// alternatively, you could have an array, but in that case, you won't be able to fetch a specific player as fast
// if you don't need that functionality, array is as good cause you will just loop through active players and destroy them
var activePlayersKeys = Object.keys(ytPlayers);
if (activePlayersKeys.length > 0) { // if players exist in the pool, destroy them
for (var i = 0; i < activePlayersKeys.length; i++) {
var activeChannel = activePlayersKeys[i];
var activePlayer = ytPlayers[activeChannel];
activePlayer.getIframe().classList.remove('playing'); // mark pause accordingly, by removing class, not necessary
activePlayer.pauseVideo();
}
}
// check if player already initialised and if player exists, check if it has resumeVideo as a function
if (ytPlayers.hasOwnProperty(channel)) {
ytPlayers[channel].playVideo();
} else {
var iframe = iframeContainer[0];
player = new YT.Player(iframe, {
events: {
'onReady': function (event) {
// event.target is the YT player
// get the actual DOM node iframe nad mark it as playing via a class, styling purposes, not necessary
event.target.getIframe().classList.add('playing');
// play the video
event.target.playVideo();
// video may not autoplay all the time in Chrome, despite its state being cued and this event getting triggered, this happens due to a lot of factors
},
// you should also implement `onStateChange` in order to track video state (as a result of user actions directly via YouTube controls) - https://developers.google.com/youtube/iframe_api_reference#Events
}
});
// append to the list
ytPlayers[channel] = player;
}
}
}
// Get the element with id="defaultOpen" and click on it
function onYouTubeIframeAPIReady() {
// YouTube API will call this when it's ready, only then attempt to "mount" the initial channel
document.getElementById("defaultOpen").click();
}
This is the first time I worked with YouTube's iframe API, but it seems reasonable.
I have a problem with embedded video. I use "Embed.ly API" and "player.js":
https://docs.embed.ly/docs/oembed
https://docs.embed.ly/docs/playerjs
I'm trying to embed a video from "youku.com" on my site. The "youku.com" is a content provider, as stated here: https://embed.ly/providers
Video is added but player's methods (play, pause and others) does not work for video. But if I changing video service from "youku.com" to "youtube.com" and use video from "youtube.com", then methods works well.
I also tried to connect the "platform.js" instead of the player. The result is the same.
Maybe the "player.js" worked before, I guess that the "youku.com" has changed its API, but the "player.js" did not track changes.
Does the player.js really work with "youku.com" now? Perhaps something should be added to my code for solve the problem? I am not in China, can it have any effect due to the fact that "youku.com" is a Chinese service?
Thanks.
My code js: (And another question: are there any errors in the code?)
var obj_json = $.getJSON('https://api.embedly.com/1/oembed?' + $.param({
url: :url, // for example: https://v.youku.com/v_show/id_XMzg2MjgwNzA0OA==.html
key: :key // my_API_key
})).done(function () {
$('.embeded').html(obj_json.responseJSON.html).find('iframe.embedly-embed').each(function () {
// initialize the player.
var player = new playerjs.Player(this);
player.on('ready', function () {
player.setLoop(true); // it is not work
player.play(); // it is not work
//..................................
// There are also special buttons on the page, clicking on which the video should start or stop:
$('.button_play').click(function () {
player.play(); // pressing occurs, but player it is not work
});
$('.button_pause').click(function () {
player.pause(); // pressing occurs, but player it is not work
});
//.........................................
});
});
});
Here I found an example how I can listen Play\Pause button of youtube iframe.
player.addEventListener('onStateChange', function(e) {
console.log('State is:', e.data);
});
Now I need to listen the volume changes.
In the youtube documentation and here I found a method player.getVolume(), but I have no idea how this method can be implemented if I want to be informed about volume changes from iframe side, instead of ask iframe from my side.
On YouTube Player Demo page such functionality exists (when I change the volume of a player, I see appropriate changes in the row Volume, (0-100) [current level: **]), but neither in the doc nor in internet I can not find how to implement it.
I also tried to use the above mentioned code with onApiChange event (it is not clear for me what this event actually does), like:
player.addEventListener('onApiChange', function(e) {
console.log('onApiChange is:', e.data);
});
but console shows nothing new.
player.getOptions(); shows Promise {<resolved>: Array(0)}.
Could anyone show an example?
See this question.
You can listen to postMessage events emitted by the IFrame and react only to the volume change ones:
// Instantiate the Player.
function onYouTubeIframeAPIReady() {
var player = new YT.Player("player", {
height: "390",
width: "640",
videoId: "dQw4w9WgXcQ"
});
// This is the source "window" that will emit the events.
var iframeWindow = player.getIframe().contentWindow;
// Listen to events triggered by postMessage.
window.addEventListener("message", function(event) {
// Check that the event was sent from the YouTube IFrame.
if (event.source === iframeWindow) {
var data = JSON.parse(event.data);
// The "infoDelivery" event is used by YT to transmit any
// kind of information change in the player,
// such as the current time or a volume change.
if (
data.event === "infoDelivery" &&
data.info &&
data.info.volume
) {
console.log(data.info.volume); // there's also data.info.muted (a boolean)
}
}
});
}
See it live.
Note that this relies on a private API that may change at anytime without previous notice.
I inspected the code of YouTube Player Demo page and found that the html line which shows the current YouTube volume (<span id="volume">**</span>) constantly blinking (~ 2 times per 1 sec), so I can assume this demo page uses something like this:
// YouTube returns Promise, but we need actual data
self = this
setInterval(function () { self.player.getVolume().then(data => { self.volumeLv = data }) }, 250)
Possibly not the best method, but it seems there is no other option (I also tried to listen changes in the appropriate style of the volume bar, but no luck due to the cross-origin problem).
So, this let us 'listen' volume changes of youtube.
Just in case, if someone wants to set youtube volume, you need to use [this.]player.setVolume(volume_from_0_to_100)
How Can using the youtube api and javascript detect the time it takes for the first image of the video to play after clicking the play. Therefore I can measure the time it takes and the amount it prebuffers before it can play
There are event handlers in the API itself as documented here :
https://developers.google.com/youtube/js_api_reference?hl=en#Events
Notably : onYouTubePlayerReady(playerid)
Therefore you could try something like :
function onYouTubePlayerReady(playerId) {
player = document.getElementById("playerID");
player.addEventListener("onStateChange", "callMeWhenStateChange");
}
The callMeWhenStateChange is then fired on the state change, so you want to listen for state 1 I believe.. Again refer to documentation.
function callMeWhenStateChange(state) {
if(state == 1)
console.log("Current state is playing : " + state);
}
Hope that answers your question.
I have an app that tracks video views and integrates it with other marketing activities. In doing so, I needed to keep track of how long a person watches a html5 video and post it back to my app (via an API). I'm using videojs player, but really this is just a wrapper around the HTML5's api for this attribute. This is in an app with various videos can be loaded based on what page they are watching, so I needed a solution that tracked regardless of video length.
The problem I had, as a video plays the API reports back every ~300MS and I didn't want to hit my API that often. So I needed a solution to keep track of last time I posted. After digging around, I couldn't find an answer, so in case someone else with a similar need, my solution to this problem is below.
We've decided that I wanted to post my video viewing results every 5 seconds, but since we have no guarantee that the currentTime will report back at exactly 5 seconds, so we just need to round to closest whole integer value.
On my video wrapper div, I've added a data attribute called data-last-time-push. I post the rounded time every time I push and check to see if we have exceed the interval before we post again.
HTML
<div id="video-wrapper" data-time-last-push="0">
Javascript
Bind the videojs container to the timeupdate property.
var vid = videojs("video-container", {}, function() {
this.on('timeupdate', videoTracker);
});
function for posting ajax...
var videoTracker = function() {
var player = this;
var last_push, wrapper, current;
wrapper = $('#video-wrapper');
last_push = wrapper.attr("data-time-last-push");
current = Math.round(player.currentTime());
//you could make the 5 here to be a variable or your own interval...
if (current%5 === 0) {
if (current > last_push) {
//do your AJAX post here...
wrapper.attr("data-time-last-push", current);
console.log('currentTime = ' + player.currentTime());
console.log(' duration: ' + player.duration());
}
}
};
Note, I tried to do a jsfiddle to show it working, but ended up running into HTTPS videos because the sample videos don't work through secure connections.