1

I am trying to follow documentation but never get the 0Auth2 to connect. Btw I'm running the script manually from the google sheets scripting page, where should I get prompting for allowing access? (I don't understand all this 0Auth2 scheme and I have already gave authorization to run the script and got client id and secret)... See below my log and script routines (the first get to photo is still minimalistic as I didn't yet get through the 0Auth2 step ;-) . Thanks in advance for any hint. I thought it would be trivial as it is my own sheet and google photo account...

Log:

[19-01-06 17:50:05:809 CET] starting
[19-01-06 17:50:05:810 CET] getPhotoService
[19-01-06 17:50:05:849 CET] false
[19-01-06 17:50:05:850 CET] redirectURI=https://script.google.com/macros/d/[REMOVED]/usercallback
[19-01-06 17:50:05:864 CET] Open the following URL and re-run the script: https://accounts.google.com/o/oauth2/auth?client_id=[removed].apps.googleusercontent.com&response_type=code&redirect_uri=https%3A%2F%2Fscript.google.com%2Fmacros%2Fd%2F[removed]%2Fusercallback&state=[removed]&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fphotoslibrary.readonly&login_hint=[removed]&access_type=offline&approval_prompt=force

Script:

function getPhotoService() {
  // Create a new service with the given name. The name will be used when
  // persisting the authorized token, so ensure it is unique within the
  // scope of the property store.
  Logger.log('getPhotoService');
  return OAuth2.createService('gPHOTOfj')

  // enable caching on the service, so as to not exhaust script's PropertiesService quotas
  .setPropertyStore(PropertiesService.getUserProperties())
  .setCache(CacheService.getUserCache())

  // Set the endpoint URLs, which are the same for all Google services.
  .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
  .setTokenUrl('https://accounts.google.com/o/oauth2/token')

  // Set the client ID and secret, from the Google Developers Console.
  .setClientId(CLIENT_ID)
  .setClientSecret(CLIENT_SECRET)

  // Set the name of the callback function in the script referenced
  // above that should be invoked to complete the OAuth flow.
  .setCallbackFunction('authCallback')
  //.setCallbackFunction('https://script.google.com/macros/d/'+SCRIPT_ID+'/authCallback')

  // Set the property store where authorized tokens should be persisted.
  .setPropertyStore(PropertiesService.getUserProperties())

  // Set the scopes to request (space-separated for Google services).
  .setScope('https://www.googleapis.com/auth/photoslibrary.readonly')

  // Below are Google-specific OAuth2 parameters.

  // Sets the login hint, which will prevent the account chooser screen
  // from being shown to users logged in with multiple accounts.
  .setParam('login_hint', Session.getActiveUser().getEmail())

  // Requests offline access.
  .setParam('access_type', 'offline')

  // Forces the approval prompt every time. This is useful for testing,
  // but not desirable in a production application.
  .setParam('approval_prompt', 'force');
}

function authCallback(request) {
  Logger.log('Called back!');
  var photoService = getPhotoService();
  var isAuthorized = photoService.handleCallback(request);
  if (isAuthorized) {
    Logger.log('Authorisation Success!');
  } else {
    Logger.log('Authorisation Denied...!');
  }
}

// Modified from http://ctrlq.org/code/20068-blogger-api-with-google-apps-script
function photoAPI() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length); 
  albums_sh.clear(); var nrow = new Array(); var narray = new Array(); 
  Logger.log("starting");  

  var service = getPhotoService();
  Logger.log(service.hasAccess());
  Logger.log('redirectURI='+service.getRedirectUri());

  if (service.hasAccess()) {

    var api = "https://photoslibrary.googleapis.com/v1/albums";

    var headers = {
      "Authorization": "Bearer " + service.getAccessToken()
    };

    var options = {
      "headers": headers,
      "method" : "GET",
      "muteHttpExceptions": true
    };

    var response = UrlFetchApp.fetch(api, options);

    var json = JSON.parse(response.getContentText());

    for (var i in json.items) {
      nrow = []; nrow.push(json.items[i].id);  nrow.push(json.items[i].name); nrow.push(json.items[i].url); narray.push(nrow);
    }
    albums_sh.getRange(1,1,narray.length,narray[0].length).setValues(narray);


  } else {
    var authorizationUrl = service.getAuthorizationUrl();
    Logger.log("Open the following URL and re-run the script: " + authorizationUrl);
  }
}
Cooper
  • 59,616
  • 6
  • 23
  • 54
Frç Ju
  • 31
  • 2

1 Answers1

2

So it works, if others want to use it. But it is quite slow (I have 500 albums...):

function photoAPI() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length); 
  albums_sh.clear();
  var narray = []; 

  var api = "https://photoslibrary.googleapis.com/v1/albums";
  var headers = { "Authorization": "Bearer " +  ScriptApp.getOAuthToken() };
  var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };

  var param= "", nexttoken;
  do {
    if (nexttoken)
      param = "?pageToken=" + nexttoken; 
    var response = UrlFetchApp.fetch(api + param, options);
    var json = JSON.parse(response.getContentText());
    json.albums.forEach(function (album) {
      var data = [
        album.title,
        album.mediaItemsCount,
        album.productUrl
      ];
      narray.push(data);
    });
    nexttoken = json.nextPageToken;
  } while (nexttoken);
  albums_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
tehhowch
  • 9,645
  • 4
  • 24
  • 42
Frç Ju
  • 31
  • 2
  • 1
    In your case, how about increasing ``pageSize``? The default is 20. This means that 20 values are returned by one API call. You can set it to 50 which is the maximum like ``https://photoslibrary.googleapis.com/v1/albums?pageSize=50``. I thought that by this, it might become a bit faster. You can see the detail information at [here](https://developers.google.com/photos/library/reference/rest/v1/albums/list). – Tanaike Jan 08 '19 at 23:03
  • 1
    thanks I will try to see indeed if the bottleneck is in the UrlFetch – Frç Ju Jan 09 '19 at 17:19
  • Without required Authorization Scopes (`https://www.googleapis.com/auth/photoslibrary.readonly`) the above code will not work. To add scopes you need to use appsscript.json(manifest file). Check here: https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes – Ivan Gusev May 30 '19 at 08:37
  • I am the owner of both the sheets and the albums and it works fine without manifest (just asked me interactively to allow the script once if I remember) – Frç Ju May 31 '19 at 11:16
  • Thanks for this! I was able to take your code and make a similar function to actually upload photos, by implementing the 2 API calls documented [here](https://developers.google.com/photos/library/guides/upload-media) using urlFetch. My urlFetch was a bit different as it had to use POST and in one of the 2 cases had to use a stringified JSON, but it wasn't too hard to figure out once I had yours as a starting point. I DID have to manually edit the oauthscopes in the manifest file, which was annoying as it removed all the scopes which had been obtained interactively. – aberson Aug 10 '19 at 04:28