1

In short, I am struggling to figure out when the video reaches 80% AND 80% of the movie has been played in order to prevent people from fast forwarding.

I currently manage to fire a method, once the movie ends and the total value of the amount played is equal to or larger than the duration of the video.

var video = document.getElementById("video");

var timeStarted = -1;
var timePlayed = 0;
var duration = 0;
var checkpoint = 0;
var percentage = 0.8;
var percentComplete = 0;

// If video metadata is laoded get duration
if(video.readyState > 0)
  getDuration.call(video);
//If metadata not loaded, use event to get it
else
{
  video.addEventListener('loadedmetadata', getDuration);
}
// remember time user started the video
function videoStartedPlaying() {
  timeStarted = new Date().getTime()/1000;
}
function videoStoppedPlaying(event) {
  // Start time less then zero means stop event was fired vidout start event
  if(timeStarted>0) {
    var playedFor = new Date().getTime()/1000 - timeStarted;
    timeStarted = -1;
    // add the new ammount of seconds played
    timePlayed+=playedFor;
  }
  document.getElementById("played").innerHTML = Math.round(timePlayed)+"";
  // Count as complete only if end of video was reached
  if(timePlayed>=duration && event.type=="ended") {
    document.getElementById("status").className="complete";
  }
}

function videoIsPlaying(event) {
  if(timeStarted>0) {
    var playedFor = new Date().getTime()/1000 - timeStarted;
    timeStarted = -1;
    // add the new ammount of seconds played
    timePlayed+=playedFor;
  }
  
  checkpoint = playedFor / duration;
  percentComplete = video.currentTime / video.duration;
  
  console.log('timePlayed is '+timePlayed);
  console.log('percentComplete is '+percentComplete);
  console.log('checkpoint is '+checkpoint);
  console.log('duration is '+duration);
  console.log('playedFor is '+playedFor);
  
  if (percentComplete >= percentage && checkpoint >= percentage) {
   
  }
}

function getDuration() {
  duration = video.duration;
  document.getElementById("duration").appendChild(new Text(Math.round(duration)+""));
  console.log("Duration: ", duration);
}

video.addEventListener("play", videoStartedPlaying);
video.addEventListener("playing", videoStartedPlaying);

video.addEventListener("timeupdate", videoIsPlaying);

video.addEventListener("ended", videoStoppedPlaying);
video.addEventListener("pause", videoStoppedPlaying);
#status span.status {
  display: none;
  font-weight: bold;
}
span.status.complete {
  color: green;
}
span.status.incomplete {
  color: red;
}
#status.complete span.status.complete {
  display: inline;
}
#status.incomplete span.status.incomplete {
  display: inline;
}
<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"></source>
</video>

<div id="status" class="incomplete">
<span>Play status: </span>
<span class="status complete">COMPLETE</span>
<span class="status incomplete">INCOMPLETE</span>
<br />
</div>
<div>
<span id="played">0</span> seconds out of 
<span id="duration"></span> seconds. (only updates when the video pauses)
</div>
<br><br>

I would appreciate any hints and snippets into the right direction!

