Over the last few weeks I’ve been struggling to get to grips with how I can deal with images in my current blog workflow. My most recent post on image-handling was about beginning this process. After much struggling with the existing documentation, I finally managed to set up a process for accessing and working with Picasa API.

Over the last few weeks I’ve been struggling to get to grips with how I can deal with images in my current blog workflow. My most recent post on image-handling was about beginning this process. After much struggling with the existing documentation, I finally managed to set up a process for accessing and working with Picasa API.

I outline the steps here for the benefit of anyone trying to do something similar.

Setting up your application in the console

Please if you’re planning on using the Picasa API yourself, ignore anything on ClientLogins, AuthTokens and so on. Use OAuth 2.0 and save yourself a lot of time and energy.

  1. First, go to the Google Developer’s Console and set up a project. To begin with, all that’s needed is a name and ID for the project, both of which you can make up as needed.

  2. Select the newly created project to reveal more options. The option needed in this case is “APIs & auth”, under which you should find an option called “Consent Screen”.

  3. Click “APIs & auth” and then “Consent Screen” in the sub-menu. This is where you’ll find the options to present to the user of your app or workflow when asking for permission to access their account. You can simply put the name of your app here but you also have the option to provide a link to your app’s homepage, to terms and conditions, even to a privacy policy. You can also upload a logo. These are all optional. Save your settings once done.

  4. Go to the “Credentials” option, just above “Consent Screen” in the “APIs & auth” menu. You will see options on this screen for OAuth and public API keys. Public API keys are for accessing data that users have deemed public anyway or for public information like that Google maps. Here the concern is for private or secure data for which authentication is required – and that’s where OAuth comes in.

  5. Click the “Create new Client ID” button. A little pop-up should appear with some options in it. The other options may be worth exploring but the only one I could get to work was “Installed Application”. This may due to incomplete documentation on Google’s part, or to my own lack of experience with this. But, for now, to follow what I did…

  6. Select “Installed Application” and “Other” from the “Installed application type” list.

  7. Click “Create Client ID”. When the pop-up disappears, you should see a set of client properties ready to use in your application or workflow. You can download this as JSON or simply copy the properties from this page to use in your application.

Using credentials in your code

At my end, I created two files _client_id.txt and _client_secret.txt. I stored my newly created Client ID and Client Secret in these files respectively.

Because of the difficulty I had in getting my application to work in any other way, I treated the application request as if it was coming from a device with limited input.

There are three steps to this process:

  1. Use the client ID to request three pieces of information:
- a user code, - a device code, and - a verification URL.

The request returns the information in JSON.

  1. Present the user with the user code and direct them to the verification URL where they must enter the code. Once they’ve done this, they will be presented with the consent screen you set up earlier on.

  2. Poll the Google endpoint to see if the user has allowed your application the permissions needed. The return from this poll will tell you if the user hasn’t done anything yet, or if they have rejected or accepted. If they accept you will be given two tokens: an access token and a request token. You will need the access token to make requests to the API but this expires periodically so you’ll need the refresh token to request a new access token when that happens.

Once you’ve got these three steps in place, you can start working with Picasa directly from your application.

Here’s how you get each of the steps in place, using simple Bash script.

Step 1. Request user code

Assuming you’ve put your Client ID in a file called _client_id.txt, the following code should work.

# Request `user_code` and `verification_url`.

