using youtube API with Google Apps Scripts - google-apps-script

I am not able to test my scripts.
If you can help with the code here - it is not working
As per documentation - https://developers.google.com/youtube/v3/docs/videos/insert
if (serviceYT.hasAccess()) {
url = 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&mine=true&key='+ API_KEY;
var data;
data = '{"snippet":{"title":"testtitle","description":"testdes","categoryId":"19","tags":["asdf","sadfds"]},"status":{"privacyStatus":"public"}}';
var options = {
'headers': {
'Authorization': 'Bearer ' + serviceYT.getAccessToken()
,'Accept': 'application/json'
},
'contentType': 'application/json',
'method' : 'POST',
'payload' : data,
'muteHttpExceptions' : true
};
//execute and handle the response
var response = UrlFetchApp.fetch(url, options);
var responseCode = response.getResponseCode();
var result = JSON.parse(response.getContentText());
Logger.log(result);
}
My questions -
where to put the video?
Resolution of the error I am getting:
{error={message='status', code=400.0, errors=[{reason=unexpectedPart, domain=youtube.part, message='status', location=part, locationType=parameter}]}}
A youtube object (a empty video basically) is successfully added to the youtube which is visible via studio. its a empty video but the title, description, status etc... other things are correcty setup.
Any ideas on how to add the media or video??
Where in the payload the video blob has to be added?
I am not able to get it in the documentation.
https://developers.google.com/youtube/v3/docs/videos/insert

I believe your goal is as follows.
You want to upload a movie file to your YouTube channel with the title, description, and so on.
The movie file is put in your Google Drive.
You want to achieve this by directly requesting to the endpoint using UrlFetchApp.
In this case, how about the following modified script?
Modified script:
Before you use this script, please enable YouTube Data API v3 and add the scope of https://www.googleapis.com/auth/youtube. And please set the file ID of the movie file in your Google Drive to const fileId = "###";.
serviceYT.getAccessToken() is from your script.
function myFunction() {
const fileId = "###"; // Please set the file ID of movie file on the Google Drive.
const metadata = {
"snippet": { "title": "testtitle", "description": "testdes", "categoryId": "19", "tags": ["asdf", "sadfds"] },
"status": { "privacyStatus": "public" }
};
const url = 'https://www.googleapis.com/upload/youtube/v3/videos?part=snippet%2Cstatus';
const file = DriveApp.getFileById(fileId);
const boundary = "xxxxxxxxxx";
let data = "--" + boundary + "\r\n";
data += "Content-Type: application/json; charset=UTF-8\r\n\r\n";
data += JSON.stringify(metadata) + "\r\n";
data += "--" + boundary + "\r\n";
data += "Content-Type: " + file.getMimeType() + "\r\n\r\n";
const payload = Utilities.newBlob(data).getBytes().concat(file.getBlob().getBytes()).concat(Utilities.newBlob("\r\n--" + boundary + "--").getBytes());
const options = {
method: "post",
contentType: "multipart/form-data; boundary=" + boundary,
payload: payload,
headers: { 'Authorization': 'Bearer ' + serviceYT.getAccessToken() },
muteHttpExceptions: true,
};
const res = UrlFetchApp.fetch(url, options).getContentText();
console.log(res);
}
In your script, you are using the endpoint of url = 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&mine=true&key='+ API_KEY;. Your data has the properties of snippet and status. In this case, it is required to use the endpoint of https://www.googleapis.com/upload/youtube/v3/videos?part=snippet%2Cstatus.
In order to upload the movie file, it is required to request with multipart/form-data.
When this script is run, the movie file on Google Drive is uploaded to YouTube.
Note:
In the current stage, even when "privacyStatus": "public" is used, the uploaded video is not public. About this, you can see it at the official document as follows. Ref
All videos uploaded via the videos.insert endpoint from unverified API projects created after 28 July 2020 will be restricted to private viewing mode. To lift this restriction, each API project must undergo an audit to verify compliance with the Terms of Service. Please see the API Revision History for more details.
When you use the YouTube API at Advanced Google services, you can use the following simple script. Ref
function myFunction2() {
const fileId = "###"; // Please set the file ID of movie file on the Google Drive.
const res = YouTube.Videos.insert({
"snippet": { "title": "testtitle", "description": "testdes", "categoryId": "19", "tags": ["asdf", "sadfds"] },
"status": { "privacyStatus": "public" }
}, ["snippet", "status"], DriveApp.getFileById(fileId).getBlob());
console.log(res);
}
References:
Videos: insert
Multipart-POST Request Using Google Apps Script
fetch(url, params)

Related

Using Google Apps Script to Add Custom Playlist Cover Image for Spotify Playlist

