save how much of the video has been played - javascript

I currently try to find out how much of a video has been watched and played and at a certain point I would like to trigger a function.
The problem I occur is that I don't know how to keep track of how much has been watched. For example if you watched 30 seconds and rewind for 10, then you essentially watched 30 seconds and not 20, which currentTime would display.
const video = document.getElementById("video");
const set = new Set();
const percent = .8;
let toWatch;
function mediaWatched (curr) {
alert(`${curr}% of media watched`)
}
function handleMetadata(e) {
toWatch = Math.ceil(video.duration * percent);
console.log(toWatch, video.duration);
}
function handleTimeupdate (e) {
set.add(Math.ceil(video.currentTime));
let watched = Array.from(set).pop();
if (set.has(toWatch) && watched === toWatch) {
video.removeEventListener("timeupdate", handleTimeupdate);
console.log(watched);
mediaWatched(
Math.round(watched / Math.ceil(video.duration) * 100)
);
}
}
video.addEventListener("loadedmetadata", handleMetadata);
video.addEventListener("timeupdate", handleTimeupdate);
<video width="400" height="300" controls="true" poster="" id="video">
<source type="video/mp4" src="http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_2mb.mp4" />
</video>
How can I add such behavior to trigger a function that if 80 percent has been watched and has at least been played?
Thanks so much!

So what you're saying is if a user ever hits a marker then you want to do something? So if they ever hit 80% of the way through the video then you want to do something? If that is the case, just check on each timeupdate and once they hit the marker do something. If they ever go past the marker and rewind, it won't matter (just make sure to guard against doing your action again if thats not what you want.)
If what you're saying is you want to check if the user has been watching the video for a certain time, as opposed to how much of the video they have watched. Then just use a setInterval to keep track of how long they have watched for.
const timer = setInterval(() => {
videoWatchedFor += 1;
if (videoWatchedFor === someTime) {
//doSomething;
}
}, 1000);

Here is how I do it
const getPlayedTime = (player) => {
let totalPlayed = 0;
let played = player.played;
for (let i = 0; i < played.length; i++) {
totalPlayed += played.end(i) - played.start(i);
}
return {
total: totalPlayed,
percent: (totalPlayed / player.duration) * 100,
};
};
player.addEventListener("timeupdate", () => {
const timeWatched = getPlayedTime(player);
console.log(timeWatched);
});

Related

How to get duration from audio tag in react? [duplicate]

