How to get file (video) duration of google drive file programmatically? - google-apps-script

Either using rest API, Google Scripts, Node SDK, whatever works.
I'm seeing this in the docs but that doesn't seem to tell me the duration:
function watchFile(fileId, channelId, channelType, channelAddress) {
var resource = {
'id': channelId,
'type': channelType,
'address': channelAddress
};
var request = gapi.client.drive.files.watch({
'fileId': fileId,
'resource': resource
});
request.execute(function(channel){console.log(channel);});
}
I found this link but it doesn't seem to help https://apis-nodejs.firebaseapp.com/drive/classes/Resource$Files.html#watch

You want to retrieve the duration of the video on your Google Drive.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this sample script? In this modification, I used files.get and files.list methods of Drive API. From your question, I thought that the script that the endpoint is directly requests might be useful for your situation. So I proposed the following script.
1. Using files.get method
In this sample script, the duration is retrieved from a video file.
Sample script:
function sample1() {
var fileId = "###"; // Please set the file ID of the video file.
var fields = "mimeType,name,videoMediaMetadata"; // duration is included in "videoMediaMetadata"
var url = "https://www.googleapis.com/drive/v3/files/" + fileId + "?fields=" + encodeURIComponent(fields) + "&access_token=" + ScriptApp.getOAuthToken();
var res = UrlFetchApp.fetch(url);
var obj = JSON.parse(res);
Logger.log("filename: %s, duration: %s seconds", obj.name, obj.videoMediaMetadata.durationMillis / 1000);
// DriveApp.getFiles() // This line is put for automatically detecting the scope (https://www.googleapis.com/auth/drive.readonly) for this script.
}
2. Using files.list method
In this sample script, the durations are retrieved from a folder including the video files.
Sample script:
function sample2() {
var folderId = "###"; // Please set the folder ID including the video files.
var q = "'" + folderId + "' in parents and trashed=false";
var fields = "files(mimeType,name,videoMediaMetadata)"; // duration is included in "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);
var obj = JSON.parse(res);
for (var i = 0; i < obj.files.length; i++) {
Logger.log("filename: %s, duration: %s seconds", obj.files[i].name, obj.files[i].videoMediaMetadata.durationMillis / 1000);
}
// DriveApp.getFiles() // This line is put for automatically detecting the scope (https://www.googleapis.com/auth/drive.readonly) for this script.
}
Note:
These are simple sample scripts. So please modify them for your situation.
I'm not sure about the format of your video files. So if above script cannot be used for your situation, I apologize.
References:
Files of Drive API
Class UrlFetchApp
If I misunderstood your question and this was not the result you want, I apologize.
Updated: March 19, 2020
From January, 2020, the access token cannot be used with the query parameter like access_token=###. Ref So please use the access token to the request header instead of the query parameter. It's as follows.
var res = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});

Related

Google Drive API files by created Date

