Creating a YouTube Video or Channel ID validation in Apps Script - google-apps-script

I am learning how to use the YouTube Data API through Apps Script. I am now trying to modify the example provided here https://developers.google.com/youtube/v3/quickstart/apps-script to verify if the ID exists.
I slightly modified it to look at video IDs since that is more common.
With help by #Tanaike based on my last question, Tanaike helped me create a loop to allow the script to add IDs as comma-separated. Now I'm trying to add a validation.
function channelsListByVideoid(part,params){
var response = YouTube.Videos.list(part,params);
var video = response.items[0];
var dataRow = [video.id,video.status.uploadStatus, video.snippet.title, response.pageInfo.totalResults, video.snippet.channelId, video.snippet.channelTitle];
SpreadsheetApp.getActiveSheet();
SpreadsheetApp.getActiveSpreadsheet().getSheetByName().appendRow(dataRow);
function getvideo() {
var ui = SpreadsheetApp.getUi();
var videoId = ui.prompt("Enter the YouTube Video ID: ").getResponseText();
var values = videoId.split(",");
for (var i = 0; i < values.length; i++) {
channelsListByVideoid('snippet,status,contentDetails,statistics', {'id': values[i].trim()});
Sometimes, a video ID such as https://www.youtube.com/watch?v=--fPZOu_H8g is not available. Running that ID within the apps-script example will throw an error TypeError: Cannot read property 'id' of undefined which will stop the script from running.
I was thinking about how to use video.list.response Property:
{
"kind": "youtube#videoListResponse",
"etag": etag,
"nextPageToken": string,
"prevPageToken": string,
"pageInfo": {
"totalResults": integer,
"resultsPerPage": integer
},
"items": [
video Resource
]
}
The totalResults and the resultsPerPage will show as 1 if the video is valid and 0 if invalid:
"pageInfo": {
"totalResults": 0,
"resultsPerPage": 0
which can be verified through the API explorer (https://developers.google.com/youtube/v3/docs/videos/list?apix_params=%7B%22part%22%3A%22snippet%2CcontentDetails%2Cstatistics%22%2C%22id%22%3A%22--fPZOu_H8g%22%7D)
I added ````
response.pageInfo.totalResults,
Logically to me, I need to add a
````var response````
to my getvideo() and add the validation check within the loop.
I'm not clear if you can write it as simply as
if response.pageinfo.totalResults = 1 then "valid" ELSE "Invalid"
and then how to allow the code to skip the error that is generated.
Thank you!

Related

Image and Data Merge Sheets to Slides Apps Script - Update

I 'm very new to both Apps Script and coding in general.
I'm trying to take a google slides presentation, and merge with a csv to create multiple images.
I cannot get the merge function to work. I'm pretty sure I am not loading the csv data correctly - I can get it to show the data range in the execution log but it does not merge into the new presentation. I know I have my tags correct, because I've used them in a 3rd party extension and it works.
I know I'm probably just missing something stupid, but I cannot figure it out. Any help would be appreciated!
Below is the code I currently have:
Update 1
I was able to figure out how to call the data correctly, and am now able to replace the text correctly. However, when I use replaceAllShapesWithImage I get the following error:
GoogleJsonResponseException: API call to slides.presentations.batchUpdate failed with error: Invalid requests[2].replaceAllShapesWithImage: There was a problem retrieving the image. The provided image should be publicly accessible, within size limit, and in supported formats.
The images are being called from google drive, and are accessible by anyone with the link. Any ideas? Updated code below:
const spreadsheetId = '1JSXC0XrfUAtcRLXCgVnB-SAQjwk_-YG7W_kGefowONE';
const thetemplateId = '1Pug2cPiGsPL9iKPEnBAhvBVqfSTMyJERCRaSGkyFOr0';
const dataRange = 'Monthly Top Producers!A2:F';
function generateTopPro(){
var Presentation=SlidesApp.openById(thetemplateId);
let values = SpreadsheetApp.openById(spreadsheetId).getRange(dataRange).getValues();
for (let i = 0; i < values.length; ++i) {
const row = values[i];
const agent_name = row[0]; // name in column 1
const agent_phone = row[3]; // phone in column 4
const agent_photo = row[4]; // agent photo url column 5
const logo_state = row[5]; // state logo url column 6
// Duplicate the template presentation using the Drive API.
const copyTitle = agent_name + ' September';
let copyFile = {
title: copyTitle,
parents: [{id: 'root'}]
};
copyFile = Drive.Files.copy(copyFile, templateId);
const presentationCopyId = copyFile.id;
// Create the text merge (replaceAllText) requests for this presentation.
const requests = [{
replaceAllText: {
containsText: {
text: '{{agent_name}}',
matchCase: true
},
replaceText: agent_name
}
}, {
replaceAllText: {
containsText: {
text: '{{agent_phone}}',
matchCase: true
},
replaceText: agent_phone
}
}, {
replaceAllShapesWithImage: {
imageUrl: agent_photo,
imageReplaceMethod: 'CENTER_INSIDE',
containsText: {
text: '{{agent_photo}}',
matchCase: true
}
}
}, {
replaceAllShapesWithImage: {
imageUrl: logo_state,
imageReplaceMethod: 'CENTER_INSIDE',
containsText: {
text: '{{logo_state}}',
matchCase: true
}
}
}];
// Execute the requests for this presentation.
const result = Slides.Presentations.batchUpdate({
requests: requests
}, presentationCopyId);
// Count the total number of replacements made.
let numReplacements = 0;
result.replies.forEach(function(reply) {
numReplacements += reply.replaceAllText.occurrencesChanged;
});
console.log('Created presentation for %s with ID: %s', agent_name, presentationCopyId);
console.log('Replaced %s text instances', numReplacements);
}
}
There was a problem retrieving the image. The provided image should be publicly accessible, within size limit, and in supported formats is a known issue when trying to use an image from Google Drive
See for example Google slides API replaceAllShapesWithImage returns error for Google Drive file (2020)
There are workarounds like Google script replaceAllShapesWithImage with image from drive doesn"t work any more that work for some users/ images but not always.
There are several related issues filed on Google's Issue Tracker and a feature request asking for a full support of the functionality.
If no workaround work for you, the only two things you can do in the current state are:
"Star" the feature request to increase visibility which will hopefully accelerate implementation
Use the thumbnailLink that oyu can retrieve with Files: get as imageUrl - this gives you an image in low quality, but better than nothing

Can I list all the times I saved a file?

My company uses Google Drive and we are still mainly using Microsoft Office documents.
Is it possible to see my activity or each time I saved a document in the Shared Drive even though it is not Google Documents I want to see the activity of?
I found this link https://developers.google.com/apps-script/advanced/drive-activity , but the code only returns Google Docs activity. Not non-Google documents like Word and Excel.
You might want to check if using Revisions.list will fit your needs.
Revisions.list
Lists the current file's revisions.
Path parameters:
fileId - The ID of the file.
Optional query parameters:
fields - The paths of the fields you want included in the response. If not specified, the response includes a default set of fields specific to this method. For development you can use the special value * to return all fields, but you'll achieve greater performance by only selecting the fields you need. For more information, see Return specific fields for a file.
pageSize - The maximum number of revisions to return per page. Acceptable values are 1 to 1000, inclusive. (Default: 200)
pageToken - The token for continuing a previous list request on the next page. This should be set to the value of 'nextPageToken' from the previous response.
Response Body:
{
"kind": "drive#revisionList",
"nextPageToken": string,
"revisions": [
revisions Resource
]
}
Revisions Resource Representation:
You can obtain useful information related to the revision done to the file such as the modified time and last modifying user.
{
"kind": "drive#revision",
"id": string,
"mimeType": string,
"modifiedTime": datetime,
"keepForever": boolean,
"published": boolean,
"publishedLink": string,
"publishAuto": boolean,
"publishedOutsideDomain": boolean,
"lastModifyingUser": {
"kind": "drive#user",
"displayName": string,
"photoLink": string,
"me": boolean,
"permissionId": string,
"emailAddress": string
},
"originalFilename": string,
"md5Checksum": string,
"size": long,
"exportLinks": {
(key): string
}
}
You can specify specific fields in your request under fields parameter so that only necessary information can be shown in the response body:
Sample Fields Parameter:
nextPageToken, revisions/id, revisions/modifiedTime, revisions/lastModifyingUser/displayName, revisions/lastModifyingUser/emailAddress
Sample Response Body:
{
"revisions": [
{
"id": "1898",
"modifiedTime": "2020-12-16T22:29:02.971Z",
"lastModifyingUser": {
"displayName": "User1 Test",
"emailAddress": "user1#example.com"
}
}
]
}
Play with DriveApps file.getLastUpdated(). This is not the same as ALL the times you've updated it but it should get the last time the file was changed. https://developers.google.com/apps-script/reference/drive/file#getLastUpdated()
Or do you really need a list of all the edit times not just the most recent one? In that case you could run a script once a day that records the lastUpdated for all the files you care about or all the files in a folder and record if they've changed. What is the use case?
Thanks for that Ron. Revisions did the trick.
It took me a while to wrap my head around it, but these are the 2 functions I used. I will try to put them together at some point when I have Drive API v3.
function listFilesInFolder() {
// get the id's of files in a folder
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow(["Name", "File-Id"]);
var folder = DriveApp.getFolderById(" ID STRING ");
var contents = folder.getFiles();
var counter = 0;
var file;
while (contents.hasNext()) {
var file = contents.next();
counter++;
data = [
file.getName(),
file.getId(),
];
sheet.getRange("C2").setValue = counter
sheet.appendRow(data);
};
};
function fileactivity() {
var revs = Drive.Revisions.list(" FileID String ");
var savedList = [];
for(var i=0; i<revs.items.length; i++) {
var revision = revs.items[i];
// modifiedByMeTime requires Drive API v3
savedList.push([revision.kind, revision.modifiedDate]);
};
var sheet = SpreadsheetApp.getActiveSheet();
Logger.log(savedList);
}

Using Google Places API in Google Sheets

I'm using this tutorial (https://scrapediary.com/find-local-leads-with-google-places-api-and-sheets/) to scrape data from google places API into a google sheet. I copied the code exactly:
var output = [ ["Name", "Place ID", "Latitude", "Longitude", "Types"]]
var url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?types=food&location=51.4977836,-0.1522502&radius=200&key=AIzaSyBtepY6mCTkHr3m4UCacxSkePkli5yEbCM";
var response = UrlFetchApp.fetch(url)
payload = JSON.parse(response)
for (var x = 0; x < payload['results'].length; x++){
var inner = [ payload['results'][x]['name'], payload['results'][x]['place'],payload['results'][x]['latitude'],payload['results'][x]['longitude'],payload['results'][x]['types']]
output.push(inner)}
}
and I'm trying to run it in google sheets like this:
=placeSearch("Golf Course","51.4977836","-0.1522502","20000","i_put_my_api_key_here")
and it shows "Loading" and then returns nothing. I've double checked that the url itself works by pasting it into the browser and it returns the results in JSON format. I feel like there's a problem with pushing the results to the sheet but I can't find it
There is no doubt that the code you copied is working. Upon testing the same exact code you posted to replicate the problems, I only added return in the function to populate the cell.
See my exact code which worked and returned the data in sheets.
function placesAPI(keyword,latitude,longitude,radius,api_key,depth) {
var output = [ ["Name", "Place ID", "Latitude", "Longitude", "Types"]]
var url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?types=food&location=51.4977836,-0.1522502&radius=200&key=AIzaSyBtepY6mCTkHr3m4UCacxSkePkli5yEbCM";
var response = UrlFetchApp.fetch(url)
payload = JSON.parse(response);
for (var x = 0; x < payload['results'].length; x++){
var inner = [ payload['results'][x]['name'], payload['results'][x]['place'],payload['results'][x]['latitude'],payload['results'][x]['longitude'],payload['results'][x]['types']]
output.push(inner)}
return(output); // added this code to put the value on the cell
}
In the function call, you need to use the api key in the url first to establish a connection. I have confirmed in my testing that if you used other api keys in the first function call, it will not return anything.
=placesAPI("Golf Course","51.4977836","-0.1522502","20000","AIzaSyBtepY6mCTkHr3m4UCacxSkePkli5yEbCM",20)
After that, it should return the same output below. Same with what we see when visiting the url manually.

google-apps-script | TypeError for getEvents()

I have a calendar called 'IMPORTANT!!' which when subbed into my code causes it not to work (there is no error, the function just does nothing). I think this problem is caused by the !! characters at the end of the name, but I do not know what that problem is or how to fix it. What should I do to avoid this problem?
function myFunction() {
var year = 2019;
var month = 0;
var fromDate = new Date(year,month,1,0,0,0);
var toDate = new Date(year,month,28,0,0,0);
var theCalendar = CalendarApp.getCalendarsByName('IMPORTANT!!')[0];
var events = theCalendar.getEvents(fromDate, toDate);
for(var i = 0; i < events.length; i++){
var ev = events[i];
ev.deleteEvent();
}
}
How do I fix the error so my script will delete all of the events from the google calendar posted in 'def' in january of 2019.
getCalendarsByName() returns Calendar[] which is an array. So if the calendar name of def is only one in your calendars, how about following modification?
From:
var theCalendar = CalendarApp.getCalendarsByName('def');
To:
var theCalendar = CalendarApp.getCalendarsByName('def')[0];
Note:
If you have several calendars with same calendar name, I recommend to use the calendar ID.
As a point, when the method name is seen, it is found that "Calendars" of "getCalendarsByName" is plural. By this, it might notice that several values will be returned.
Reference:
getCalendarsByName()
Edit 1:
About your new question, if events has not elements, the for loop doesn't work.
I think that this might be the reason of your new issue. Please confirm about this.
When events can be retrieved by theCalendar.getEvents(fromDate, toDate). eleteEvent() in the for loop will work.
Edit 2:
You can retrieve the calendar IDs and calendar names of all calendars using the following script.
Sample script:
var result = CalendarApp.getAllCalendars().map(function(e) {return {id: e.getId(), name: e.getName()}});
Logger.log(result);
Result:
[
{
"id": "### calendarId1 ###",
"name": "### calendarName1 ###"
},
{
"id": "### calendarId2 ###",
"name": "### calendarName2 ###"
},
,
,
,
]

Is there a setTitle() similar to getTitle() of a page element?

As the title suggests, I'm looking for a way to set the alt title of an image in a slideshow.
Currently this is what i have tried, but for some reason it doesn't seem to update:
var resource = {"requests": [
{"updatePageElementAltText": {
"objectId": id,
"description": "",
"title": elementTitle
}
}]};
Slides.Presentations.batchUpdate(resource, presentationId);
It might be worth noting that the script is running in the Script Editor of a google sheet. The variables id, elementTitle and presentationId are all defined earlier in the script and I've checked that they are correct.
Can anyone spot the issue with this or suggest an easier way to do it?
Edit: Tanaike helped me make this specific part of the script work, but it isn't working in the larger picture, hence this edit.
What the script is supposed to do, is basically do a find/replace on all Image elements in the slideshow.
Based on keys in a sheet in Column A it should replace the Image URL in with the corresponding URL in column B. The script then cycles through all elements in the slideshow, finds the images, and then cycles through them to check if any of the titles have the 'key' as the title. The image URL should then be replaced with the URL on the same row in sheet. This part of the script is tested and works, but the key is removed from the object when the URL is updated. This shouldn't be happening as the Image should be able to be replaced again later.
For this reason, I tried to save the title before updating the URL and the put it back with the above-mentioned batchUpdate, but for some reason, it isn't working properly.
Here is the full script:
function imageReplacer() {
var newPresentationSlides = SlidesApp.openByUrl(myslidesurl).getSlides();
var imageTitles = SpreadsheetApp.openByUrl(mysheeturl).getRange("'Image Replace List'!A2:A").getValues();
var imageURLs = SpreadsheetApp.openByUrl(mysheeturl).getRange("'Image Replace List'!B2:B").getValues();
var presentationId = 'myslidesid';
for (y = 0; y < newPresentationSlides.length; y++) {
var pageElements = newPresentationSlides[y].getPageElements();
for (x = 0; x < pageElements.length; x++) {
for (a = 0; a < imageTitles.filter(String).length; a++) {
if (pageElements[x].getPageElementType() == "IMAGE") {
if(pageElements[x].asImage().getTitle() == imageTitles[a]) {
var elementTitle = pageElements[x].asImage().getTitle();
var id = pageElements[x].getObjectId();
pageElements[x].asImage().replace(imageURLs[a]);
var id = pageElements[x].getObjectId();
var resource = {"requests": [
{"updatePageElementAltText": {
"objectId": id,
"description": "Sample description",
"title": elementTitle
}
}]};
Slides.Presentations.batchUpdate(resource, presentationId);
}
}
}
}
}
}
As you can see the middle part of the script is exactly the same as tanaike suggested, but it's just not working properly (I even tested that specific part as a stand-alone script and it worked fine.).
Second edit:
Examples:
Sheet: https://docs.google.com/spreadsheets/d/1npWyONio_seI3bRibFWxiqzHxLZ-ie2wbszgROkLduE/edit#gid=0
Slides: https://docs.google.com/presentation/d/1rfT7TLD-O7dBbwV5V3UbugN1OLOnBI2-CZN2GPnmANM/edit#slide=id.p
I think that your script works. You can confirm the updated result on the slide.
But if you want to retrieve the title and description using Slides services like getTitle() and getDescription() after the title and description are updated using Slides API, it seems that those results are not updated. The updated results couldn't be retrieved even if saveAndClose() is used. And also, unfortunately, in the current stage, I couldn't find the methods like setTitle() and setDescription() in my environment. So how about this workaround? In this workaround, the title and description are updated by Slides API and those are retrieved by Slides API.
Sample script:
var presentationId = "###"; // Please set this.
var objectId = "###"; // Please set this.
// Update title and description
var resource = {"requests": [
{"updatePageElementAltText": {
"objectId": objectId,
"description": "Sample description",
"title": "Sample title"
}
}]};
Slides.Presentations.batchUpdate(resource, presentationId);
// Retrieve updated title and description
var res = Slides.Presentations.get(presentationId);
var slides = res.slides;
for (var i = 0; i < slides.length; i++) {
var pe = slides[i].pageElements;
for (var j = 0; j < pe.length; j++) {
if (pe[j].objectId == objectId) {
Logger.log(pe[j].title)
Logger.log(pe[j].description)
break;
}
}
}
Note:
If you use this script, please enable Slides API at Advanced Google Services and API console.
References:
presentations.batchUpdate
presentations.get
If I misunderstand what you want, I'm sorry.
Edit:
You want to replace all images in Slides.
At this time, you want to search the title of each image and replace the image from URL using the title.
When the images are replaced, you don't want to change the title (key) of each image.
If my understanding is correct, how about this modification?
Modification points:
It seems that when the image is replaced, the title of image is cleared.
In order to avoid this, when the image is replaced, it also puts the title. For this situation, batchUpdate of Slides API is used.
From the viewpoint of the process cost, at first, it creates the request body and requests the request body. By this, this situation can be achieved by only one API call.
Modified script:
function imageReplacer() {
var spreadsheetId = "### spreadsheetId ###"; // Please modify this.
var sheetName = "Image Replace List";
var presentationId = "### presentationId ###"; // Please modify this.
var sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName);
var values = sheet.getRange(2, 1, sheet.getLastRow(), 2).getValues().filter(function(e) {return e[0] && e[1]});
var s = SlidesApp.openById(presentationId);
var slides = s.getSlides();
var requests = slides.reduce(function(reqs, slide) {
var r = slide.getPageElements().reduce(function(ar, e) {
if (e.getPageElementType() == "IMAGE") {
var key = values.filter(function(v) {return v[0] == e.getTitle()});
if (key.length == 1) {
var id = e.getObjectId();
var rq = [
{"replaceImage":{"imageObjectId":id, "url": key[0][1]}},
{"updatePageElementAltText":{"objectId":id, "title": key[0][0]}}
];
Array.prototype.push.apply(ar, rq);
}
}
return ar;
}, []);
if (r.length > 0) Array.prototype.push.apply(reqs, r);
return reqs;
}, []);
Slides.Presentations.batchUpdate({requests: requests}, presentationId);
}
Note:
I'm not sure about the maximum number of requests for one API call. So if you want to replace a lot of images, if the error due to this occurs, please modify above script.