video.js - find the start time of a seek during playback - javascript

I am using video.js (http://www.videojs.com/) to build a video approval system and need to log user actions in the player. I can do this easily enough with play, pause, end etc. but have hit a problem when trying to log seeks.
I want to be able to log the start and end times of any seeks within the plaback, so we know if the user has not actually watched a section of the video. The player seems to offer events to support this, but I am struggling to get correct timings from it.
When the user skips through a video the player emits the following events in order: pause, seeking, seeked, play.
If I query the player object at any of these events using currentTime() the result is always the end time for the seek, even on the initial pause event. This means I can log where the seek ended but not where it started.
Can anyone help me to find the position in the video where the seek begins?
If this is not possible, I'd settle for a way to disable seeking during playback.
EDIT: adding code as requested. It's pretty simple:
var trackedPlayer = videojs('pvp-player');
trackedPlayer.on("play", function (e) {
console.log("Video playback started: " + trackedPlayer.currentTime());
});
trackedPlayer.on("pause", function (e) {
console.log("Video playback paused: " + trackedPlayer.currentTime());
});
trackedPlayer.on("seeking", function (e) {
console.log("Video seeking: " + trackedPlayer.currentTime());
});
trackedPlayer.on("seeked", function (e) {
console.log("Video seek ended: " + trackedPlayer.currentTime());
});
trackedPlayer.on("ended", function (e) {
console.log("Video playback ended.");
});
If I can get all the tracking I want I will replace console.log with ajax calls to store the data.

You can listen to timeupdate und take the next to last value you got there before seeking is called as your source:
var previousTime = 0;
var currentTime = 0;
trackedPlayer.on('timeupdate', function() {
previousTime = currentTime;
currentTime = trackedPlayer.currentTime();
});
trackedPlayer.on('seeking', function() {
console.log('seeking from', previousTime, 'to', currentTime, '; delta:', currentTime - previousTime);
});
This seems to work with the HTML5 tech. I have not tested with other techs.
There is, however, one glitch: the first time seeking a paused player yields only a small delta (and the almost-same previous value for both variables). But this shouldn’t matter much since the delta is only a few hundred milliseconds (and I gather you’re only interested in the “from” value).
Update
seeked is triggered far more infrequently than seeking. Try the following.
var previousTime = 0;
var currentTime = 0;
var seekStart = null;
trackedPlayer.on('timeupdate', function() {
previousTime = currentTime;
currentTime = trackedPlayer.currentTime();
});
trackedPlayer.on('seeking', function() {
if(seekStart === null) {
seekStart = previousTime;
}
});
trackedPlayer.on('seeked', function() {
console.log('seeked from', seekStart, 'to', currentTime, '; delta:', currentTime - previousTime);
seekStart = null;
});
There are also many libraries for debouncing function calls (in this case the call to your backend).

I needed to find the same value for a project I was working on so I could determine whether or not a user was skipping forward or backward in a videojs player.
Initially, I thought to save the currentTime() a user was seeking from on timeupdate then immediately removing my timeupdate listener once seeking was dispatched. While this worked in some browsers like Chrome, unfortunately, I found that other browsers continued to fire timeupdate more frequently and would continue to update the currentTime() I was saving after the player actually seeked.
Here was the solution that ultimately worked across Safari/Chrome/Firefox. I have yet to test in IE.
var previousTime = 0,
currentTime = 0,
completeTime = 0,
position = 0;
trackedPlayer.on('timeupdate', function() {
previousTime = currentTime;
currentTime = Math.floor(player.currentTime());
// save 'position' so long as time is moving forward with each update
if (previousTime < currentTime) {
position = previousTime;
previousTime = currentTime;
}
});
// when seeking starts
trackedPlayer.on('seeking', function() {
player.off('timeupdate', onTimeUpdate);
player.one('seeked', onSeekComplete);
});
// when seeking completes
trackedPlayer.on('seeked', function() {
completeTime = Math.floor(player.currentTime());
console.log("User came from: " + position);
console.log("User ended at: " + completeTime);
});

I know this is an old post but this is the only solution that worked for me.
var counter = 0;
var beforeTimeChange = 0;
function handleSeeking() {
var timeoutTime = 300;
var beforeCounter = counter + 1;
if (trackedPlayer.cache_.currentTime === trackedPlayer.duration()) {
return;
// when video starts over, calls seek
}
beforeTimeChange = beforeTimeChange || trackedPlayer.cache_.currentTime;
setTimeout(function() {
if (beforeCounter === counter) {
console.log('before seek', beforeTimeChange, '\nafter seek', trackedPlayer.currentTime() - (timeoutTime / 1000));
counter = 0;
beforeTimeChange = 0;
}
}, timeoutTime);
counter++;
}
trackedPlayer.on('seeking', handleSeeking);

For a more accurate solution, you can listen to the events that trigger the seek such as mousedown on progress bar, left key, right key etc., and get the current time from these events.
For example in version 7.10.2 you can do the following,
let seekStartTime;
player.controlBar.progressControl.on('mousedown', () => seekStartTime = player.currentTime());
player.controlBar.progressControl.seekBar.on('mousedown', () => seekStartTime = player.currentTime());
player.on('keydown', (e) => {
if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
seekStartTime = player.currentTime();
}
});
console.log(seekStartTime);
Note 1: There are two seperate mousedown event listeners for progress control and seek bar. This is because the video can be seeked by clicking outside the seek bar on the progress control as well.
Note 2: Seeking using hotkey numbers does not pause the video. However, if necessary you can add those keydown listeners too.