My goal is to delete files from a specific folder older than 60 days.
I have a script as follows (based on code from another post I can't find anymore):
function getOldFileIDs() {
var fileIDs = [];
// Old date is 60 days
var oldDate = new Date().getTime() - 3600*1000*24*60;
var cutOffDate = Utilities.formatDate(new Date(oldDate), "GMT", "yyyy-MM-dd");
// Get folderID using the URL on google drive
var folder = DriveApp.getFolderById('XXXXX');
var files = folder.searchFiles('modifiedDate < "' + cutOffDate + '"');
while (files.hasNext()) {
var file = files.next();
fileIDs.push(file.getId());
Logger.log('ID: ' + file.getId() + ', Name: ' + file.getName());
}
return fileIDs;
};
function deleteFiles() {
var fileIDs = getOldFileIDs();
fileIDs.forEach(function(fileID) {
DriveApp.getFileById(fileID).setTrashed(true);
});
};
This code works but it uses "modifiedDate" When I look in the folder, I see there are many very old files with a "modified" date of today for some reason, but the "Created" date seems correct.
Therefore, I tried changing the code to "createdDate" but that doesn't work.
I decided to actually look at the documentation:
https://developers.google.com/apps-script/reference/drive/drive-app#searchFiles(String)
https://developers.google.com/drive/api/guides/ref-search-terms
It seems like "modifiedDate" isn't even listed yet it seems to work.
Delete old files:
function delOldFiles() {
const dt = new Date();
const dtv = new Date(dt.getFullYear(),dt.getMonth(),dt.getDate() - 60).valueOf();
var folder = DriveApp.getFolderById('XXXXX');
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
if(new Date(file.getDateCreated()).valueOf() < dtv) {
Drive.Files.remove(file.getId());
}
}
}
Issue and workaround:
The parameter of "searchFiles" method uses the search query for Drive API v2. When I tested createdDate > '####-##-##' for "searchFiles" and "Files: list" of Drive API v2, I confirmed errors like Invalid argument: q and Invalid query occurred, respectively. This has already been mentioned in Lorena Gomez's answer
Fortunately, when Drive API v3 is used, createdTime can be used. createdTime of Drive API v3 is the same with createdDate of Drive API v2. In this answer, as a workaround, I would like to propose using Drive API v3 instead of Drive API v2 ("searchFiles" of DriveApp). I have already posted this workaround at this post. But, this has not been posted in Stackoverflow. When this is posted here, I thought that it might be useful for other users.
When Drive API v3 is reflected in your script, how about the following modification?
Modified script:
This script uses Drive API. So, please enable Drive API at Advanced Google services.
function getOldFileIDs() {
var folderId = "###"; // Please set your folder ID.
var fileIDs = [];
// Old date is 60 days
var oldDate = new Date().getTime() - 3600 * 1000 * 24 * 60;
var cutOffDate = Utilities.formatDate(new Date(oldDate), "GMT", "yyyy-MM-dd");
var query = `'${folderId}' in parents and createdTime < '${cutOffDate}' and trashed=false`;
var pageToken = "";
do {
var url = encodeURI(`https://www.googleapis.com/drive/v3/files?q=${query}&pageSize=1000&pageToken=${pageToken}&fields=files(id,name,createdTime),nextPageToken&orderBy=createdTime`);
var res = UrlFetchApp.fetch(url, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } });
var obj = JSON.parse(res.getContentText());
if (obj.files.length > 0) {
fileIDs = [...fileIDs, ...obj.files.map(({ id }) => id)];
obj.files.forEach(({ id, name, createdTime }) => Logger.log(JSON.stringify({ id, name, createdTime })));
}
pageToken = obj.nextPageToken;
} while (pageToken);
return fileIDs;
}
When this script is run, the file list including file IDs is retrieved with the search query of '${folderId}' in parents and createdTime < '${cutOffDate}' and trashed=false.
When you are not required to check the process, please remove obj.files.forEach(({ id, name, createdTime }) => Logger.log(JSON.stringify({ id, name, createdTime })));.
Note:
As additional information, in order to remove the files, when the number of files is large when setTrashed is run in a loop, the process cost becomes high. In this case, how about using batch requests? When batch requests are used, the process cost can be reduced. Ref
When batch requests are used with Google Apps Script, a script is a bit complicated. So, I created a Google Apps Script library. Ref When your script of deleteFiles() is modified for removing the files with the batch requests, how about the following modification?
1. Install Google Apps Script library.
Please install the Google Apps Script library. You can see how to install it here.
2. Sample script.
In this sample, the result is the same with setTrashed. The files of fileIDs are moved to the trash box.
function deleteFiles() {
var fileIDs = getOldFileIDs();
var requests = fileIDs.map(id => ({
method: "PATCH",
endpoint: `https://www.googleapis.com/drive/v3/files/${id}`,
requestBody: { trashed: true },
}));
var result = BatchRequest.EDo({ batchPath: "batch/drive/v3", requests });
console.log(result);
}
If you want to completely delete the files of fileIDs, please use the following script. But, this script completely deletes the files. So, please be careful about this. So, I would like to recommend using the above script. After you check the files in the trash box, you can empty the trash box.
function deleteFiles() {
var fileIDs = getOldFileIDs();
var requests = fileIDs.map(id => ({
method: "DELETE",
endpoint: `https://www.googleapis.com/drive/v3/files/${id}`,
}));
var result = BatchRequest.EDo({ batchPath: "batch/drive/v3", requests });
console.log(result);
}
References:
Files: list of Drive API v3
BatchRequest of Google Apps Script library
Regarding your question:
Therefore, I tried changing the code to "createdDate" but that doesn't work.
This seems to be an issue which has already been reported in this issue tracker, you can keep track of this to see any updates in the future, this is another thread related to the issue. #Cooper's answer is a good workaround to achieve what you're looking for.

