I store many of my photographs on smugmug (see http://pitonyak.smugmug.com/). They gave me a "coupon" to use that will save you $5 on your renewal (and if you use it, I save $10 on mine). The code is: Rl0h94ubYv13o
The upload client from Linux does not work well, so I am investigating accessing the website using restful web services. Step 1, obtain an account. Step 2, request a key that allows me to use the web services. Note that the key is free for the asking and they respond promptly.
For my first attempt, I connected directly from my web browser. Obviously, my login email address is not x@y.com, my password is not 1234, and my APIKey is not abcdefg, but, if it were, I can go to this URL to login.
https://api.smugmug.com/hack/rest/1.2.0/?method=smugmug.login.withPasswo...
I received the following reply:
<rsp stat="ok">
<method>smugmug.login.withPassword</method>
<Login PasswordHash="xyzzy" AccountType="Power" FileSizeLimit="12582912" SmugVault="0">
<Session id="gfedcba"/>
<User id="94628" NickName="pitonyak" DisplayName="pitonyak"/>
</Login>
</rsp>
After parsing the XML, I made another call to obtain information regarding all of my albums:
https://api.smugmug.com/hack/rest/1.2.0/?SessionID=gfedcba&Heavy=1&metho...
A representative reply showing only a single album is shown below:
<rsp stat="ok">
<method>smugmug.albums.get</method>
<Albums>
<Album id="1" Key="2" Position="1" ImageCount="2" Title="Lib 1" Description="Whatever" Keywords="" Public="1" Password="" PasswordHint="" Printable="1" Filenames="0" Comments="1" External="1" Originals="1" EXIF="1" Share="1" SortMethod="Position" SortDirection="0" LastUpdated="2007-08-21 19:53:25" FamilyEdit="0" FriendEdit="0" HideOwner="0" CanRank="1" Clean="0" Geography="1" SmugSearchable="1" WorldSearchable="1" SquareThumbs="0" ColorCorrection="2" X2Larges="1" X3Larges="1" Header="0" Protected="0" UnsharpRadius="1" UnsharpSigma="1" UnsharpAmount="0.2" UnsharpThreshold="0.05">
<Highlight id="0"/>
<Community id="0"/>
<Template id="0"/>
<Category id="0" Name="Other"/>
</Album>
</Albums>
</rsp>
Don't forget to logout
https://api.smugmug.com/hack/rest/1.2.0/?SessionID=gfedcba&method=smugmu...
My first attempt was using Java. The HashMap contained a list of key/value pairs, such as APIKey/abcdefg. The method is a string such as "smugmug.logout".
public String callMethod(HashMap<String, String> parameters, String method)
{
if (parameters == null)
{
parameters = new HashMap<String, String>();
}
if (method != null && method.length() > 0)
{
if (parameters.containsKey(SmugMugConstants.XmlMethod))
{
parameters.remove(SmugMugConstants.XmlMethod);
}
parameters.put(SmugMugConstants.XmlMethod, method);
}
if (!parameters.containsKey(SmugMugConstants.ArgAPIKey))
{
parameters.put(SmugMugConstants.ArgAPIKey, apiKey);
}
String response = null;
try
{
URL url = new URL(baseEndPoint + EncoderHelper.buildEncodedURL(parameters, "?", encodeArguments));
System.out.println("Using URL: " + url.toString());
//make connection, use post mode, and send query
URLConnection urlc = url.openConnection();
urlc.setDoOutput(true);
urlc.setAllowUserInteraction(false);
PrintStream ps = new PrintStream(urlc.getOutputStream());
//ps.print(EncoderHelper.buildEncodedURL(parameters, "?", false));
ps.close();
//retrieve result
BufferedReader br = new BufferedReader(new InputStreamReader(urlc.getInputStream()));
String str;
StringBuffer sb = new StringBuffer();
while ((str = br.readLine()) != null)
{
sb.append(str);
sb.append("\n");
}
br.close();
response = sb.toString();
logger.info("Response = " + response);
}
catch (Exception e)
{
logger.severe("Failed REST service call.\n" + exceptionStacktraceToString(e));
response = null;
}
return response;
}
This is the encoder helper class.
public class EncoderHelper
{
public static String Encoding = "UTF-8";
/**
* Build the argument portion of a URL. Iterate through all entries in the hash table and
* build a string such as "?key1=value1&key2=value2&key3=value3".
* @param parameters Map of key/value pairs that will become the argument string.
* @param leadingString Usually a "?", this is the first character in the string.
* @param encodeValues Determines if values are URL encoded.
* @return
* @throws java.io.UnsupportedEncodingException
*/
public static String buildEncodedURL(HashMap<String, String> parameters, String leadingString, boolean encodeValues) throws UnsupportedEncodingException
{
StringBuilder sb = new StringBuilder();
if (parameters != null)
{
String value = null;
for (String key : parameters.keySet())
{
value = parameters.get(key);
if (key != null && key.length() > 0 && value != null)
{
if (sb.length() == 0)
{
if (leadingString != null)
{
sb.append(leadingString);
}
}
else
{
sb.append("&");
}
sb.append(key + "=");
if (encodeValues)
{
sb.append(URLEncoder.encode(value, Encoding));
}
else
{
sb.append(value);
}
}
}
}
return sb.toString();
}
}
The driver code sets the arguments and makes the call to process the arguments:
public SmugMugLoginEntity login(String emailAddress, String password) throws XMLStreamException, IllegalAccessException
{
SmugMugLoginEntity entity = new SmugMugLoginEntity();
HashMap<String, String> parameters = new HashMap<String, String>();
setArgumentValue(parameters, SmugMugConstants.ArgEmailAddress, emailAddress, true);
setArgumentValue(parameters, SmugMugConstants.ArgPassword, password, true);
String result = callMethod(parameters, CmdLoginWithPassword);
if (!entity.processXML(result, CmdLoginWithPassword, true))
{
return null;
}
return entity;
}
public SmugMugAlbumsEntity getAlbumsForAccount(String session, boolean verboseInformation) throws XMLStreamException, IllegalAccessException
{
SmugMugAlbumsEntity entity = new SmugMugAlbumsEntity();
HashMap<String, String> parameters = new HashMap<String, String>();
setArgumentValue(parameters, SmugMugConstants.ArgSessionID, session, true);
if (verboseInformation)
{
setArgumentValue(parameters, SmugMugConstants.ArgHeavy, "1", true);
}
String result = callMethod(parameters, CmdAlbumsGet);
if (!entity.processXML(result, CmdAlbumsGet, true))
{
return null;
}
return entity;
}
public SmugMugImagesEntity getImagesForAlbum(String session, boolean verboseInformation, String sitePassword, SmugMugAlbumEntity entity) throws XMLStreamException, IllegalAccessException
{
String albumKey = entity.getAlbumKey();
String albumId = entity.getAlbumId();
String albumPassword = entity.getAlbumPassword();
return getImagesForAlbum(session, albumId, verboseInformation, albumPassword, sitePassword, albumKey);
}
/**
* Retrieves a list of images for a given album.
* @param session
* @param albumId
* @param verboseInformation
* @param albumPassword
* @param sitePassword
* @param albumKey
* @return
* @throws javax.xml.stream.XMLStreamException
* @throws java.lang.IllegalAccessException
*/
public SmugMugImagesEntity getImagesForAlbum(String session, String albumId, boolean verboseInformation, String albumPassword, String sitePassword, String albumKey) throws XMLStreamException, IllegalAccessException
{
SmugMugImagesEntity entity = new SmugMugImagesEntity();
HashMap<String, String> parameters = new HashMap<String, String>();
setArgumentValue(parameters, SmugMugConstants.ArgSessionID, session, true);
setArgumentValue(parameters, SmugMugConstants.ArgAlbumID, albumId, true);
setArgumentValue(parameters, SmugMugConstants.ArgAlbumKey, albumKey, true);
if (albumPassword != null)
{
setArgumentValue(parameters, SmugMugConstants.ArgPassword, albumPassword, true);
}
if (sitePassword != null)
{
setArgumentValue(parameters, SmugMugConstants.ArgSitePassword, sitePassword, true);
}
if (verboseInformation)
{
setArgumentValue(parameters, SmugMugConstants.ArgHeavy, "1", true);
}
String result = callMethod(parameters, CmdImagesGet);
if (!entity.processXML(result, CmdImagesGet, true))
{
return null;
}
return entity;
}
/**
* This method will return camera and photograph details about the image
* specified by ImageID. The Album must be owned by the Session holder,
* or else be Public (if password-protected, a Password must be provided),
* to return results. Otherwise, an "invalid user" faultCode will result.
* Additionally, the album owner must have specified that EXIF data is
* allowed. Note that many photos have no EXIF data, so an empty or
* partially returned result is normal.
* @param session Current Session Id
* @param imageId Desired Image Id
* @param albumPassword Password for the album, or NULL of not required.
* @param sitePassword Password for the site, or NULL if not required.
* @param imageKey Key for the desired Image.
* @return Image EXIF information.
* @throws javax.xml.stream.XMLStreamException
* @throws java.lang.IllegalAccessException
*/
public SmugMugImageEntity getImageExif(String session, String imageId, String albumPassword, String sitePassword, String imageKey) throws XMLStreamException, IllegalAccessException
{
SmugMugImageEntity entity = new SmugMugImageEntity();
HashMap<String, String> parameters = new HashMap<String, String>();
setArgumentValue(parameters, SmugMugConstants.ArgSessionID, session, true);
setArgumentValue(parameters, SmugMugConstants.ArgImageID, imageId, true);
setArgumentValue(parameters, SmugMugConstants.ArgImageKey, imageKey, true);
if (albumPassword != null)
{
setArgumentValue(parameters, SmugMugConstants.ArgPassword, albumPassword, true);
}
if (sitePassword != null)
{
setArgumentValue(parameters, SmugMugConstants.ArgSitePassword, sitePassword, true);
}
String result = callMethod(parameters, CmdImagesGetExif);
if (!entity.processXML(result, CmdImagesGetExif, true))
{
return null;
}
return entity;
}
/**
* This method will return details about the image specified by ImageID.
* The Album must be owned by the Session holder, or else be Public
* (if password-protected, a Password must be provided), to return results..
* Otherwise, an "invalid user" faultCode will result. Additionally,
* some fields are only returned to the Album owner.
* @param session Current Session Id
* @param imageId Desired Image Id
* @param albumPassword Password for the album, or NULL of not required.
* @param sitePassword Password for the site, or NULL if not required.
* @param imageKey Key for the desired Image.
* @return Information for a specific image.
* @throws javax.xml.stream.XMLStreamException
* @throws java.lang.IllegalAccessException
*/
public SmugMugImageEntity getImageInfo(String session, String imageId, String albumPassword, String sitePassword, String imageKey) throws XMLStreamException, IllegalAccessException
{
SmugMugImageEntity entity = new SmugMugImageEntity();
HashMap<String, String> parameters = new HashMap<String, String>();
setArgumentValue(parameters, SmugMugConstants.ArgSessionID, session, true);
setArgumentValue(parameters, SmugMugConstants.ArgImageID, imageId, true);
setArgumentValue(parameters, SmugMugConstants.ArgImageKey, imageKey, true);
if (albumPassword != null)
{
setArgumentValue(parameters, SmugMugConstants.ArgPassword, albumPassword, true);
}
if (sitePassword != null)
{
setArgumentValue(parameters, SmugMugConstants.ArgSitePassword, sitePassword, true);
}
String result = callMethod(parameters, CmdImagesGetInfo);
if (!entity.processXML(result, CmdImagesGetInfo, true))
{
return null;
}
return entity;
}
/**
* Logout a session ID.
* @param session Current session ID.
* @throws javax.xml.stream.XMLStreamException
*/
public void logout(String session) throws XMLStreamException
{
HashMap<String, String> parameters = new HashMap<String, String>();
setArgumentValue(parameters, SmugMugConstants.ArgSessionID, session, true);
String x = callMethod(parameters, CmdLogout);
}
public SmugMugAlbumEntity getAlbumInfo(String session, String albumID, String albumKey, String albumPassword) throws XMLStreamException, IllegalAccessException
{
SmugMugAlbumEntity entity = new SmugMugAlbumEntity();
HashMap<String, String> parameters = new HashMap<String, String>();
setArgumentValue(parameters, SmugMugConstants.ArgSessionID, session, true);
setArgumentValue(parameters, SmugMugConstants.ArgAlbumKey, albumKey, false);
setArgumentValue(parameters, SmugMugConstants.ArgAlbumID, albumID, false);
setArgumentValue(parameters, SmugMugConstants.ArgPassword, albumPassword, false);
String result = callMethod(parameters, CmdAlbumsGetInfo);
if (!entity.processXML(result, CmdAlbumsGetInfo, true))
{
return null;
}
return entity;
}
/**
* Set a key/value pair, overwriting any existing key/value pair.
* @param parameters key/value pairs
* @param key
* @param value
* @param forceValue If true, then value can not be null.
*/
public static void setArgumentValue(HashMap<String, String> parameters, String key, String value, boolean forceValue)
{
if (parameters == null)
{
throw new NullPointerException("Parameters can not be null in setArgumentValue");
}
if (key == null)
{
throw new NullPointerException("Key can not be null in setArgumentValue");
}
if (value == null)
{
if (forceValue)
{
throw new NullPointerException("Value can not be null in setArgumentValue for key (" + key + ")");
}
return;
}
if (parameters.containsKey(key))
{
parameters.remove(key);
}
parameters.put(key, value);
}
Pizza's here, so I will post a follow-up later where I use C++ and QT rather than Java.