I needed to find the start and end of a seeking action in my project and I used #Motorcykey answer and it worked, but there was a small bug. when I tried to seek to a time before the current time while the player was paused, the position didn't get updated. so I added just one line and it fixed it. I've tried other solutions too but so far this was the best solution that I've found. Here's the code snippet on player 'seeked'
player.on('seeked', function () {
completeTime = Math.floor(player.currentTime());
console.log("User came from: " + position);
console.log("User ended at: " + completeTime);
position= Math.floor(player.currentTime())
});

Try with this code to know the length of video.
var duration = document.getElementById("duration");
var vid_duration = Math.round(document.getElementById("video").duration);
//alert(vid_duration);
duration.innerHTML = vid_duration;
//duration.firstChild.nodeValue = vid_duration;

Related

Sound not always playing with javascript

I am using the following code to play sound on the browser:
HTML
<audio id="' + newMessageSoundObjectId + '"><source src="sound.mp3" type="audio/mpeg"></audio>
Javascript
this.newMessageSoundObject = document.getElementById(newMessageSoundObjectId);
// ...
var playPromise = this.newMessageSoundObject.play();
if (playPromise !== undefined) {
playPromise.then(function() {
// Automatic playback started!
}).catch(function(error) {
console.error("Sound was not played: " + error);
});
}
It works well most of the time, but sometimes it randomly stops working for a few minutes and starts again, without reloading the page or navigating. Moreover, console doesn't error any error.
For more context, this is used to play a notification when a user receives a new message.
Is there a better way to ensure sound is played?
You may be running into the situation where it waits to finish the clip before responding to any new clicks.
If so, insert this code at the beginning of your function to reset the clip each time it is clicked:
audio.currentTime = 0; // On each click, rewind clip to start
It would look something like this:
if (playPromise !== undefined) {
playPromise.then(function() {
audio.currentTime = 0; // On each click, rewind clip to start
// Automatic playback started!
}).catch(function(error) {
console.error("Sound was not played: " + error);
});
You may want to consider adjusting your code so it's a bit cleaner. I haven't tested this, but it should work just fine:
const soundObject = document.querySelector("#newMessageSoundObjectId");
soundObject.addEventListener("mousedown", playClip);
function playClip() {
audio.currentTime = 0; // On each click, rewind clip to start
audio.play();
} else {
console.error("Something went wrong");

Javascript - how to tell the video element to stop playing the video at xyz duration?

I have content in French, Nederland, English (on same topic example: "global warming" )
But its in one large file, how can i tell the video element to please play from duration 00:00:00 till 00:05:00 only do not play the whole 02:00:00 ?
function video_play_onetime_withcut(input) {
$('#mediaplayer').prop('loop', false);
$('#mediaplayer').attr('src', filename).show();
mediaplay_video= document.getElementById('mediaplayer');
mediaplay_video.play();
mediaplay_video.onended = function(e) {
console.log('>>> Playing finished: ', e);
};
}
Use timeupdate event.
mediaplay_video.ontimeupdate = function(event) {
if( mediaplay_video.currentTime === 5 * 60 ) {
mediaplay_video.pause();
// Just so we don't stop the player multiple times if the user
// wants to see the whole thing...
mediaplay_video.ontimeupdate = false;
// Do whatever you want.
}
};
If you plan to hide the player afterwards, you can use this:
mediaplay_video.ontimeupdate = function(event) {
if( mediaplay_video.currentTime >= 5 * 60 ) {
mediaplay_video.pause();
// #TODO: Remove player element...
}
};
More information can be found here.
You should be able to use a timeout function.
var clipLength = 10000; //milliseconds
window.setTimeout(function(){
mediaplay_video.stop();
}, clipLength);

How to pause a video after a certain time? video.js

I've been messing with video.js whilst learning javascript but can't seem to figure out how to make the video pause after a certain time has passed.
myPlayer.play(function(){
whereYouAt = myPlayer.currentTime();
if (whereYouAt == 10) {
myPlayer.pause();
}
})
That is my pause code.
Check the currentTime in the timeupdate event callback:
var pausetime = 2; // stop at 2 seconds
var myPlayer = videojs('example_video_1');
myPlayer.on('timeupdate', function(e) {
if (myPlayer.currentTime() >= pausetime) {
myPlayer.pause();
}
});
myPlayer.play();
JSFiddle demo: http://jsfiddle.net/EdjxN/17/

Setting the currentTime of an <audio> tag not working?

I have this audio tag playing in the background, and I'm able to store the progress in seconds to a cookie.
But in no way I'm able to start the audio from that cookie. (for continuing on other pages)
$("p#sound audio").currentTime = $.cookie("audioTime");
<audio autoplay="autoplay" loop="loop" ontimeupdate="document.getElementById('tracktime').innerHTML = Math.floor(this.currentTime); $.cookie('audioTime', Math.floor(this.currentTime));">
<source src="audio/song.ogg" type="audio/ogg" />
<source src="audio/song.mp3" type="audio/mp3" />
Your browser does not support the audio tag.
</audio>
<span id="tracktime">0</span>
Does this have to do with the song being loaded again from start?
Thanks!
EDIT:
$("p#sound audio").get[0].currentTime
With .get[0], it doesn't work either.
Can someone please clear things up for me? Greatly appreciated!
You need to wait until audio source loads before you set the current time.
$(function(){
$('audio').bind('canplay', function(){
$(this)[0].currentTime = $.cookie('audioTime');
});
});
You can set the start time by adding t=<time> to the URL, as documented here: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_HTML5_audio_and_video#Specifying_playback_range
E.g. <audio src="http://example.com/audio.mp3#t=50></audio>
At first there is an error in your code because currentTime is not a part of jQuery (but you already know this)
$("p#sound audio").currentTime // is incorrect (refers to a property of jQuery)
$("p#sound audio")[0].currentTime // is correct (refers to a property of DOM element)
I discover that the audio tag has some strange things and can be operate differently from browser to browser, for example in Chrome.
At first you have to wait for the 'durationchange' event to be sure the length is known by the object.
After this you have to start the stream with 'play()' (if not already started) and pause it (sometimes after a short delay) with the 'pause()' function. Then you can change the 'currentTime' property with the value. After this you have to start the stream again by using the 'play()' function.
Also it is sometimes needed to load the stream by yourself by using the 'load()' function.
Something like this:
$(document).ready( function()
{
var a = $('audio:first'),
o = a[0];
a.on( 'durationchange', function(e)
{
var o = e.target;
if( o )
{
o.pause();
o.currentTime = parseInt( $.cookie("audioTime"));
o.play();
}
});
if( o )
{
o.load();
o.play();
}
});
You have to play with it to be sure what is the best in your situation, for example the resume (play again) method to delay it for a second or so.
When using this method you don't have to use the autoplay feature because most of the time it doesn't work.
Hope it helps, greetz,
Erwinus
what I found in my case is that there is an issue with context somewhere. I initialize audio under the window context but when I try to change currentTime from XMLHttpRequest response it does NOT work. I don't know the answer yet but I'm providing a clue maybe an expert in Javascript will know how to make it work.
/* initialize this.audio player */
Audio = function() {
var that = this;
// keep track of playback status
var AudioStatus = {
isPlaying : false
};
// define references to this.audio, pulldown menu, play-button, slider and time display
that.audio = document.querySelector("AUDIO");
/* load track by menu-index */
var loadTrack = function() {
if(that.audio == null){
log("audio null"); return;
}
that.audio.src = '../sounds/400.mp3';
that.audio.load();
};
/* callback to play or pause */
that._play = function() {
if(that.audio == null){
log("audio null"); return;
}
that.audio.play();
AudioStatus.isPlaying = true;
};
that._pause = function() {
if(that.audio == null){
log("audio null"); return;
}
that.audio.pause();
AudioStatus.isPlaying = false;
};
that.playPause = function() {
if (that.audio.paused) {
self._play();
}
else {
self._pause();
}
};
/* callback to set or update playback position */
that.updateProgress = function(value) {
if(that.audio == null){
log("audio null"); return;
}
that.audio.currentTime = value; // <<<--- it does NOT work if I call from XMLHttpRequest response but it DOES if it is called from a timer expired call back
};
that.isAudioPlaying = function(){
return AudioStatus.isPlaying;
};
};
This works for me.
if (!isNaN(audio.duration)) {
audio.currentTime = 0;
}
Hope it helps!
This solved it for me:
$("p#sound audio").on('loadedmetadata', () => {
$("p#sound audio").get(0).load();
});
So I could later set the currentTime without worrying about whether the audio was loaded or not.
I had a similar problem when trying to play a HLS stream through an HTML audio element.
No matter where and how I tried to set the currentTime property on the audio element, it wouldn't work until about 2.5s after calling audioElement.play().
//Calling this
htmlElement.currentTime = 5.5;
console.log('currentTime: '+htmlElement.currentTime);
//Would return 'currentTime: 0';
What did work tho, was if I called that in a timeout with 2500-3000ms delay.
After a day of debugging to pinpoint the element state in which it would allow me to set the currentTime property, so I wouldn't have to rely on a fixed timeout.
My solution was to listen to 3 HTMLMediaElement events (https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#events): loadedmetadata, loadeddata and canplaythrough and setting custom flags when they were first reported.
When all 3 were reported and the HTMLMediaElement.duration property was bigger than 0, I set the HTMLMediaElement.currentTime to my desired value inside the first HTMLMediaElement timeupdate event callback.
This can be summed up in the following code snippet:
let audioElementStates = {
'metadataloaded': false,
'dataloaded': false,
'canplaythrough': false
};
let shouldSetOldTime = true;
let audioElement = document.getById('audio-element');
audioElement.src = 'https://somedomain.com/hlsplaylist.m3u8';
audioElement.addEventListener('loadedmetadata',()=>{audioElementStates.metadataloaded = true;});
audioElement.addEventListener('loadeddata',()=>{audioElementStates.dataloaded = true;});
audioElement.addEventListener('canplaythrough',()=>{audioElementStates.canplaythrough = true;});
audioElement.addEventListener('timeupdate',()=>{
if(shouldSetOldTime &&
audioElement.duration>0 &&
audioElementStates.metadataloaded &&
audioElementStates.dataloaded &&
audioElementStates.canplaythrough){
audioElement.currentTime = 5.5;
shouldSetOldTime=false;
}
});
audioElement.play();
For reference I was using this with Vue.js in a Quasar framework mobile app packaged with Apache Cordova.
NOTE: The loadedmetadata check can probably be skipped, since loadeddata would presumably never fire before loadedmetadata.

How can I execute a setTimeout() only when the user is present (with the mouse movement) with jQuery

I have a function that updates a <div /> via AJAX:
function update() {
<!-- .ajax() -->
setTimeout(update(), 3000);}
}
What I need is that this is not executed when the user is not present on the website, so if there is no movement of the mouse (we will suppose that if move it is in the website) it will not update .mousemove(). By the way, there is any other thing that we can do to know is someone is active on the website?
How can this be done? Thank you in advance!
Edit: probably I explained bad. I need to know the way to only update when there is activity. Like Facebook does with his news feed, the front page. Thanks!
You could use a mousemove handler to track when the user last moved, and then have the process only happen if they last moved the mouse within X seconds. But of course, if the user is sitting there reading something, or if they're a keyboard-oriented kind of person, that will tend to miss that they are there... So you'd probably want to look at keydown as well.
Here's a mousemove example:
jQuery(function($) {
var count = 0, lastmove = new Date();
$(document).mousemove(function() {
++count;
lastmove = new Date();
$('#display').html("Moved " + count + " times");
});
});​
Then your update code could do this:
function update() {
if (new Date() - lastmove < 60000) { // 60 seconds
// Really do the update
}
else {
// Check back in a few seconds
setTimeout(update, 3000);
}
}
Off-topic, but you have an error in your update code. You have:
setTimeout(update(), 3000);
...which will call update immediately and then try to use its return value to schedule something to happen in three seconds. If you want the call to update to be scheduled to happen in three seconds, leave off the () after it:
setTimeout(update, 3000);
I think I might have ended up with something such as this. Avoids date arithmetic. Only cares whether there's been some activity since the last update().
window.activeFlag = false;
window.updateDelay = 3000;
$(document).bind('mousemove scroll keydown', function(){ activeFlag = true; });
function update() {
if(activeFlag) {
doWork();
activeFlag = false;
}
}
window.setTimeout(update, updateDelay);
edit: I've discovered a flaw in the code. The following is more appropriate:
window.activeFlag = false;
window.updateDelay = 3000;
$(document).bind('mousemove scroll keydown', function(){ activeFlag = true; });
function update() {
if(activeFlag) {
doWork();
activeFlag = false;
}
window.setTimeout(update, updateDelay);
}
update();
I think there is no easy way to determine if the user is present
I would use a combination of mousemove, scroll, keypress.
var bUpdate = false;
function update() {
if(bUpdate){
///perform your ajax request
}
}
$(document).mousemove(function(){
bUpdate = true;
setTimeout(function(){bUpdate=false;}, 3000);}
});

Categories

Resources