Revert Revision of an Excel File - Drive API

I have an Excel file on my Drive (web version). That Excel has many versions. I require the file every so often to revert to a specific version without deleting the most recent version.
I have the following code:
function revertRevisionExcel() {
var revisionId = "######"; // Revision ID to revert.
var fileId = "#####"; // Set the file ID.
var endpoint = "";
var token = ScriptApp.getOAuthToken();
Logger.log(token);
var res = Drive.Revisions.get(fileId, revisionId);
endpoint =
"https://www.googleapis.com/drive/v3/files/" +
fileId +
"/revisions/" +
revisionId +
"?pageToken=" +
token;
var mediaData = UrlFetchApp.fetch(endpoint).getBlob();
Drive.Files.update({}, fileId, mediaData);
}
When I run it shows me the error 401 "Login Required". Could any one please guide me?
Modification points:
About your question of When I run it shows me the error 401 "Login Required". Could any one please guide me?, when I saw your script, I think that the following modification is required.
token and res are not used.
I think that var token = ScriptApp.getOAuthToken() cannot be used for the query parameter of ?pageToken=" + token.
I think that the reason of your issue of Login Required is due to this. In this case, please use the access token retrieved by var token = ScriptApp.getOAuthToken() to the request header.
In order to retrieve the blob from the specific revision, please use alt=media in the query parameter.
When above modification points are reflected to your script, it becomes as follows.
Modified script:
function revertRevisionExcel() {
var revisionId = "######"; // Revision ID to revert.
var fileId = "#####"; // Set the file ID.
var endpoint = "https://www.googleapis.com/drive/v3/files/" + fileId + "/revisions/" + revisionId + "?alt=media";
var token = ScriptApp.getOAuthToken();
var mediaData = UrlFetchApp.fetch(endpoint, {headers: {authorization: "Bearer " + token}}).getBlob();
Drive.Files.update({}, fileId, mediaData);
}
Note:
In this modified script, it supposes that you have already enabled Drive API at Advanced Google services and the values of revisionId and fileId are the valid values. Please be careful this.
References:
Revisions: get
fetch(url, params)

How to get video duration of google drive file via API?

On my website, I am hosting a few videos via Google Drive. On my sidebar, there is a thumbnail of the videos and I'd like to show the duration of the video in the corner. I have looked at two similar questions (here and here) to solve this problem. This is what I produced from looking at these two problems:
function sample1() {
console.log("running script");
var fileId = "theFileID"; // Please set the file ID of the video file.
var fields = "mimeType,name,videoMediaMetadata"; // duration is included in "videoMediaMetadata"
var url = "https://www.googleapis.com/drive/v3/files/" + fileId + "?fields=" + encodeURIComponent(fields) + "&access_token=" + ScriptApp.getOAuthToken();
var res = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
var obj = JSON.parse(res);
console.log("filename: %s, duration: %s seconds", obj.name, obj.videoMediaMetadata.durationMillis / 1000);
}
sample1();
However, when I check the console after running this script, nothing is printed after "running script". Is there a different approach I should be taking in my program when attempting to solve this problem via Google APIs?
Using the Drive API method Files: get with Apps Script I have been able to get the durationMillis from the video.
You have to add the Drive API Advanced Services on the Apps Script project:
function getVideoLength() {
var fileId = "FILE ID";
var returnedFile = Drive.Files.get(fileId);
Logger.log(returnedFile.videoMediaMetadata.durationMillis)
}