CLNTID=$(<_client_id.txt)
CSCOPE="https%3A%2F%2Fpicasaweb.google.com%2Fdata%2F"
VERREQ=$(curl -s -d "client_id=$CLNTID&scope=$CSCOPE" https://accounts.google.com/o/oauth2/device/code)

The first line of code in this snippet simply takes all the contents of _client_id.txt and assigns it a variable CLNTID. You need to make sure there’s nothing in that text file but the ID!

The second line of the snippet is to represent the scope we’re requesting of the user’s account, with the variable CSCOPE. The documentation on OAuth 2.0 authentication is a bit misleading here. They say you need https://picasaweb.google.com. I found that was accepted however and it’s only when I encoded the URI that I started to get results.

The third line executes a cURL command using the client ID and the scope and sending the request for Google’s device OAuth 2.0 URI. The -s argument simply silences the output of this. It assigns the return from the request to the variable VERREQ.

The contents of VERREQ is JSON so this needs parsing if we’re to use it. It looks like this (the codes given are just examples):

{
  "device_code" : "1/a2bc3dEFGhi45JklM6nopRStu_v7WXY8z9A0B_Cd-eF",
  "user_code" : "USER-CODE",
  "verification_url" : "https://www.google.com/device",
  "expires_in" : 1800,
  "interval" : 5
}

It’s probably not the best way to parse it, but as an interim measure I used the following code:

re='\"verification_url\" \: \"(.*)\"\,'
if [[ $VERREQ =~ $re ]]; then
	VERURL=${BASH_REMATCH[1]}
fi

re='\"user_code\" \: \"(.*)\"\,[[:space:]]+\"v'
if [[ $VERREQ =~ $re ]]; then
	USERID=${BASH_REMATCH[1]}
fi

re='\"device_code\" \: \"(.*)\"\,[[:space:]]+\"u'
if [[ $VERREQ =~ $re ]]; then
	DEVCDE=${BASH_REMATCH[1]}
fi

The odd characters in the regular expressions are there to try and ensure we’re capturing the right information. Unfortunately, unlike other regular expression features, Bash’s does not have an option for non-greedy capture. If the pattern for device_code for example simply ended with a quotation mark, we’d capture all of the user_code and verification_url lines as well.

This code assigns the verification URL, the user code, and the device code to VERURL, USERID and DEVCDE respectively, using Bash’s native regular expression feature to extract the information.

One can then assign this to a file, with a line like the following:

echo $DEVCDE > _device_code.txt

However, I don’t save the user_code or verification_url parts as I only need them temporarily…

Step 2. Ask the user for permission

Because I’m working in command line right now, and expect my users will too, I simply display the URL and ask the user to enter the code:

echo "Please visit " $VERURL "and enter this code: " $USERID

This line and all the above, sit in one file which I’ve called authenticate-as-device.sh.

Step 3. Get the access and refresh tokens

At the moment, I have a separate shell script for retrieving the access and refresh tokens. Once a user has approved the application to work with their account through the consent screen, they need to run the poll-for-access-token.sh. It’s not ideal and I’ll bring it altogether later, but it does help illustrate the different steps involved in the authenticate process for now.

The script starts with the following request.

CLNTID=$(<_client_id.txt)
CLNTSE=$(<_client_secret.txt)
DEVCDE=$(<_device_code.txt)
POLLRT=$(curl -s -d "client_id="$CLNTID"&client_secret="$CLNTSE"&code="$DEVCDE"&grant_type=http://oauth.net/grant_type/device/1.0" https://accounts.google.com/o/oauth2/token)

The client ID, client secret (which we also noted earlier and stored in a text file), and the device code (which we stored in step 1 above), are all retrieved and assigned to CLNTID, CLNTSE and DEVCDE respectively.

Then a cURL command is executed to the token URI.

What’s returned is yet more JSON, which as before needs parsing (the tokens again being quite clearly examples):

{
	"access_token" : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
	"token_type" : "Bearer",
	"expires_in" : 3600,
	"refresh_token" : "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr"
}

In this JSON, as can be seen in the example, is the access token and the refresh token. Once again, we can use Bash regular expressions to capture what’s needed.

re='\"access_token\" \: \"(.*)\"\,[[:space:]]+\"t'
if [[ $POLLRT =~ $re ]]; then
	ACCTOK=${BASH_REMATCH[1]}
fi

re='\"refresh_token\" \: \"(.*)\"'
if [[ $POLLRT =~ $re ]]; then
	REFTOK=${BASH_REMATCH[1]}
fi

Finally, we can put these into local files to be called on when they’re needed.

echo $ACCTOK > _access_token.txt
echo $REFTOK > _refresh_token.txt

With the access token, it’s now possible to start interacting directly with the Picasa API.

Sending requests

Most of this post is about authentication so I won’t dwell too much on making the requests to Picasa itself. But it’d be a shame not to see how the access token is used to actually get on with working with the API.

First, to test this out, I took the XML from the Picasa Developer’s Guide on Protocol and saved it to a test-post-album.xml file, with a few tweaks (not shown here).

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns='http://www.w3.org/2005/Atom'
    xmlns:media='http://search.yahoo.com/mrss/'
    xmlns:gphoto='http://schemas.google.com/photos/2007'>
  <title type='text'>Trip To Italy</title>
  <summary type='text'>This was the recent trip I took to Italy.</summary>
  <gphoto:location>Italy</gphoto:location>
  <gphoto:access>public</gphoto:access>
  <gphoto:timestamp>1152255600000</gphoto:timestamp>
  <media:group>
    <media:keywords>italy, vacation</media:keywords>
  </media:group>
  <category scheme='http://schemas.google.com/g/2005#kind'
    term='http://schemas.google.com/photos/2007#album'></category>
</entry>

It’s then only a simple bit of code to post the album to the Picasa account.

ACCTOK=$(<_access_token.txt)

# Try posting with test xml.
NEWALB=$(curl https://picasaweb.google.com/data/feed/api/user/default -X POST -H "GData-Version: 2" -H "Authorization: Bearer $ACCTOK" -H "Content-Type: application/atom+xml" -d @test-post-album.xml)
echo $NEWALB > _result_new_album.xml

The first line of the above snippet retrieves the access token. This is then used in a cURL post request which has some other crucial features. The GData-Version: 2 must be listed in the header for example and the content type has to be specified to. One can specify the username for the account after the user/ section of the request URI but default, as shown in the example, seems to work just as well. I assume that authentication takes care of the simple matter of identification too.

The XML return is then stored to a local XML file, _result_new_album.xml, for later use.

We can then parse this later when we want to, say, post an image to our newly created album. As before, we can use Bash’s regular expression to feature, this time to extract the album ID.

NEWALB=$(<_result_new_album.xml)

re="<gphoto:id>(.*)</gphoto:id>"
if [[ $NEWALB =~ $re ]]; then
	ALBMID=${BASH_REMATCH[1]}
fi

With this and the authentication token we can actually post an image to the new album.

FILESIZE=$(stat -c "%s" "../mongolian-flower.jpg")

NEWALB=$(curl https://picasaweb.google.com/data/feed/api/user/default/albumid/$ALBMID -X POST -H "GData-Version: 2" -H "Authorization: Bearer $ACCTOK" -H "Content-Type: image/jpeg" -H "Content-Length: $FILESIZE" -H "Slug: test-photo.jpg" --data-binary @../mongolian-flower.jpg)

The only other bit of information that was missing was the size of the file, which is why the first line of the snippet is needed. As you can see the file in this case was the same image of a Mongolian flower which I used in my most recent post on image-handling. Definitely nothing to with any of my trips to Italy, but it’ll do as an example!

And that’s how one can use all the above to post to Picasa.

Refreshing the access token

One last thing. The access token has a limited life, which is why it’s important to hold onto the refresh token too. A simple shell script which can be run when the access token does expire, can refresh it and allow one to continue to make requests:

CLNTID=$(<_client_id.txt)
CLNTSE=$(<_client_secret.txt)
REFTOK=$(<_refresh_token.txt)

# Get new access token with refresh
POLLRT=$(curl -d "client_id="$CLNTID"&client_secret="$CLNTSE"&refresh_token="$REFTOK"&grant_type=refresh_token" https://accounts.google.com/o/oauth2/token)

re='\"access_token\" \: \"(.*)\"\,[[:space:]]+\"t'
if [[ $POLLRT =~ $re ]]; then
	ACCTOK=${BASH_REMATCH[1]}
fi

echo $ACCTOK > _access_token.txt

What’s next?

Given that the OAuth authentication returns JSON and this is much more lightweight interchange format for specifying information for posting with cURL I’d like to explore this at some point. Unfortunately, Bash doesn’t seem particularly well-suited to JSON so I’ll have to explore options for how I can do this.

I’d also like to look at automating the polling and the refresh aspects of this process so that the user of the workflow (in this case me) as to run as few things as possible to get going.

But in the short term, I’ll most likely be working out how I can use this to automate the uploading and managing of images directly from the post I’m writing.