Search code examples
javascripthtmlreactjsiframevideo-reactjs

Javascript: How to count how much time a user spent watching a video?


I am trying to save how much time a user spent watching video.
I stumbled upon the played video attribute:

It’s worth mentioning the played property — this tells us which time ranges have been played within the media. For example:

var played = audio.played; // returns a TimeRanges object

This could be useful for establishing the parts of your media that are most listened to or watched.

In my React app, I tried using that like this:

  handleStateChange(state, prevState) {
    const { played } = state;
    for (let index = 0; index < played.length; index++) {
      const beginning_of_played_segment = Math.trunc(played.start(index));
      const end_of_played_segment = Math.trunc(played.end(index));
    }
  }

handleStateChanged is used to subscribe to the video player state change:

  componentDidUpdate(prevProps, prevState) {
    if (this.state.player && !this.state.hasSubscribedToPlayerState) {
      this.state.player.subscribeToStateChange(
        this.handleStateChange.bind(this)
      );
      this.setState({
        hasSubscribedToPlayerState: true,
      });
    }
  }

If I play the video between [0..8] and [347..357] secnds, the function handleStateChange logs something like this:

🚀 ~ file: VideoItem.js ~ line 212 ~ VideoItem ~ handleStateChange ~ beginning_of_play_segment 0

🚀 ~ file: VideoItem.js ~ line 212 ~ VideoItem ~ handleStateChange ~ end_of_play_segment 8

🚀 ~ file: VideoItem.js ~ line 212 ~ VideoItem ~ handleStateChange ~ beginning_of_play_segment 347

🚀 ~ file: VideoItem.js ~ line 212 ~ VideoItem ~ handleStateChange ~ end_of_play_segment 357

And then I thought I could just calculate the difference between all the beginning_of_played_segment and end_of_played_segment values.

The problem with this approach is that if I replay the video in a previously played segment, for example if I restart the video in the segment [0..8] in second 4, played will not create a new segment, and it will continue counting in that same segement.

For example, if I restart in 4, and continue playing until 13, the segment [0..8] will become [0..13].

This makes sense given that the played property tells us which time ranges have been played within the media.

However, I would like to know if there's a property that would allow me to achieve my goal, of creating a new segment, everytime the user plays the video anywhere so that I can record how much time exactly he spent watching the video.


Solution

  • I have managed to implement a workaround. However, I am not sure this is the best approach. But, it works:

      constructor() {
        super();
        this.state = {
          start_of_current_segment: 0,
          current_segment_duration: 0,
          segments: [],
          total_segments_duration: 0,
        };
       
      }
    
      handlePlayerStateChange(state, prevState) {
        const userHasJustPlayedVideo = prevState.paused && !state.paused;
        const userHasJustPausedVideo = !prevState.paused && state.paused;
    
        const { currentTime, seeking, hasStarted } = state;
        const has_user_moved_video_to_a_new_position = seeking;
        let { start_of_current_segment, segments } = this.state;
        if (userHasJustPlayedVideo) {
          segments = [...segments, 0];
          this.setState({
            start_of_current_segment: Math.trunc(currentTime),
            segments: segments,
          });
        } else if (userHasJustPausedVideo) {
        } else if (has_user_moved_video_to_a_new_position) {
          segments = [...segments, 0];
          this.setState({
            start_of_current_segment: Math.trunc(currentTime),
            segments: segments,
          });
        } else if (hasStarted) {
          const current_segment_duration =
            Math.trunc(currentTime) - start_of_current_segment;
          const segment_index = segments.length - 1;
          segments[segment_index] = current_segment_duration;
    
          let total_segments_duration = 0;
          for (
            let segment_index = 0;
            segment_index < segments.length;
            segment_index++
          ) {
            total_segments_duration += segments[segment_index];
          }
    
          this.setState({
            current_segment_duration: current_segment_duration,
            segments: segments,
            total_segments_duration: total_segments_duration,
          });
    
        }
      }