Google app script Error could not parse text

I am trying to retrieve data by ID. Use the 3rd method in this link: How to speed ​up the search data in sheet
I run the function and err : Could not parse text.
I do not understand why I have used this method so many times and ran well, but this case is faulty.
This is my code:
function loadDataOfThread() {
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("sheet1");
var ID = "12345";
var formatRange = ws.getRange(1, 1, ws.getLastRow() ,ws.getLastColumn()).setNumberFormat("#STRING#");
var query = "select * where A ='" + ID + "'";
var url = "https://docs.google.com/spreadsheets/d/" + ss.getId() + "/gviz/tq?gid=" + ws.getSheetId() + "&tqx=out:csv&tq=" + encodeURIComponent(query);
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
}
};
var csv = UrlFetchApp.fetch(url, options);
var f = Utilities.parseCsv(csv); // err this line
var dataArr = [];
if (f.length > 0) {
for (var i = 0; i < f.length; i++) {
dataArr.push(f[i][1]);
}
}
}
I think in a spreadsheet whose data type is the date time column and it make err my function but i have convert to string !!! I do not understand why ?
How about this answer? Your issue might be able to be removed with "PasteDataRequest" because "PasteDataRequest" is better than parseCsv() as the parser of CSV data. In this answer, I would like to propose a method for using "PasteDataRequest" of Sheets API. Please think of this as just one of several answers. The flow of this method is as follows.
Insert a sheet as a temporal sheet.
Put the CSV data to the inserted sheet using "PasteDataRequest" of Sheets API.
Retrieve values from the temporal sheet.
Delete the temporal sheet.
Modified script:
When your script is modified, please modify as follows.
Before you use this script, please enable Sheets API at Advanced Google services.
From:
var f = Utilities.parseCsv(csv);
To:
var temp = ss.insertSheet("temp");
var sheetId = temp.getSheetId();
var resource = {requests: [{pasteData: {data: csv.getContentText(), coordinate: {sheetId: sheetId}, delimiter: ","}}]};
Sheets.Spreadsheets.batchUpdate(resource, ss.getId());
var f = temp.getDataRange().getValues();
ss.deleteSheet(temp);
Note:
Of course, I think that the issue can be also removed by modifying csv of var csv = UrlFetchApp.fetch(url, options);. But from your question, I cannot image the values of your issue. So I proposed above method. If you want to use other method, can you provide a sample Spreadsheet for replicating your issue? Of course, please remove your personal information. By this, I would like to think of the issue.
References:
Method: spreadsheets.batchUpdate
PasteDataRequest
If I misunderstood your question and this was not the direction you want, I apologize.

Google apps script to email google spreadsheet excel version [duplicate]

