Thursday, February 5, 2015

Tutorial: Accessing Picasa Web Albums through Apps Script


Editor's Note: Romain Vialard is a Google Apps Script Top Contributor. He has contributed interesting articles and blog posts about Apps Script. - Anton Soradoi

March 2012

Goal

This tutorial teaches you how to use the Picasa Web Albums API with Apps Script to request data from the Picasa service and parse the returned XML to display images from your public albums through a UI.

Time to Complete

Approximately 30 to 60 minutes, depending on your level of familiarity with JavaScript and XML.

Prerequisites

  1. Familiarity with the UrlFetch Services
  2. Familiarity with the Ui Services
  3. Make sure you have at least one public album in Picasa.

Contents

This tutorial is divided into the following sections:
  1. Running a Simple Example
  2. The Script Explained
  3. Going Beyond this Tutorial

Running a Simple Example

  1. Open a new spreadsheet in Google Docs.
  2. Choose Tools > Script Editor.
  3. Copy and paste the following script:
    // Define global variables needed to access Picasa Web Albums Data API
    var NAME = 'lh2';
    var SCOPE = 'http://picasaweb.google.com/data/';
    var URL = "https://picasaweb.google.com/data/feed/api/user/default";
    /*
    - Fetch the Picasa albums of the current user
    - Find the public albums among them
    - Add their titles to a listbox
    - Put this listbox in a UI and show it in the spreadsheet
    */
    function getPicasaAlbums(){
     var data = UrlFetchApp.fetch(URL, googleOAuth_()).getContentText();
     var xmlOutput = Xml.parse(data, false);
     var albums = xmlOutput.getElement().getElements('entry');
     var info = [];
     var app = UiApp.createApplication().setTitle('Picasa');
     var panel = app.createVerticalPanel();
     var listBox = app.createListBox().setName('albumListBox').addItem('select');
     var handler = app.createServerHandler('showImagesEventHandler').addCallbackElement(listBox);
     listBox.addChangeHandler(handler);
     for(var i = 0; i < albums.length; i++){
       if(albums[i].getElement('rights').getText() == "public"){
         var title = albums[i].getElement('title').getText();
         var id = albums[i].getElement('http://schemas.google.com/photos/2007', 'id').getText();
         info.push([title,id]);
         listBox.addItem(title);
       }
     }
     // use a script property to save the links to each album for later use
     ScriptProperties.setProperty('info', Utilities.jsonStringify(info));
     var scrollPanel = app.createScrollPanel().setWidth('500').setHeight('300');
     scrollPanel.setAlwaysShowScrollBars(true);
     scrollPanel.add(app.createVerticalPanel().setId('panelForImages'));
     app.add(panel.add(listBox).add(scrollPanel));
     SpreadsheetApp.getActiveSpreadsheet().show(app);
    }
    /*
    - Clear the UI
    - Retrieve the album selected by the user from the listBox
    - Get the ID of this album from the 'info' script property
    - Get the photos of this album from Picasa
    - Show those photos in the UI
    */
    function showImagesEventHandler(e){
     var app = UiApp.getActiveApplication();
     var panel = app.getElementById('panelForImages').clear();
     var info = Utilities.jsonParse(ScriptProperties.getProperty('info'));
     for(i in info){
       if(info[i][0] == e.parameter.albumListBox){
         var data = UrlFetchApp.fetch(URL+'/albumid/'+info[i][1], googleOAuth_()).getContentText();
         var xmlOutput = Xml.parse(data, false);
         var photos = xmlOutput.getElement().getElements('entry');
         for(j in photos){
           panel.add(app.createImage(photos[j].getElement('content').getAttribute('src').getValue()));
         }
       }
     }
     return app;
    }
    /*
    Authenticate the user when accessing data from Google Services through UrlFetch
    There are three URIs required to authenticate an application and obtain an access token,
    one for each step of the OAuth process:
    - Obtain a request token
    - Authorize the request token
    - Upgrade to an access token
    */
    function googleOAuth_() {
     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'};
    }
  4. Save the script.
  5. Select the function getAlbums in the function combo box and click Run. A pop-up box opens, asking for authorization to access the Spreadsheet and Script Properties Services.
  6. Click Authorize.
  7. Once again click Run. A new pop-up box asking for authorization opens.
  8. Click Authorize.
  9. A new tab opens in your browser asking for authorization to access Picasa. Click Grant access.
  10. Go back to your spreadsheet. You will see a listbox filled with the names of your public albums.
  11. Select an album. The photos from that album are displayed as shown below.

The Script Explained

Using OAuth to Authenticate

To authenticate when you connect to a Google Data API through UrlFetch, you must call googleOAuth_() as follows:
function googleOAuth_() {
 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'};
}

Parsing the XML Output

The protocol guide shows the structure of the returned feed of album entries. You can parse the feed using XML Services.
Here is an example of an <entry> inside the <feed>.
<feed>
  <entry>
    <title>Lolcats</title>
      <rights>public</rights>
    <gphoto:id>albumID</gphoto:id>
  </entry>
</feed>
The XML Service provides a command to parse all the entries in a feed into an array:
var albums = xmlOutput.getElement().getElements('entry');
You can iterate over the albums array and check the access rights for each album with the getText() method.
albums[i].getElement('rights').getText()
To extract the album ID you have to call getElement(namespaceUrl, elementName) instead of getElement(elementName), because the ID is linked to a specific name space.
//<gphoto:id>albumID</gphoto:id>
var albums = xmlOutput.getElement().getElements('entry');
<?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom'
    xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/'
    xmlns:exif='http://schemas.google.com/photos/exif/2007'
    xmlns:gphoto='http://schemas.google.com/photos/2007'

Storing Information as JSON

The function getPicasaAlbums makes a call to the Picasa API to retrieve all the user albums. The function showImagesEventHandlermakes another call to the Picasa API to retrieve all the photographs of a specific album. The protocol guide indicates that the HTTP request to get photos from a specific album requires the album id. Thus in the function getPicasaAlbums a Script Property is created to store all albums names and id, to be reused in the function showImagesEventHandler.
The function getPicasaAlbums creates a two-dimensional array to store album names and ids.
var info = [];
 for(var i = 0; i < albums.length; i++){
   if(albums[i].getElement('rights').getText() == "public"){
     var title = albums[i].getElement('title').getText();
     var id = albums[i].getElement('http://schemas.google.com/photos/2007', 'id').getText();
     info.push([title,id]);
   }
 }
Script Properties must be strings, not arrays. Thus the jsonStringify and jsonParse methods from Utilities Services are used to encode and decode the album information into and out of the 'info' Script Property.
In the function getPicasaAlbums the array ‘info’ is stored as a JSON string:
ScriptProperties.setProperty('info', Utilities.jsonStringify(info));
In the function showImagesEventHandler the JSON string is decoded to retrieve the array:
var info = Utilities.jsonParse(ScriptProperties.getProperty('info'));

Going Beyond this Tutorial

This tutorial example can be adapted for connecting to many other Google Data APIs.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.