I am trying to use the "Add Custom Playlist Cover Image" endpoint in the Spotify API. I have seen multiple questions on this for different languages but never a Google Apps Script example. I am quite sure that access and scope is in order, but I still get this reply back:
{
"error" : {
"status" : 401,
"message" : "Unauthorized."
}
}
Below you can see my code. I would appreciate a hint on what is wrong, or - even better - a snip of code from a working example.
imageid = '1B6I3kol0UeEWLtpNUyKmj3e6B9DEQ2hy'
var imageBytes = DriveApp.getFileById(imageid).getBlob().getBytes();
var imageEncoded = Utilities.base64Encode(imageBytes);
url = 'https://api.spotify.com/v1/playlists/' + id +'/images'
var options = {
muteHttpExceptions: true,
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + service.getAccessToken(),
'Content-Type': 'image/jpeg'
},
payload: imageEncoded
};
var response = UrlFetchApp.fetch(url, options);

How to filter Google Drive API v3 mimeType?

I wrote a script that uses DriveApp to read Google Drive, SpreadsheetApp to log data in Sheets, and Google Drive API v3 + service account + OAuth to make changes on behalf of G Suite users.
It would be nice to search Google Drive from the target user perspective (calling Google Drive API v3) instead of the account running the script (calling DriveApp). I can't get the filter to work.
The query is built with parent folder keys, mimeType = or mimeType != for folders, and is passed into the function. The format is:
var query = "('GoogleDriveFolderKey01' in parents or 'GoogleDriveFolderKey02' in parents) and trashed = false and mimeType = 'application/vnd.google-apps.folder'"
The DriveApp function uses:
files = Drive.Files.list({
q: query,
maxResults: 100,
pageToken: pageToken
});
The Google Drive API v3 function uses:
var url = 'https://www.googleapis.com/drive/v3/files/'
var options = {
'contentType': 'application/json',
'method' : 'get',
'headers' : { Authorization: 'Bearer ' + service.getAccessToken() },
'muteHttpExceptions': true,
'corpora' : 'domain',
'q' : query,
'spaces' : 'drive',
'pageSize' : 100,
'pageToken' : pageToken
};
var response = UrlFetchApp.fetch(url, options);
var resultParsed = JSON.parse(response.getContentText());
files = resultParsed.files
pageToken = resultParsed.pageToken
The results with DriveApp are as expected, but Google Drive API v3 results in:
"files": [
{
"kind": "drive#file",
"id": "01abc123_etc",
"name": "Something something (2021-04-15).pdf",
"mimeType": "application/pdf"
},
{
"kind": "drive#file",
"id": "02ABC4321-qwertyuiop",
"name": "Super Special Worksheet",
"mimeType": "application/vnd.google-apps.spreadsheet"
},
{
"kind": "drive#file",
"id": "whatever",
"name": "Copy of V1",
"mimeType": "application/vnd.google-apps.folder"
},
...
Any ideas?
Edit:
Thank you! The problem seems to be 'corpus' and 'space'. I have used to following in Google AppsScript:
var options = {
'contentType': 'application/json',
'method' : 'get',
'headers' : { Authorization: 'Bearer ' + service.getAccessToken() },
'muteHttpExceptions': true,
'pageSize' : 100
};
url += '?q=' + encodeURIComponent(query);
if ( pageToken.length > 0 ) url += '&pageToken=' + pageToken;
var response = UrlFetchApp.fetch(url, options);
How about this answer?
Modification points:
At the method of "Files: list" in Drive API v3, the values of corpora, q, space, pageSize, pageToken are used as the query parameters. I think that the reason of your issue is due to this.
At GET method, contentType is not required.
Modified script:
When your script is modified, it becomes as follows.
var query = "('GoogleDriveFolderKey01' in parents or 'GoogleDriveFolderKey02' in parents) and trashed = false and mimeType = 'application/vnd.google-apps.folder'"
var url = 'https://www.googleapis.com/drive/v3/files' // Modified
var options = { // Modified
'method' : 'get',
'headers' : { Authorization: 'Bearer ' + service.getAccessToken() },
'muteHttpExceptions': true,
};
url += `?corpora=domain&q=${encodeURIComponent(query)}&spaces=drive&pageSize=100&pageToken=${pageToken}`; // Added
var response = UrlFetchApp.fetch(url, options);
var resultParsed = JSON.parse(response.getContentText());
files = resultParsed.files
pageToken = resultParsed.pageToken
Note:
This modified script supposes that service.getAccessToken() can be used for using the method of "Files: list" in Drive API v3.
If an error occurs please remove corpora=domain from the URL like below and test it again.
url += `?q=${encodeURIComponent(query)}&spaces=drive&pageSize=100&pageToken=${pageToken}`;
Reference:
Files: list

Google photos api adding photos not working, upload seems to work

Trying to use Google Apps Script and Google Photos API to add photos to Google Photos. Upload seems to work / returns a token, but then adding the photo to the library fails. The process consists of two steps: 1. Upload the photo data as described here, then 2. Add the photo to photo library as described here.
Step 1. works for me, as I get an upload token, but step 2 with source code below, throws an error, but my call has the one media item it needs.
{
"error": {
"code": 400,
"message": "Request must have at least one newMediaItem.",
"status": "INVALID_ARGUMENT"
}
}
My code after the upload step below. I have tried to stringify request body and have passed it to payload instead of body, but nothing worked. As the error seems specific enough, I've the feeling I'm just overlooking a tiny thing, but what??? Who has a working piece of code, preferably in apps script that I can have a look at?
requestHeader = {
"authorization": "Bearer " + photos.getAccessToken(),
"Content-Type": "application/json"
}
var requestBody = {
"newMediaItems": [
{
"description": "Photo description",
"simpleMediaItem": {
"fileName": fileName,
"uploadToken": uploadToken
}
}
]
}
var options = {
"muteHttpExceptions": true,
"method" : "post",
"headers": requestHeader,
"body" : requestBody
};
var response = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate", options);
Logger.log("raw: " + response);
You want to add an image file to the album using Photo API with Google Apps Script.
You have already enabled Google Photo API at API console. And yout access token can be used for using the method of mediaItems.batchCreate.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Usage:
1. Linking Cloud Platform Project to Google Apps Script Project:
About this, you can see the detail flow at here.
2. Add scope:
In this case, please addt the scope of https://www.googleapis.com/auth/photoslibrary to the manifest file (appsscript.json).
Although I think that from your question, above step 1 and 2 have already been done, I added them because I thought that this might be useful for other users.
3. Sample script:
In your script, I cannot see the detail of uploadToken. But in your question, I could confirm that you have alredy retrieved the value of uploadToken. So when you want to use your script for retrieving uploadToken, please replace uploadToken to yours. As the modification point of your script, 1. Include the album ID. 2. There is no body property of UrlFetchApp. 3. Please use JSON.stringify() to the payload.
function getUplaodToken_(imagefileId) {
var headers = {
"Authorization": "Bearer " + ScriptApp.getOAuthToken(),
"X-Goog-Upload-File-Name": "sampleFilename",
"X-Goog-Upload-Protocol": "raw",
};
var options = {
method: "post",
headers: headers,
contentType: "application/octet-stream",
payload: DriveApp.getFileById(imagefileId).getBlob()
};
var res = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/uploads", options);
return res.getContentText()
}
// Please run this.
function myFunction() {
var imagefileId = "###"; // Please set the file ID of the image file.
var albumId = "###"; // Please set the album ID.
var uploadToken = getUplaodToken_(imagefileId);
var requestHeader = {Authorization: "Bearer " + ScriptApp.getOAuthToken()};
var requestBody = {
"albumId": albumId,
"newMediaItems": [{
"description": "Photo description",
"simpleMediaItem": {
"fileName": "sampleName",
"uploadToken": uploadToken
}}]
};
var options = {
"muteHttpExceptions": true,
"method" : "post",
"headers": requestHeader,
"contentType": "application/json",
"payload" : JSON.stringify(requestBody)
};
var response = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate", options);
Logger.log(response);
}
In this script, it supposes that the image file is put in Google Drive.
Note:
If the error of No permission to add media items to this album. occurs, please create the album by the script. The official document says as follows.
Media items can be created only within the albums created by your app.
In this case, please create new album by the following script, and please retrieve the album ID.
function createNewAlbum() {
var options = {
headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()},
payload: JSON.stringify({album: {title: "sample title"}}),
contentType: "application/json",
method: "post"
};
var res = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/albums", options);
Logger.log(res);
}
References:
Class UrlFetchApp
Upload media
Creating a media item
Method: mediaItems.batchCreate
If I misunderstood your question and this was not the direction you want, I apologize.
Found it! Not shown in the code I submitted, but still adding the fix, as it might help others making the same mistake I did. I directly assigned the response from UrlFetchApp to be the upload token, like so:
uploadToken = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/uploads", options);
but needed to call .getContentText() on it to get it as string, like so:
uploadToken = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/uploads", options).getContentText();

