skip to Main Content

I am using videojs for displaying a film, and depending on the time of the video (timeupdate), I display a description from the backend, that is required for this project.

In case of a problem with loading the description on time (but not with buffering of the video), I’d like to stop the video, for example after 3 seconds delay, and continue the video when the description fetch finishes downloading the data.

I am not sure whether I can use a wrapped setTimeout and fetch with Promise for solving this problem.

Is it better to repeat fetch after video has stopped by using AbortController? Is this a proper way to approach this?

This is what I use for downloading the data without setTimeout and starting/stopping the video:

const player = videojs('my-video');
player.on('timeupdate', async function(){
        //...
        const verset = await checkVerset(zone);
        //...
});
async function checkVerset(zone) {  
    let zoneRes;
    await connectFetch('./in.php', zone ).then(resolve =>{ 
        zoneRes = resolve;
        console.log(zoneRes);   
    }).catch(error => console.log( 'Error of details: ', error));
    return zoneRes; 
}
async function connectFetch (url, text) {
    const response = await fetch(url, { 
        method:'post', 
        mode:'cors',
        credentials:'same-origin',
        headers:{'Content-type':'text/plain;charset=UTF-8'}, 
        body: JSON.stringify(text) 
    })
    if(!response.ok){
        const message = 'Error: ${response.status}';
        throw new Error(message);
    }
    const resolve  = response.text();
    return resolve;
}
//player.pause();
//player.play();

2

Answers


  1. Chosen as BEST ANSWER

    I have edited function by add setTimeout and clearTimeout. Now program is working properly even with small time(500ms). For smooth play video I use more period and I will try implement function of download data ahead of proper time displaying descriptions.

    async function checkVerset(zone) {  
    let zoneRes;
    console.log('fetch: ');
    const timer = setTimeout( function(){ 
        player.pause(); 
        console.log('pause');
    }, 500);
    await connectFetch('./info.php', zone ).then(resolve =>{
        clearTimeout(timer);
        console.log('clearTimeout');
        zoneRes = resolve;
        console.log(zoneRes);   
    }).catch(error => console.log( 'Error of details: ', error));
    if(player.pause){
        player.play();
        console.log('play');
    }
    return zoneRes; 
    }   
    

    Thank you


  2. Here is a basic implementation (that can further be optimized) that you can use a start point:

    const video = document.querySelector('video');
    const descEl = document.querySelector('#description');
    
    // These are hard-coded for now, just for testing. 
    // Ideally, they can be created dynamically.
    const lockTimestamps = {
      t5: false,
      t10: false,
      t15: false,
      t25: false,
      t45: false,
    }
    // ...same goes here. Another structure that can be 
    // created dynamically.
    const descriptions = {
      t5: null,
      t10: null,
      t15: null,
      t25: null,
      t45: null,
    }
    
    // ...same here. t10, t25 and t45 have a delay, just to test whether the video pauses as expected.
    const URLS = {
     t5: "https://jsonplaceholder.typicode.com/users/1",
     t10: "https://jsonplaceholder.typicode.com/users/2?_delay=5000&debug=" + Math.random(),
     t15: "https://jsonplaceholder.typicode.com/users/3",
     t25: "https://jsonplaceholder.typicode.com/users/4?_delay=4000&debug=" + Math.random(),
     t45: "https://jsonplaceholder.typicode.com/users/5?_delay=8000&debug=" + Math.random(),
    }
    
    const threshold = 2000;
    
    function loadDescription(id, currentTime, video){
    
      const time = Number(id.slice(1));
    
      if ( currentTime === time && !lockTimestamps[id] ){
        
        console.log(`Loading ${time} description`)
    
        lockTimestamps[id] = true; // Ensures that this call does not run 2 or more times.
    
        setTimeout(()=>{
          if ( descriptions[id] === null ){
            console.log(`Ops! Description ${id} takes a bit too long. Pausing video and waiting for response.`);
            descEl.textContent = `Description ${id} takes a bit too long. Pausing`;
            video.pause();        
          }
        },threshold);
        
        descEl.textContent = `Loading description for ${id}`;
        
        // Start fetch and display description.
        fetch(URLS[id]).then( res => res.json()).then( data => {
          descriptions[id] = data;
          descEl.textContent = `${id}: ${descriptions[id].name}`
          if ( video.paused ) { video.play() }; // Resume if video is paused.
        });
      } 
    
    }
    
    video.addEventListener("timeupdate", (e) =>{
      const currentTime = Math.floor(video.currentTime);
      
      // NOTE: This point here can be optimized, so that we don't need
      // to loop over all the timestamps once we've past some point in time.
      for ( const timestamp in descriptions ){
        loadDescription(timestamp, currentTime, video);
      }
    });
    <div id="description">Descriptions will appear here:</div>
    <video src="https://assets.codepen.io/13471/trailer.webm" controls />

    It’s important to point, that the threshold must be less than the actual timestamp distance, as we might run into race conditions which means the code will require substantial additional checks. I am keeping it simple for now, and setting threshold to 2".


    Sidenote: I came up with this quick Excalidraw diagram before diving into the code, just to let my intuition guide the implementation.

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search