rlsaj
rlsaj

Reputation: 735

Iterate Through Folders/Subfolders/Files in Google Drive and get video duration

My goal is to calculate the total length - in seconds - of all videos within all sub directories of a Google Drive folder.

I have followed the Answer from here: Iterate Through Folders/Subfolders/Files in Google Drive to try and create a script that iterates through all directories within a folder and grabs the file count, plus file name and video duration if the file is a video.

I am currently using:

function testTraverse(){
  var originFolder = DriveApp.getFoldersByName('All Media').next(); 
  var totalCount = traverseFolder(originFolder,0);
  Logger.log('total files = '+totalCount);
}

 function getVideoDuration(folder,folderId, files) {  
  var q = "'" + folderId + "' in parents and trashed=false";
  var fields = "files(mimeType,name,videoMediaMetadata)"; 
  var url = "https://www.googleapis.com/drive/v3/files?q=" + encodeURIComponent(q) + "&fields=" + encodeURIComponent(fields) + "&access_token=" + ScriptApp.getOAuthToken();
  var res = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
  var obj = JSON.parse(res);  

   for (var i = 0; i < obj.files.length; i++) {
      sum += obj.files[i].videoMediaMetadata.durationMillis / 1000; 
   }
   Logger.log("Total duration: " + sum);    
 }

function traverseFolder(folder,total) {
  var name = folder.getName();
  var count = 0;
  var files = folder.getFiles();     
  var folderId = folder.getId();     


  while (files.hasNext()) {
    count++; 
    getVideoDuration(folderId, files);

  }

  var subs = folder.getFolders();
  while (subs.hasNext()) {
    total+=traverseFolder(subs.next(),count);
  }
  return total;
}

Running the above throws an error from the line: for (var i = 0; i < obj.files.length; i++):

Cannot read property 'length' of undefined

Which is confusing, because I am in a directory with multiple files.

And the function I am way out of my depth with is the getVideoDuration, which I used from the sample script from the answer here: How to get file (video) duration of google drive file programmatically?.

My desired result is to calculate the total length - in seconds - of all videos within all sub directories.

Is there a simpler way to do this?

Upvotes: 0

Views: 1420

Answers (1)

Tanaike
Tanaike

Reputation: 201358

Understanding

  • You want to retrieve all files under the all sub folders in the specific folder.
  • From retrieved all files, you want to retrieve only video files. And you want to retrieve the number of files, filenames and the total length of the video files.
  • You want to achieve this using Google Apps Script.

Modification points:

  • I think that your error of Cannot read property 'length' of undefined is due to the following 2 reasons.
    1. About getVideoDuration(folderId, files) in traverseFolder, you send folderId, files as the arguments. But at the function of getVideoDuration, the script is getVideoDuration(folder,folderId, files). By this, the folder ID is not used. And also, files is not used in getVideoDuration.
    2. From this official document, the query parameter of access_token cannot be used now. So it is required to use the access token at the request header. I thought that I had modified my answers related to this. But I had forgot to modify to this answer. I apologize for this.
  • sum is not declared. By this, an error occurs.
  • When the files except for video are in the folder, an error occurs at obj.files[i].videoMediaMetadata.durationMillis.

When above points are reflected to your script, it becomes as follows.

Modified script:

// Please run this function.
function testTraverse() {
  var originFolder = DriveApp.getFoldersByName('All Media').next(); 
  var res = traverseFolder(originFolder, {totalLength: 0, totalFiles: 0, filenames: []});
  Logger.log(res)
}

function getVideoDuration(folderId, o) {
  var q = "'" + folderId + "' in parents and trashed=false";
  var fields = "files(mimeType,name,videoMediaMetadata)"; 
  var url = "https://www.googleapis.com/drive/v3/files?q=" + encodeURIComponent(q) + "&fields=" + encodeURIComponent(fields);
  var res = UrlFetchApp.fetch(url, {muteHttpExceptions: true, headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()}});
  var obj = JSON.parse(res);
  for (var i = 0; i < obj.files.length; i++) {
    var file = obj.files[i];
    if (file.hasOwnProperty("videoMediaMetadata")) {
      o.filenames.push(file.name);
      o.totalFiles++;
      o.totalLength += file.videoMediaMetadata.durationMillis / 1000;
    }
  }
 }

function traverseFolder(folder, obj) {
  var name = folder.getName();
  var count = 0;
  var files = folder.getFiles();
  var folderId = folder.getId();
  if (files.hasNext()) getVideoDuration(folderId, obj);
  var subs = folder.getFolders();
  while (subs.hasNext()) {
    traverseFolder(subs.next(), obj);
  }
  return obj;
}
  • In this case, the result returns an object like {totalLength: ##, totalFiles: ##, filenames: [###]}. totalLength, totalFiles and filenames are the total length of video length, the total number of video files and the filenames of video files, respectively.

Note:

  • Unfortunately, I'm not sure about the mimeTypes of your video files. So if the mimeTypes are found, I think that the process cost can be reduce a little. Because the files can be retrieved by the mimeTypes.
  • I think that this modified script can be run with and without V8.

Reference:

Upvotes: 2

Related Questions