Stackdriver Logging API returns response code 200, but response is empty

I'm trying to fetch stackdriver logs via Stackdriver Logging API v2. I do this by making a POST request from google apps script project, in particular using UrlFetchApp. The thing is, it runs successfully, but the response shown in log is empty. However, when I made the same request using apirequest.io, curl and Google API explorer, I got the necessary response.
I searched extensively, but to no avail. Tried experimenting with header, url, but nothing.
function exportLogs () {
var options = {
"method" : "post",
"headers": {Authorization: 'Bearer ' + ScriptApp.getOAuthToken()},
"resourceNames": [
"projects/MyProject"
],
"pageSize": 1,
}
var response = UrlFetchApp.fetch('https://logging.googleapis.com/v2/entries:list?key=MyApiKey', options)
Logger.log(response)
}
What I want to get is some logs, but I'm only getting {}
Issue:
Unacceptable keys are used in options object.
Solution:
payload is the only acceptable parameter for including request body.
Code:
function exportLogs() {
var options = {
method: "post",
headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() }, //Include https://www.googleapis.com/auth/cloud-platform in scopes
payload: JSON.stringify({
resourceNames: ['projects/[PROJECT_ID]'],
pageSize: 1,
}),
};
var response = UrlFetchApp.fetch(
'https://logging.googleapis.com/v2/entries:list?key=MyApiKey',
options
);
Logger.log(response);
}
To Read:
Urlfetch#params
Logging api#entriesList
Setting scopes
For some strange reason, adding an orderBy sort property in the request body object is the only way I could get results to be retrieved.
Also, a filter should be added to get only Apps Script logs.
load.filter = 'resource.type="app_script_function"';//Only from Apps Script
See example code at GitHub:
Apps Script Download Stackdriver Logs
Code:
function exportStackdriverLogs() {
var id,load,options,param,response;
id = 'Enter your Cloud Project ID here';//See your console at:
//https://console.cloud.google.com/iam-admin/settings
param = "projects/" + id;
load = {};
load.resourceNames = [param];
load.orderBy = "timestamp desc";
load.filter = 'resource.type="app_script_function"';//Only get logs that
//came from Apps Script
load.pageSize = 1;//You will probably want more than 1 but this is for an example
options = {};
options.method = "post";
options.headers = { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() };
options.payload = JSON.stringify(load);
options.muteHttpExceptions = true;
options.contentType = "application/json";
response = UrlFetchApp.fetch('https://logging.googleapis.com/v2/entries:list',options);
//Logger.log('response: ' + response.getResponseCode())
//Logger.log('content: ' + response.getContentText())
}