I have a html5 <audio> tag in page, but how can I know its duration time?
<audio controls="">
<source src="p4.2.mp3">
</audio>
2020 solution:
You will get undefined or NaN (not a number) when the audio metadata isn't loaded yet. Therefore some people suggested to use onloadedmetadata to make sure the metadata of the audio file is fetched first. Also, what most people didn't mention is that you have to target the first index of the audio DOM element with [0] like this:
-- Vanilla Javascript:
var audio = document.getElementById('audio-1');
audio.onloadedmetadata = function() {
alert(audio.duration);
};
If this won't work try this, however not so reliable and dependent on users connection:
setTimeout(function () {
var audio = document.getElementById('audio-1');
console.log("audio", audio.duration);
}, 100);
-- JQuery:
$(document).ready(function() {
var audio = $("#audio-1")[0];
$("#audio-1").on("loadedmetadata", function() {
alert(audio.duration);
});
});
var au = document.createElement('audio');
au.addEventListener('loadedmetadata',function(){
au.setAttribute('data-time',au.duration);
},false);
In a comment above, it was mentioned that the solution is to bind an event handle to the event loadedmetadata. This is how I did that -
audio.onloadedmetadata = function() {
alert(audio.duration);
};
I was struggling with loading the duration in a React component so following #AlexioVay's solution, here is an answer if you're using React:
This assumes you are using a ref for your audio component class which you will need to target the audio elements for your play/pause handler(s).
<audio /> element:
<audio ref={audio => { this.audio = audio }} src={this.props.src} preload="auto" />
Then in your componentDidMount():
componentDidMount() {
const audio = this.audio
audio.onloadedmetadata = () => {
console.log(audio.duration)
this.setState({
duration: this.formatTime(audio.duration.toFixed(0))
})
}
}
And finally the formatTime() function:
formatTime(seconds) {
const h = Math.floor(seconds / 3600)
const m = Math.floor((seconds % 3600) / 60)
const s = seconds % 60
return [h, m > 9 ? m : h ? '0' + m : m || '0', s > 9 ? s : '0' + s]
.filter(a => a)
.join(':')
}
With this, the duration in h:mm:ss format will display as soon as the audio src data is loaded. Sweetness.
I used "canplaythrough" event to get the track duration. I have a case where I have two players, and I want to stop the second player 2 seconds before the first one is complete.
$('#' + _currentPlayerID).on("canplaythrough", function (e) {
var seconds = e.currentTarget.duration;
var trackmills = seconds * 1000;
var subTimeout = trackmills - 2000; //2 seconds before end
//console.log('seconds ' + seconds);
//console.log('trackmills ' + trackmills);
//console.log('subTimeout ' + subTimeout);
//Stop playing before the end of thet track
//clear this event in the Pause Event
_player2TimeoutEvent = setTimeout(function () { pausePlayer2(); }, subTimeout);
});
Simply use audioElement.duration
To obtain the end of reading it is necessary that 'loop = false' with the event 'onended'. If loop = true, onended does not work;)
To make a playlist, you have to set loop = false to use the 'onended' event in order to play the next song.
for the duration if your script is done correctly you can recover the duration anywhere. If the value of 'duration' is NaN the file is not found by the browser. If the value is 'Infinity' 'INF' it is a streaming, in this case, adds 1 mn compared to the reading time 'currentime'.
For * I.E it's crap. Currenttime may be greater than duration, in which case you do:
var duration = (this.currentime> this.duration)? this.currenttime: this.duration;
That's (O_ °)
it's better to use the event like this ...
ObjectAudio.onprogress =function(){
if(this.buffered.length){
var itimeend = (this.buffered.length>1)? this.buffered.end(this.buffered.length-1):this.buffered.end(0);
....
Your code here.......
}
}

Any way to get a currentTime value prior to the seek on HTMLMediaElement?

Let's say our app is using the default video player on Safari.
When a user is playing a video and then attempts to move to a different position of the video using the seek bar, it seems like pause event is fired first, and then we'll get seeking and seeked events fired.
I am wondering if we can get the currentTime value prior to the seek. For instance, assuming that a user jumps from t = 7 to t = 42 using the seek bar, I want to get 7 as the currentTime value somehow.
I expected that we could get this value by accessing currentTime property inside the pause event handler that is invoked right after the seek like the following:
const video = document.querySelector('#myvideo');
video.addEventListener('pause', () => {
// I expected that the `video.currentTime` here has the "previous" position,
// but it already points to the new position
console.log(video.currentTime);
});
but unfortunately the currentValue was already updated to the new value at that point.
Is there any good way to achieve it?
(EDIT)
Caching currentTime manually doesn't help, because apparently a timeupdate event fires before a pause event. More specifically, taking the following code as an example, when a user attempts to jump to another position, cache and currentTime printed within the pause handler seem always identical.
<!DOCTYPE html>
<html>
<body>
<video
id="myvideo"
width="640"
height="360"
controls
src="video.mp4"
></video>
</body>
<script>
const video = document.querySelector("#myvideo");
let cache = 0;
video.addEventListener("timeupdate", () => {
cache = video.currentTime;
});
video.addEventListener("pause", () => {
console.log({ cache, currentTime: video.currentTime });
});
</script>
</html>
I think #Kaiido means this when saying "Cache two values".
Code is untested (but looks better than being kept in comments section)
<script>
const video = document.querySelector("#myvideo");
let cache = 0;
let cache_prev = 0;
video.addEventListener("timeupdate", () => {
cache_prev = cache; //# save last known value
cache = video.currentTime; //# before updating to new currentTime
});
video.addEventListener("pause", () => {
console.log("cache_prev : " + cache_prev );
console.log("cache : " + cache );
console.log("currentTime : " + video.currentTime );
});
</script>

having jerks when playing video on mousemove

