I am looking for a way to have player.getDuration() and player.getCurrentTime() from youtube API, written in normal time (Minute:Seconds) Like this bellow:
var mind = player.getCurrentTime() /*or player.getDuration()*/ % (60 * 60);
var m = Math.floor(mind / 60);
var secd = mind % 60;
var s = Math.ceil(secd)
document.write(m,":",s); seconds;
But it keeps sending an error NaN:NaN
Also I want to change an image when the video has finished using the function YT.PlayerState.ENDED like this:
if(YT.PlayerState.ENDED){
$("#change").attr('src', "../play button.png");
}
But it is not working?
Make sure you call these function when the video has been loaded or else it will be NaN. Just alert(player.getCurrentTime()); and see what the output is.
-
Your if statment if wrong. You are comparing ENDED with nothing. See this for an example
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
}
}
Most likely player is not properly set.
Also, the value of getCurrentTime() changes during playing; put the code in a function which you call every second.
Maybe use jquery.min.js to display the value.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type='text/javascript'>
var player; // Reference to global player object.
// call the function every 1 second.
setInterval(displTime,1000); // PUT THIS STATEMENT JUST AFTER THE PLAYER HAS BEEN CREATED.
function displTime() {
if (! player)
alert("NO player object !"); // Check if player has been set.
// player.getCurrentTime() or player.getDuration()
var mind = player.getCurrentTime(); // returns elapsed time in seconds
var m = Math.floor(mind / 60);
var secd = mind % 60;
var s = Math.ceil(secd)
$("#pt").html(m + ":" + s); // Using the JQUERY library to write to body
}
</script>
<div id="pt"></div> <!-- Put this in the html BODY to display m:s -->
Related
I'm busy with a webdoc that I'm partially creating on hype, the video are hosted on vimeo (so I need to use the vimeo api for some tasks like seekto) but my difficulties should be limited to js.
the objective is to display a given image at a given time interval of the video.
With my code below, I do get the string "test", "success" and "confirmed success" at the right time in my div id=popimgbox, and I can seek back and forth in the video and still get the right "answear", if I may say so.
Now, I have images that are all stored in the same folder, and all named popimgX.jpg, with X being a number.
I want
to store the URLs of my images in a variable let's say "popimgurl"
that my variable is updated (by a function???) in order to contain the URL of a given immage for a given interval of time of the video
to still be able seekto back and forth in the video and get the right URL at the right time
To do so I created a function increment, and a pair of variable. With the code below, my popimgurl variable is indeed updated once the video reach 3 seconds, but it do not increment only once... untill the video reach 6 seconds, when I want to update my popimgurl variable once again.
I tried to use for with js break and js closure but did not manage for some understandable reasons after thought;
I did quite some try with switch, but I'm stuck with the fact that the case must be string or single numerical value, not numerical interval or comparaison.
thank's in advance for your help :-)
var iframe = $('#vplayer_1')[0];
var player = $f(iframe);
var status = $('.status');
fired = 0;
//my try to sync increment
var dia = (function () {
var n = 0;
return function increment() {return n += 1;}
})();
function dian(){
popimgurl = '${resourcesFolderName}/popimg'+ dia() +'.jpg';
popimgloader = '<img src ="' + popimgurl + '">';
}
// When the player is ready, add listeners for pause, finish, and playProgress
player.addEvent('ready', function() {
status.text('ready');
player.addEvent('pause', onPause);
player.addEvent('finish', onFinish);
player.addEvent('playProgress', onPlayProgress);
});
// Call the API when a button is pressed
$('button').bind('click', function() {
player.api($(this).text().toLowerCase());
});
function onPause(id) {
status.text('paused');
}
function onFinish(id) {
status.text('finished');
}
function onPlayProgress(data, id) {
status.text(data.seconds + 's played');
//my chapters, when I want the img to change within popimgbox
if (data.seconds >= 1) {
popimgbox.innerHTML = "test";
}
if (data.seconds >= 3) {
// popimgbox.style.display = "success"
dian();
popimgbox.innerHTML = popimgurl;
}
if (data.seconds >= 6) {
// popimgbox.style.display = "confirmed success"
dian();
popimgbox.innerHTML = popimgurl;
}
}
PS1: disclamer, I'm a beginer coder, i do my best so excuse my french if my question isn't well formulated or if the answer is somewhere but I was unable to see/understand it
PS2 : i did quite a try with popcornjs, but not way to make it work with vimeoapi and within hype, quite frustrated ;-)
PS3: as this is my first post I would like to thank's you all for the great support available here; I owe you most ;-)
Finally I'll answer myself.
It's a solution that only stand for vimeo, as this is what I use to host my videos, but very little changes have to be done to work with the html5 <video> tag as well.
First you need to define your variables and your intervals:
var intervals =[11.56, 44.08, 115, 125, 127.92, 177.72];
var index;
Then you need to add an event listener timeupdate that return the elapsed time , filter intrevals according to the elapsed time data.seconds or seconds and define the value of index as the indexOf the last entry of your filtered array intervals
player.on('timeupdate', function(data) {
seconds = data.seconds;
index = intervals.indexOf(intervals.filter(function(nb) {
return seconds < nb;
})[0]);
if (diaIndex == -1) {
// do something if seconds > the higher value of your last interval
}
And that's it !
Now for
seconds = [0, 11.56[ --> index = 0
seconds = [11.56, 44.08[ --> index = 1
seconds = [44.08, 115[ --> index = 2
and so on
Now we can use index as a variable for instance to display a given image :
var imgNum = 0;
function diaplayImg(index) {
if(index === imgNum) {
return;
// this will avoid that the same image is releaded on every timeupdate events
}
else {
imgNum =+ index
document.getElementById('myImageWraper').innerHTML = "<img src='img" + imgNum+ ".png'>"
};
}
Don't forget, you need to call the function displayImage() in your timeupdate event listener, it will then be fired every ±250ms but the image won't be reloaded each time
PS : vimeo has realeased its new api between my question and my answer so the question use the old api while the answer use the new one
I'm using katspaugh's waveSurfer library for playing sound files.
I wrote code to show 'elapsed time / total time' in this way.
waveSurfer.on('play', function() {
$scope.getCurrentDuration();
});
$scope.getCurrentDuration() is a function to transform floating-type variable to string-type variable, like below:
$scope.getDuration = function() {
if ($scope.waveSurferReady) {
var time = waveSurfer.getDuration(); // get duration(float) in second
var minute = Math.floor(time / 60); // get minute(integer) from time
var tmp = Math.round(time - (minute * 60)); // get second(integer) from time
var second = (tmp < 10 ? '0' : '') + tmp; // make two-figured integer if less than 10
return String(minute + ':' + second); // combine minute and second in string
}
else {
return 'not ready yet'; // waveSurfer is not ready yet
}
};
But the problem is,
in this part:
waveSurfer.on('play', function() ...)
the callback function execute only once.
I expect the callback function called periodically, but it executes only once, so as the result, elapsed time is shown only at the start time.
How can I solve this?
Looking into the source, I've found the audioprocess event paired with html5 timeupdate event.
Try it out.
waveSurfer.on('audioprocess', function() {
// do stuff here
});
I am trying to get an accurate calculation of how much time a user has spent watching a video. The time they spent watching it should not include skipping forward or when the video is paused. The code I have now is giving me a different percentage each time (I'm using an interval). I don't know how else to go about this?
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING && !done) {
clicked_video = true;
arr.length = (player.getDuration()-1).toFixed(0);
done = true;
}if(event.data === YT.PlayerState.PLAYING){
id = setInterval(check, 1000);
}if(event.data == YT.PlayerState.PAUSED || event.data == YT.PlayerState.BUFFERING){
clearInterval(id);
var percent = 0;
for(var i=0;i<arr.length;i++){
if(arr[i] == "x"){
percent++;
console.log(percent);
}
}
percent = (percent / arr.length) * 100;
alert(percent + "%");
}
}
function check(){
arr[Math.floor(player.getCurrentTime())] = "x";
}
Here is a demo, I think the variables and functions have names explicit enough to understand everything, but if you have any question or problem, go ahead and ask.
One thing I have to mention is that it won't be exact. If your interval gets executed once per second and the user skips a part in the middle of a second, that one might not be counted. To have a more accurate percentage, you can execute your interval more often (100ms here, I got 97% when watching the video back and forth), but be careful not to use too much resources.
var player, timer, timeSpent = [], display = document.getElementById('display');
function onYouTubeIframeAPIReady() {
player = new YT.Player( 'player', {
events: { 'onStateChange': onPlayerStateChange }
});
}
function onPlayerStateChange(event) {
if(event.data === 1) { // Started playing
if(!timeSpent.length){
timeSpent = new Array( parseInt(player.getDuration()) );
}
timer = setInterval(record,100);
} else {
clearInterval(timer);
}
}
function record(){
timeSpent[ parseInt(player.getCurrentTime()) ] = true;
showPercentage();
}
function showPercentage(){
var percent = 0;
for(var i=0, l=timeSpent.length; i<l; i++){
if(timeSpent[i]) percent++;
}
percent = Math.round(percent / timeSpent.length * 100);
display.innerHTML = percent + "%";
}
JS Fiddle Demo
Here's a way to get a frame-perfect precision of how much of the video the user has watched:
The recipe
Get a reference to the HTML5 video player
Grab the played property off the video object, which returns a TimeRanges object (see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges)
Iterate over the played object, and add up all the ranges
Example
/**
*
* #param {HTMLVideoElement} video
* #returns {{total: number, percent: number}}
*/
function getPlayedTime(video) {
var totalPlayed = 0;
var played = video.played;
for (var i = 0; i < played.length; i++) {
totalPlayed += played.end(i) - played.start(i);
}
return {
total: totalPlayed,
percent: totalPlayed / video.duration * 100
};
}
By calling the above helper function and passing it your video object, you'll get back an object with two properties: the total length (in seconds) that the user actually watched, and the percentage of the video's duration that this represents.
The cool thing about this approach is that it is 100% accurate. If the user watches the first 5 seconds of the video over and over, it'll still register that only 5 seconds were watched. The browser itself keeps track of this, so you don't need to set intervals, listen for events, etc.
Youtube iframe player api provide a function to turn the current played time of the video player.getCurrentTime():Number. That can be used to track how much the user watched the video.
You can also make use of player.getDuration() api to get the total video time and calculate the percentage of the progress
here is the alternation of the exampled shared by first answer http://jsfiddle.net/ndbseftp/
Im trying to show on my site changeable clock synchronized with facebook server.
The fb server time is available at:
https://api.facebook.com/method/fql.query?query=SELECT+now%28%29+FROM+link_stat+WHERE+url+%3D+%271.2%27&format=json
How to make it changeable every second without refreshing the page?
Assuming some non-written functions, it should look like that:
var requestBegin = Date.now();
getServertimeFromFacebook(function callback(fbTime) {
var requestEnd = Date.now();
var latency = (requestEnd - requestBegin) / 2;
var curDevicetime = Date.now(); // = requestEnd, of course
var difference = fbTime - latency - curDeviceTime;
function clock() {
var cur = Date.now();
var curFbTime = cur + difference;
show(curFbTime); // print, log, whatever
};
setInterval(clock, …); // you could use a self-adjusting clock
// by using a setTimeout for each tick
});
You could do
show = function(t) { console.log(new Date(t).toString()); };
getServertimeFromFacebook = function(cb) {
ajax("https://api.facebook.com/method/fql.query?query=SELECT+now%28%29+FROM+link_stat+WHERE+url+%3D+%271.2%27&format=json", function(responsetext) {
var obj = JSON.parse(responsetext);
var ts = obj[0].anon,
tms = ts * 1000;
cb(tms);
});
};
I wouldn't call the API every second.
Instead, I would get the Facebook server time only one time at the beginning. And then, I would increment my time value every second by looping using javascript :
setTimeout(function() { /* increment time */ }, 1000);
Bergi: [{"anon":1354654854}] is a unix time. Indeed, Facebook often (always?) deals with time using this representation.
So I have a wee javascript app that receives a pair of numbers from a server every minute (or whatever) and over the course of the minute it updates a div every second (or whatever) so that the value shown in the div gradually increments from the first value to the second. It's only short, I'll post the code at the bottom of the question.
The function contains two sub functions - one of them does the ajax call to update the two values, one of them updates the div contents with a value somewhere between the two values. The ajax function uses setInterval to schedule the div updating function when it has the response from the server, and when the div updating function detects that it's time to update the two values it clears the set interval and calls the ajax function. These two functions thus carry on calling each other for ever.
I declared every variable used by the two sub functions in the outer function, so neither sub function creates any new variables, and both sub-functions are allowed to finish completely each time anyway (thanks to the setInterval in the ajax function).
The memory usage is going up almost every second, which must be every time doDivRefresh is called, but I don't understand what it's doing with new memory each time it's called - it doesn't create any variables.
Help!
/**
*
* Periodically gets the latest values for a stat and changes the contents of a
* div so that is ticks up from the previous value to the latest value
*
* #param divID
* The id of the div to update
* #param URL
* The URL that provides the values
* #param updateDivFrequency
* How often the value displayed in the div is updated, in
* miliseconds
* #param updateDataFrequency
* How often the underlying data is updated from the server, in
* seconds
*
*/
function updateStatOverPeriod(divID, URL, updateDivFrequency, updateDataFrequency)
{
var somethingDoer = new function()
{
};
var request = new XMLHttpRequest();
var currentValue = "";
var previousValue = "";
var latestValue = "";
var it = 0;
var currentTime = new Date();
var endTime = new Date().getTime();
function doDivRefresh(endTime)
{
if (currentValue != null)
{
currentValue = currentValue + it;
document.getElementById(divID).innerHTML = addCommas(parseInt(currentValue));
}
else
{
document.getElementById(divID).innerHTML = "<DIV CLASS=\"error_message\">No data</DIV>";
}
// If it's time to refresh the data end this loop and call the starting
// off method again
currentTime = new Date();
if (currentTime.getTime() >= endTime)
{
clearInterval(somethingDoer);
doAJAX();
}
}
function doAJAX()
{
// If browser supports javascript
if (window.XMLHttpRequest)
{
// Connect to the server and get the new pair of values
request = new XMLHttpRequest();
request.open('get', URL);
request.onreadystatechange = function()
{
// Once...
if (request.readyState == 4)
{
// we've got...
if (request.status == 200)
{
// the response
if (request.responseText)
{
// Parse the response and work out our iterator
previousValue = parseFloat(request.responseText.split("&")[0]);
latestValue = parseFloat(request.responseText.split("&")[1]);
if ((previousValue == 0) || (latestValue == 0))
{
it = parseFloat('0.00');
}
else
{
it = parseFloat((latestValue - previousValue) / (updateDataFrequency / updateDivFrequency));
}
// Set up the doRefreshDiv function in a loop that
// will exit and re-call this function
// once updateDataFrequency has elapsed
currentValue = previousValue;
endTime = new Date().getTime() + updateDataFrequency;
doDivRefresh(endTime);
somethingDoer = setInterval(function()
{
doDivRefresh(endTime);
}, updateDivFrequency);
alert("end of ajax response function()");
}
else
{
document.getElementById(divName).innerHTML = "<DIV CLASS=\"error_message\">Error - no data received from server</DIV>";
}
}
else
{
document.getElementById(divName).innerHTML = "<DIV CLASS=\"error_message\">Error - server returned " + request.status + "</DIV>";
}
}
};
request.send(null);
}
else
{
console.error("No window.XMLHttpRequest, does this browser support AJAX?");
}
alert("end of doAJAX()");
}
// Start
updateDataFrequency = updateDataFrequency * 1000;
doAJAX();
}
These are the first four lines corrected according to jslint.com:
function updateStatOverPeriod(divID, URL, updateDivFrequency, updateDataFrequency) {
"use strict";
var somethingDoer = function() {};
var request = new XMLHttpRequest();
Paste your code there and check for common JS errors. It seems this code was either transformed from an other language to JavaScript or somebody who is not familiar with JavaScript...
See my answer javascript memory leak for dealing with memory leaks. I still think Drip is a very good tool for finding and understanding memory leaks.
I agree that new Date() is always going to be costly. Now here as you just want this code to be executed to refresh values at regular interval can you use setTimeout("doAJAX()",2000); and you can specify the interval in milliseconds instead of checking it using a date function. That should help and in another case you can well use jquery.Ajax if you're interested because there you will be able to concentrate on your area of interest instead of dealing with XMLHttpRequest by yourself.
[head slap]
Thanks for the answers guys, but the real problem was a typo in the element calling the script. Due to a / where I meant to put a * the script was being called about once a millisecond instead of once a minute as intended!