Google Apps Script: UrlFetchApp Post File

I'm trying to POST a file to a REST API via Google Apps Script. The idea is that I have a process that is creating copies of a Google Doc, and I want to be able to post those newly created Docs to a third party system.
I found in UrlFetchApp that I can send files. However, I'm having issues sending the correct header values.
My request looks like so:
var file = DriveApp.getFileById(fileId);
var body = {
"file": file.getAs(MimeType.PDF)
};
var headers = {
'Content-Disposition': 'attachment; filename="'+ file.getName() +'"',
'Content-Length': file.getSize()
};
My options when I call UrlFetchApp.fetch(url, options) looks like :
({
method:"POST",
headers:{
'Content-Disposition':"attachment; filename=\"My Merge Development_row_1.pdf\"",
'Content-Length':90665,
Authorization:"Bearer TOKEN"
},
contentType:"application/x-www-form-urlencoded",
muteHttpExceptions:true,
payload:{file:Blob}
})
The API that I'm sending the files to requires the 'Content-Length' header. But, when I try to set a value for 'Content-Length' header I get an Apps Script error, "Attribute provided with invalid value: Header:Content-Length". If I don't set the Content-Length header then the API responds that the Content-Length and file size don't match.
Any ideas on how I set the Content-Length header so I can POST the file?
There is an existing ticket highlighting that the documentation is not clear on this very issue
The solution is:
Move content length value from "content-Length" name/value pair in
headers to the advanced argument "contentLength"
So in your example your options should look like
({
method:"POST",
headers:{
'Content-Disposition':"attachment; filename=\"My Merge Development_row_1.pdf\"",
Authorization:"Bearer TOKEN"
},
contentLength: 90665,
contentType:"application/x-www-form-urlencoded",
muteHttpExceptions:true,
payload:{file:Blob}
})
EDIT: Added a full example function to get contentLength and blob shown below:
function testFilePost() {
var file = DriveApp.getFileById(doc_id).getAs(MimeType.PDF);
var headers = {
'Content-Disposition': 'attachment; filename="'+ file.getName() +'"',
};
var options =
{
"method" : "post",
"payload": file.getBytes(),
"headers": headers,
"contentLength": file.getBytes().length,
};
var result = JSON.parse(UrlFetchApp.fetch('http://httpbin.org/post', options).getContentText());
Logger.log(result);
}