I am facing a problem with video playback when mousemove function is applied. the function is working but when the video is played it has jerks. I want it to be smooth with an easing function maybe. Please guide me on how to play video without hanging up in between.
const startDrawing = () => {
const video = document.querySelector("video");
let currentFrame = 0
let mediaTime;
let paintCount = 0;
let startTime = 0.0;
let fps = 60
video.pause();
window.addEventListener("mousemove", () => {video.currentTime = video.currentTime + 1/fps});
video.addEventListener("play", () => {
if (!("requestVideoFrameCallback" in HTMLVideoElement.prototype)) {
return alert("Your browser does not support the `Video.requestVideoFrameCallback()` API.");
}
});
};
window.addEventListener("load", startDrawing);
Courtesy of Big Buck Bunny: </br><video width="320" height="240" autoplay="" muted="" loop="" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
Dont play with frames, Play video in mousemove event else pause the video.

Using 'this.currentTime' to get the time of a video and reset it to the starting point on 'hover out'

I have a video library where I want to dynamically use the Media Fragment time in URL as the poster.
When hovering out, I am trying to reset the video to the initial start - to make sure the poster is at 2 seconds (in this specific example) instead of 0.
this.load works but creates a bad user experience as the whole video loads in again.
My idea is to define the current time as a variable (before the video starts playing) and use it when pausing the video.
However I just get "Uncaught ReferenceError: posterTime is not defined".
<video id="video">
<source src="videourl.mp4#t=2" type="video/mp4">
</video>
const videos = document.querySelectorAll("video")
videos.forEach(video => {
video.addEventListener("mouseover", function () {
var posterTime = this.currentTime;
this.currentTime = 0;
this.play()
})
video.addEventListener("mouseout", function () {
this.currentTime = posterTime;
this.pause();
})
})
Note that I use Webflow and is not very strong with jQuery/Javascript.
My idea is to define the current time as a variable (before the video
starts playing) and use it when pausing the video. However I just get
"Uncaught ReferenceError: posterTime is not defined".
Your idea and code is fine but you made a basic mistake.
Remember: A variable defined inside a function will exist only for that function where it was created.
Use let for internal variables (where possible) and use var for global variables.
solution: Define the variable as global (outside of any functions)...
const videos = document.querySelectorAll("video");
var posterTime = -1; //# global var, with starting value...
videos.forEach(video => {
video.addEventListener("mouseover", function ()
{
posterTime = this.currentTime; //# set time
this.currentTime = 0;
this.play()
})
video.addEventListener("mouseout", function ()
{
this.currentTime = posterTime; //# get time
this.pause();
})
})
As I hover out I need it to show that initial frame again (not the first frame, but the one set in the URL)
Given this requirement you can retrieve the fragment from the URL in the src attribute of the source element and apply it to the currentTime of the video when the mouseleave event occurs:
const videos = document.querySelectorAll("video")
videos.forEach(video => {
video.addEventListener("mouseenter", function() {
this.currentTime = 0;
this.play()
})
video.addEventListener("mouseleave", function() {
let src = this.querySelector('source').src;
let time = (src.split('#')[1] || 't=0').split('=')[1];
this.currentTime = time;
this.pause();
})
})
<video id="video">
<source src="http://grochtdreis.de/fuer-jsfiddle/video/sintel_trailer-480.mp4#t=5" type="video/mp4">
</video>

How to approach a setInterval triggering a setInterval within the same function

I a rewards site. Users earn points when they watch videos. Users want to skip to the end of the video to quickly earn their points. Therefore, the mechanism of the video being marked as completed is managed by a JS timer.
The Problem: When a user pauses the video, the timer needs to pause. When the user clicks play again, a setInterval is used to reopen the function, where a setInterval within the existing function does the same thing, creating a huge problem...
This is the code that is set every second to update the timer...
var video_percent_count = 0;
function video_percent() {
var prize_video = document.getElementById("prize_video");
total_duration = Math.floor(prize_video.duration) + 1;
video_percent_count++;
percent = video_percent_count / total_duration;
convert_percent = (percent * 100).toFixed(2);
if(convert_percent == 100) {
clearInterval(video_percent_interval);
}
if(prize_video.paused) {
clearInterval(video_percent_interval);
}
if(prize_video.play) {
setInterval("video_percent()", 1000);
}
$(".percent_container").text(convert_percent);
}
function prize_video(count_prize) {
var back_count_prize = count_prize - 1;
$("#prize_video").get(back_count_prize).play();
video_percent_interval = setInterval("video_percent()", 1000);
}
How can I properly manage play and pause and still keep setInterval clean?

Categories

Resources