davis
davis

Reputation: 1336

Get video duration when input a video file

I'm doing a project with HTML and Javascript that will run local with local files. I need to select a file by input, get its information and then decide if I'll add to my list and reproduce or not. And if Ii decide to use it I'll have to put it on a queue to use later. Otherwise I'll just discard and select another file.

The problem that I'm facing is that I can't find a way to get the video duration just by selecting it on input.

I've searched a lot and I didn't find any method to get the duration. In this code below I tried to use 'file.duration' but it didn't work, it just returns 'undefined'.

This is my input, normal as you can see.

<div id="input-upload-file" class="box-shadow">
   <span>upload! (ღ˘⌣˘ღ)</span> <!--ignore the text face lol -->
   <input type="file" class="upload" id="fileUp" name="fileUpload" onchange="setFileInfo()">
</div>

And this is the function that I'm using to get all the information.

function setFileInfo(){
  showInfo(); //change div content
  var file = document.getElementById("fileUp").files[0];
  var pid =  1;
  var Pname = file.name;
  Pname = Pname.slice(0, Pname.indexOf(".")); //get filename without extension
  var Ptype = file.type;
  var Psize = bytesToSize(file.size); //turns into KB,MB, etc...
  var Pprior = setPriority(Ptype); //returns 1, 2 or 3 
  var Pdur = file.duration;
  var Pmem = getMemory(Psize); //returns size * (100 || 10 || 1)
  var Pown = 'user';
  /* a lot of stuff throwing this info to the HTML */
  console.log(Pdur);
}

Is there way to do this? If not, what are the alternatives that can help me?

Upvotes: 65

Views: 147796

Answers (6)

Pavel Kalashnikov
Pavel Kalashnikov

Reputation: 2091

I've implemented getting video duration in nano-metadata package https://github.com/kalashnikovisme/nano-metadata.

You can do something like this

import nanoMetadata from 'nano-metadata'

const change = (e) => {
  const file = e.target.files[0]
  
  nanoMetadata.video.duration(file).then((duration) => {
    console.log(duration) // will show you video duration in seconds
  })
}

Upvotes: 3

Nisharg Shah
Nisharg Shah

Reputation: 19542

It's simple to get video duration from FileReader and it's easy to manage in async/await.

const getVideoDuration = file =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const media = new Audio(reader.result);
      media.onloadedmetadata = () => resolve(media.duration);
    };
    reader.readAsDataURL(file);
    reader.onerror = error => reject(error);
  });
const duration = await getVideoDuraion(file);

where file is File object

Live Example

const getVideoDuration = (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const media = new Audio(reader.result);
      media.onloadedmetadata = () => resolve(media.duration);
    };
    reader.readAsDataURL(file);
    reader.onerror = (error) => reject(error);
  });

const handleChange = async (e) => {
  const duration = await getVideoDuration(e.target.files[0]);
  document.querySelector("#duration").innerText = `Duration: ${duration}`;
};
<div>
  <input type="file" onchange="handleChange(event)" />
  <p id="duration">Duration: </p>
</div>

Upvotes: 18

Milind
Milind

Reputation: 5112

This is how I managed to get video duration before pushing it on S3.. I am using this code to upload video files of 4+ GB.

Note - for formats like .avi,.flv, .vob, .mpeg etc, duration will not be found, so handle it with a simple message to the user

  //same method can be used for images/audio too, making some little changes
  getVideoDuration = async (f) => {
    const fileCallbackToPromise = (fileObj) => {
      return Promise.race([
        new Promise((resolve) => {
          if (fileObj instanceof HTMLImageElement) fileObj.onload = resolve;
          else fileObj.onloadedmetadata = resolve;
        }),
        new Promise((_, reject) => {
          setTimeout(reject, 1000);
        }),
      ]);
    };

    const objectUrl = URL.createObjectURL(f);
    // const isVideo = type.startsWith('video/');
    const video = document.createElement("video");
    video.src = objectUrl;
    await fileCallbackToPromise(video);
    return {
      duration: video.duration,
      width: video.videoWidth,
      height: video.videoHeight,
    };
}


//call the function
//removing unwanted code for clarity
const meta = await this.getVideoDuration(file);
//meta.width, meta.height, meta.duration is ready for you to use

Upvotes: 6

Adam
Adam

Reputation: 1518

Here is async/await Promise version:

const loadVideo = file => new Promise((resolve, reject) => {
    try {
        let video = document.createElement('video')
        video.preload = 'metadata'

        video.onloadedmetadata = function () {
            resolve(this)
        }

        video.onerror = function () {
            reject("Invalid video. Please select a video file.")
        }

        video.src = window.URL.createObjectURL(file)
    } catch (e) {
        reject(e)
    }
})

Can be used as follows:

const video = await loadVideo(e.currentTarget.files[0])
console.log(video.duration)

Upvotes: 19

ricks
ricks

Reputation: 3324

I needed to validate a single file before continuing to execute more code, here is my method with the help of Kaiido's answer!

onchange event when a user uploads a file:

$("input[type=file]").on("change", function(e) {

    var file = this.files[0]; // Get uploaded file

    validateFile(file) // Validate Duration

    e.target.value = ''; // Clear value to allow new uploads
})

Now validate duration:

function validateFile(file) {

    var video = document.createElement('video');
    video.preload = 'metadata';

    video.onloadedmetadata = function() {

        window.URL.revokeObjectURL(video.src);

        if (video.duration < 1) {

            console.log("Invalid Video! video is less than 1 second");
            return;
        }

        methodToCallIfValid();
    }

    video.src = URL.createObjectURL(file);
}

Upvotes: 27

Kaiido
Kaiido

Reputation: 136668

In modern browsers, You can use the URL API's URL.createObjectURL() with an non appended video element to load the content of your file.

var myVideos = [];

window.URL = window.URL || window.webkitURL;

document.getElementById('fileUp').onchange = setFileInfo;

function setFileInfo() {
  var files = this.files;
  myVideos.push(files[0]);
  var video = document.createElement('video');
  video.preload = 'metadata';

  video.onloadedmetadata = function() {
    window.URL.revokeObjectURL(video.src);
    var duration = video.duration;
    myVideos[myVideos.length - 1].duration = duration;
    updateInfos();
  }

  video.src = URL.createObjectURL(files[0]);;
}


function updateInfos() {
  var infos = document.getElementById('infos');
  infos.textContent = "";
  for (var i = 0; i < myVideos.length; i++) {
    infos.textContent += myVideos[i].name + " duration: " + myVideos[i].duration + '\n';
  }
}
<div id="input-upload-file" class="box-shadow">
  <span>upload! (ღ˘⌣˘ღ)</span>
  <input type="file" class="upload" id="fileUp" name="fileUpload">
</div>
<pre id="infos"></pre>

Upvotes: 122

Related Questions