1

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);
Martijn
  • 13
  • 3

2 Answers2

3
  • 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:

If I misunderstood your question and this was not the direction you want, I apologize.

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • 2
    Thanks for this answer. I have this scope "https://www.googleapis.com/auth/photoslibrary" in my appsscript.json file but I keep getting error in my logs `[20-02-23 18:14:41:084 PST] { "error": { "code": 400, "message": "No permission to add media items to this album.", "status": "INVALID_ARGUMENT" } }` – Cooper Feb 24 '20 at 02:20
  • 2
    You know, what I'd really like to is how did you learn all of this? Are you a really old guy? Or perhaps really smart? – Cooper Feb 24 '20 at 03:11
  • 2
    @Cooper Thank you for replying. I apologize for the inconvenience. About `No permission to add media items to this album.`, in this case, please create the new album by a script. It seems that this is the specification of Google side. I had also got the same issue. So I had prepared the script for resolving the issue. You can see about the script for creating new album by a script at the `Note:` section in my answer. After you got the album ID from the created new album, please test the script again. – Tanaike Feb 24 '20 at 03:24
  • 2
    Yes I want to thank you for that. I just was able to create an album by script and I'm now going see if I can upload some images to it. – Cooper Feb 24 '20 at 03:29
  • 2
    @Cooper And, thank you for your comment. In my case, when I check the Google resources, I often check the official document at Google site and the scripts of official libraries on GitHub. I think that the scripts of official libraries on GitHub give a lot of information for resolving issues. Using their information, I think of the issue and solution. – Tanaike Feb 24 '20 at 03:31
  • 2
    That was it. I have uploaded my first image to the library. Thanks so much for your help. – Cooper Feb 24 '20 at 03:32
  • 2
    @Cooper I'm glad your issue was resolved. In the current stage, unfortunately, it seems that there are no methods for modifying the metadata of the uploaded image files. So as the current workaround, when you want to modify it, please delete the image file and upload it again. – Tanaike Feb 24 '20 at 03:36
  • 2
    I was looking at the output of listing albums and I notice that only the albums that I create with script have the property "isWriteable" so I guess I may be able to use that so that I can know whether an album is available to scripted access. – Cooper Feb 24 '20 at 03:43
  • 2
    It is possible to distinguish the albums created by script with the `if (isWriteable` – Cooper Feb 24 '20 at 04:01
  • @Cooper Thank you for the information. I would like to also include about `isWriteable` in the GAS library. – Tanaike Feb 24 '20 at 04:17
  • 1
    Tanaike 50K rep Congrats!! Long live Tanaike-San. – Aerials Feb 24 '20 at 11:37
  • @Aerials Thank you so much. – Tanaike Feb 24 '20 at 22:43
  • Found it! I directly assigned the response from "UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/uploads", options); " to be the uploadToken. But needed to call **.getContentText()** on it to get it as string... Thank you @Tanaike for providing a working piece of code, because in the logs I saw that your uploadToken was displayed as string, whereas mine got collapsed. This put me on the right track. – Martijn Feb 25 '20 at 00:21
  • @Martijn Thank you for replying and adding the information. – Tanaike Feb 25 '20 at 00:29
0

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();

Martijn
  • 13
  • 3