This question already has answers here:
Google app scripts: email a spreadsheet as excel
(2 answers)
Closed 2 years ago.
I would like to write an apps script to email an excel version of my Google Spreadsheet. I know I can save the spreadsheet as an Excel file. I am not sure if I can use the script to email the excel version out as an attachment. How can this be done?
After an answer on another recent post (Thomas van Latum), I tried the suggested doc api and get an interesting result... here is the test code I used and that is working nicely except the file is in xlsx format, not in xls but this is not necessarily an issue these days :
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
function test(){
var id = 'spreadsheet_ID'
var url = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(url+'download/spreadsheets/Export?key='+id+'&exportFormat=xls',
googleOAuth_('docs',url)).getBlob()
DocsList.createFile(doc).rename('newfile.xls')
}
note : if you don't rename it, its default name is Export.xlsx , it might be more usefull to get its ID to use it later...
so the last line could be like this instead :
var xlsfileID = DocsList.createFile(doc).getId()
EDIT : to trigger the authorization process, try a small function like this, run it from the script editor
function autorise(){
// function to call to authorize googleOauth
var id=SpreadsheetApp.getActiveSpreadsheet().getId();
var url = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(url+'download/documents/Export?exportFormat=html&format=html&id='+id,
googleOAuth_('docs',url)).getContentText();
}
As I spent about four hours of playing Rumpelstiltskin because none of the typically very old code snippets for the old Spreadsheet versions and the old OAUTH you can find when googling "google docs script send excel attachment" or similar (i.e. you want to take an existing Spreadsheet, convert it to Excel format and send it as email attachment) actually worked, I finally found the solution.
To create the actual attachment content, neither the supposed res.getContent() nor res.getBlob() nor res.getBytes alone worked. These hints are misleading!
The only thing that works for me is response.getBlob().getContent()!
Whole code :
function sendCurrentDocAsEmail() {
var driveService = getDriveService();
var ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
var sheetName = SpreadsheetApp.getActiveSpreadsheet().getName();
var email = "recipient#demo.com"
var subject = "Here be Subjects";
var body = "Don't even think about learning how to code. It's wasted time.";
var file = Drive.Files.get(ssID );
var url = file.exportLinks[MimeType.MICROSOFT_EXCEL];
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
}
});
var attachments = [{
fileName:sheetName+".xlsx",
content: response.getBlob().getBytes(), // this single line has cost me hours!
mimeType:"application//xls",
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
}
}];
MailApp.sendEmail(email,subject ,body, {attachments:attachments});
}
Where getDriveService() is a function from Google's "OAuth2 for Apps Script" readme on https://github.com/googlesamples/apps-script-oauth2
The latest working version is below. Based on this example, i.e. similar as in previous answer but uses Google Service Account which does not require a human going by link to receive a token. You have to install Oath library from Google, the instructions are pretty clear.
var PRIVATE_KEY = 'xxx'
var CLIENT_EMAIL = 'xxx';
var USER_EMAIL=Session.getActiveUser().getEmail()
function getOathService() {
return OAuth2.createService('GoogleDrive:' + USER_EMAIL)
// Set the endpoint URL.
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the private key and issuer.
.setPrivateKey(PRIVATE_KEY)
.setIssuer(CLIENT_EMAIL)
// Set the name of the user to impersonate. This will only work for
// Google Apps for Work/EDU accounts whose admin has setup domain-wide
// delegation:
// https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
.setSubject(USER_EMAIL)
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scope. This must match one of the scopes configured during the
// setup of domain-wide delegation.
.setScope('https://www.googleapis.com/auth/drive');
}
function sendEmail() {
var oathService = getOathService();
var ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
var file = Drive.Files.get(ssID );
var url = file.exportLinks[MimeType.MICROSOFT_EXCEL];
var file = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + oathService.getAccessToken()
}
});
var attachments = [{
fileName:'xxx.xls',//TODO DATE
content: file.getBlob().getBytes(),
mimeType:"application//xls",
headers: {
Authorization: 'Bearer ' + oathService.getAccessToken()
}
}];
MailApp.sendEmail('email#domain.com', 'xxx', 'Hi,\n\nPlease see the last data in attachment',{attachments:attachments});
}
The one that worked for me:
var AUTH_TOKEN = "Enter your OAuth_Token";
ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
var url = "http://spreadsheets.google.com/feeds/download/spreadsheets/Export?key="+ ssID + "&exportFormat=xls";
//Add &gid=x at the end of above url if you only want a particular sheet
var auth = "AuthSub token=\"" + AUTH_TOKEN + "\"";
var res = UrlFetchApp.fetch(url, {headers: {Authorization: auth}});
var attachments = [{fileName:"<Filename>.xls", content: res.getContent(),mimeType:"application/vnd.ms-excel"}];
MailApp.sendEmail("<recipient's email id>", "<email subject>", "<email body>", {attachments: attachments});
Get OAuth documentation & you token from here https://developers.google.com/accounts/docs/OAuth2
Use the following snippet of code after modifying it to suit your needs
var file = DocsList.getFileById(FILE_ID);
var attachment = file.getAs('application/vnd.ms-excel');
MailApp.sendEmail("abcd#example.com", "Subject", " Body" , {"fileName": "Your_file_name" , "mimeType" : "application/vnd.ms-excel" , "content":attachment.getBytes() } );
Note that this code is not tested, so please feel free to fix an error or two that might pop up