Chris Palmer Breuer
  • 680
  • 1
  • 10
  • 24
  • Use `timeupdate` event, see [HTML5 audio streaming: precisely measure latency?](http://stackoverflow.com/questions/38768375/html5-audio-streaming-precisely-measure-latency/38842623#38842623) – guest271314 Feb 18 '17 at 20:16
  • @LGSon, did you read my question and not realize that I am asking something quite different? I know how to get the currentTime and duration without the reference to your questions. I am asking for help in regard of how I can utilize such events to track how much the video has been watched in total. For example, if I rewind a 30s video at half point to the beginning and I then finish it, my `timePlayed` will reflect 45s then. @guest271314 thanks for link to your previous answer. Could you please elaborate a little more as to how I could utilize timeupdate in my case? – Chris Palmer Breuer Feb 18 '17 at 20:34
  • Where do you try to check if 80% of video has been played? Note also, if user seeks to a `0` after playing half of `duration`, current implementation still updates total duration of video played using a time range that has already been played. You can store current times within `timeupdate` event to an array, only update total time played if ranges are greater than values stored within array. – guest271314 Feb 18 '17 at 20:39
  • I am currently not checking for the 80%. I only have the `var checkpoint`. I checked before within `function videoStoppedPlaying`, but then realized it is not beneficial, cause the user has to pause the video (or it has to end) in order for it to work. That's when I found got aware of `timeupdate` & as you hinted, that's what I need to use. I agree, but it seems that I have a mental blockade when it comes to the implementation. https://fiddle.jshell.net/x84dsuv5/3/ I tried to check against the percentage played and percentage completed. The checkpoint outputs NaN & playedFor undefined? hmm – Chris Palmer Breuer Feb 18 '17 at 21:06
  • _"I am currently not checking for the 80%."_ Well, that is Question, yes? Consider posting a Question including the attempt you made to check if 80% of video has been played, if the linked Questions and Answers do not resolve current Question. Will try to put together a fork of your jsfiddle using either `timeupdate` or `progress` event. – guest271314 Feb 18 '17 at 21:18
  • I meant to say I was not currently checking for the 80% in the question I initially asked on here as I knew the approach I was dong was wrong. The jsfiddle I linked to does check for it, but the playedFor variable is empty and that's where it currently hiccups. edit: I did update the fiddle in my question on here as well. – Chris Palmer Breuer Feb 18 '17 at 21:22

2 Answers2

2

Create an Array and a Set object. At loadedmetadata event set value which is measurement of .8 of .duration Math.ceil(video.duration * percent), where percent is a decimal less than 1. Use for loop to fill Array instance with values from 1 to N, where N is eighty-percent of .duration +1 as an integer. Call .add() on Set instance with Math.ceil(video.currentTime) as parameter at timeupdate event. At timeupdate event iterate Array using .every() to check if each element of Set .has() element of Array, including Math.ceil(video.duration * percent), which will be eighty-percent or greater of .duration; if true is returned from .every() call function.

const video = document.getElementById("video");
const res = new Set();
const arr = Array();
const percent = .8;
let toWatch = 0;

function mediaWatched (curr) {
  alert(`${curr}% of media watched`)
}

function handleMetadata(e) {
  toWatch = Math.ceil(video.duration * percent);
  for (let i = 1; i <= toWatch + 1; i++) {
    arr.push(i);
  }
}

function handleTimeupdate (e) {
  res.add(Math.ceil(video.currentTime));
  if (arr.every(function(n) {return res.has(n)})) {
    console.log(video.currentTime, video.duration);
    video.removeEventListener("timeupdate", handleTimeupdate);
    mediaWatched(Math.ceil((video.currentTime / 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>
guest271314
  • 1
  • 15
  • 104
  • 177
  • Thanks! This works and lets the user know when the 80% checkmark was reached/played. But where I struggle with is that on top of it, I want to make sure that 80% has been watched and it has not been forwarded to that point. Does that make sense? – Chris Palmer Breuer Feb 18 '17 at 21:59
  • @ChrisPalmerBreuer `Set` does not allow duplicate entries. `Set.prototype.add()` is only called at `timeupdate` event. See updated post. Added check for `.length` of `.keys()` of `Set` passed to `Array.from()` which must be equal to `toWatch` for function to be called. This handles cases of fast forward, and reverse seeking. – guest271314 Feb 18 '17 at 22:17
  • Perfect, that sounds exactly like what I am looking for! Unfortunately, the `mediaWatched` is not called anymore. I am not too sure if I can find the hiccup as I am fairly new to JavaScript all together. – Chris Palmer Breuer Feb 18 '17 at 22:22
  • The event listener is removed. `javascript` at Answer demonstrates approach of calling a function when 80% of video is reached once. Configure the implementation to suit specific requirement. See updated post. – guest271314 Feb 18 '17 at 22:41
  • Great, that works. But for example, if I fast forward to around 50% length, and let it play then, it will fire whenever 80% of the movie is reached, but it shouldn't because I fast forwarded to 50% and essentially only watched 30%. Does that make sense? I need to have a `playedFor` variable that will count how many seconds have been played totally which is not equal to `currentTime`. e.g., there is a 30s video and if it was initially played to 50%, `playedFor` would equal `currentTime` and it would be 15, but then if I rewind to the beginning and watch till the end, `playedFor` would be 45 – Chris Palmer Breuer Feb 18 '17 at 23:02
  • This is where I struggle figuring out how I can have a variable to keeps on adding on top of itself. If you have an idea how that could be implemented into your approach, I would be forever grateful. – Chris Palmer Breuer Feb 18 '17 at 23:08
1

MDNs Cross-browser audio basics

timeupdate

The timeupdate event is fired every time the currentTime property changes. In practice this occurs every 250 milliseconds. This event can be used to trigger the displaying of playback progress.

myAudio.addEventListener("timeupdate", function() {
  //update something related to playback progress
});
Community
  • 1
  • 1
Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91