/flickr.go

https://code.google.com/p/flickgo/ · Go · 239 lines · 169 code · 26 blank · 44 comment · 31 complexity · 341b9c892f67c9dd6fd2ad3f1cbc2234 MD5 · raw file

  1. // Flickr library for Go.
  2. // Created to be used primarily in Google App Engine.
  3. package flickgo
  4. import (
  5. "fmt"
  6. "net/http"
  7. "strconv"
  8. "strings"
  9. )
  10. // Flickr API permission levels. See
  11. // http://www.flickr.com/services/api/auth.spec.html.
  12. const (
  13. ReadPerm = "read"
  14. WritePerm = "write"
  15. DeletePerm = "delete"
  16. )
  17. // Debug logger.
  18. type Debugfer interface {
  19. // Debugf formats its arguments according to the format, analogous to fmt.Printf,
  20. // and records the text as a log message at Debug level.
  21. Debugf(format string, args ...interface{})
  22. }
  23. // Flickr client.
  24. type Client struct {
  25. // Auth token for acting on behalf of a user.
  26. AuthToken string
  27. // Logger to use.
  28. // Hint: App engine's Context implements this interface.
  29. Logger Debugfer
  30. // API key for your app.
  31. apiKey string
  32. // API secret for your app.
  33. secret string
  34. // Client to use for HTTP communication.
  35. httpClient *http.Client
  36. }
  37. // Creates a new Client object. See
  38. // http://www.flickr.com/services/api/misc.api_keys.html for learning about API
  39. // key and secret. For App Engine apps, you can create httpClient by calling
  40. // urlfetch.Client function; other apps can pass http.DefaultClient.
  41. func New(apiKey string, secret string, httpClient *http.Client) *Client {
  42. return &Client{
  43. apiKey: apiKey,
  44. secret: secret,
  45. httpClient: httpClient,
  46. }
  47. }
  48. // Returns the URL for requesting authorisation to access the user's Flickr
  49. // account. List of possible permissions are defined at
  50. // http://www.flickr.com/services/api/auth.spec.html. You can also use one of
  51. // the following constants:
  52. // ReadPerm
  53. // WritePerm
  54. // DeletePerm
  55. func (c *Client) AuthURL(perms string) string {
  56. args := map[string]string{}
  57. args["perms"] = perms
  58. return signedURL(c.secret, c.apiKey, "auth", args)
  59. }
  60. // Returns the signed URL for Flickr's flickr.auth.getToken request.
  61. func getTokenURL(c *Client, frob string) string {
  62. return makeURL(c, "flickr.auth.getToken", map[string]string{"frob": frob}, true)
  63. }
  64. type flickrError struct {
  65. Code string `xml:"code,attr"`
  66. Msg string `xml:"msg,attr"`
  67. }
  68. func (e *flickrError) Err() error {
  69. return fmt.Errorf("Flickr error code %s: %s", e.Code, e.Msg)
  70. }
  71. // Exchanges a temporary frob for a token that's valid forever.
  72. // See http://www.flickr.com/services/api/auth.howto.web.html.
  73. func (c *Client) GetToken(frob string) (string, *User, error) {
  74. r := struct {
  75. Stat string `xml:"stat,attr"`
  76. Err flickrError `xml:"err"`
  77. Auth struct {
  78. Token string `xml:"token"`
  79. User User `xml:"user"`
  80. } `xml:"auth"`
  81. }{}
  82. if err := flickrGet(c, getTokenURL(c, frob), &r); err != nil {
  83. return "", nil, err
  84. }
  85. if r.Stat != "ok" {
  86. return "", nil, r.Err.Err()
  87. }
  88. return r.Auth.Token, &r.Auth.User, nil
  89. }
  90. // Returns URL for Flickr photo search.
  91. func searchURL(c *Client, args map[string]string) string {
  92. argsCopy := clone(args)
  93. argsCopy["extras"] += ",url_t"
  94. return makeURL(c, "flickr.photos.search", argsCopy, true)
  95. }
  96. // Searches for photos. args contains search parameters as described in
  97. // http://www.flickr.com/services/api/flickr.photos.search.html.
  98. func (c *Client) Search(args map[string]string) (*SearchResponse, error) {
  99. r := struct {
  100. Stat string `xml:"stat,attr"`
  101. Err flickrError `xml:"err"`
  102. Photos SearchResponse `xml:"photos"`
  103. }{}
  104. if err := flickrGet(c, searchURL(c, args), &r); err != nil {
  105. return nil, err
  106. }
  107. if r.Stat != "ok" {
  108. return nil, r.Err.Err()
  109. }
  110. for i, ph := range r.Photos.Photos {
  111. h, hErr := strconv.ParseFloat(ph.Height_T, 64)
  112. w, wErr := strconv.ParseFloat(ph.Width_T, 64)
  113. if hErr == nil && wErr == nil {
  114. // ph is apparently just a copy of r.Photos.Photos[i], so we are
  115. // updating the original.
  116. r.Photos.Photos[i].Ratio = w / h
  117. }
  118. }
  119. return &r.Photos, nil
  120. }
  121. // Initiates an asynchronous photo upload and returns the ticket ID. See
  122. // http://www.flickr.com/services/api/upload.async.html for details.
  123. func (c *Client) Upload(name string, photo []byte,
  124. args map[string]string) (ticketID string, err error) {
  125. req, uErr := uploadRequest(c, name, photo, args)
  126. if uErr != nil {
  127. return "", wrapErr("request creation failed", uErr)
  128. }
  129. resp := struct {
  130. Stat string `xml:"stat,attr"`
  131. Err flickrError `xml:"err"`
  132. TicketID string `xml:"ticketid"`
  133. }{}
  134. if err := flickrPost(c, req, &resp); err != nil {
  135. return "", wrapErr("uploading failed", err)
  136. }
  137. if resp.Stat != "ok" {
  138. return "", resp.Err.Err()
  139. }
  140. return resp.TicketID, nil
  141. }
  142. // Returns URL for flickr.photos.upload.checkTickets request.
  143. func checkTicketsURL(c *Client, tickets []string) string {
  144. args := make(map[string]string)
  145. args["tickets"] = strings.Join(tickets, ",")
  146. return makeURL(c, "flickr.photos.upload.checkTickets", args, false)
  147. }
  148. // Asynchronous photo upload status response.
  149. type TicketStatus struct {
  150. ID string `xml:"id,attr"`
  151. Complete string `xml:"complete,attr"`
  152. Invalid string `xml:"invalid,attr"`
  153. PhotoID string `xml:"photoid,attr"`
  154. }
  155. // Checks the status of async upload tickets (returned by Upload method, for
  156. // example). Interface for
  157. // http://www.flickr.com/services/api/flickr.photos.upload.checkTickets.html
  158. // API method.
  159. func (c *Client) CheckTickets(tickets []string) (statuses []TicketStatus, err error) {
  160. r := struct {
  161. Stat string `xml:"stat,attr"`
  162. Err flickrError `xml:"err"`
  163. Tickets []TicketStatus `xml:"uploader>ticket"`
  164. }{}
  165. if err := flickrGet(c, checkTicketsURL(c, tickets), &r); err != nil {
  166. return nil, err
  167. }
  168. if r.Stat != "ok" {
  169. return nil, r.Err.Err()
  170. }
  171. return r.Tickets, nil
  172. }
  173. // Returns URL for flickr.photosets.getList request.
  174. func getPhotoSetsURL(c *Client, userID string) string {
  175. args := make(map[string]string)
  176. args["user_id"] = userID
  177. return makeURL(c, "flickr.photosets.getList", args, true)
  178. }
  179. // Returns the list of photo sets of the specified user.
  180. func (c *Client) GetSets(userID string) ([]PhotoSet, error) {
  181. r := struct {
  182. Stat string `xml:"stat,attr"`
  183. Err flickrError `xml:"err"`
  184. Sets []PhotoSet `xml:"photosets>photoset"`
  185. }{}
  186. if err := flickrGet(c, getPhotoSetsURL(c, userID), &r); err != nil {
  187. return nil, err
  188. }
  189. if r.Stat != "ok" {
  190. return nil, r.Err.Err()
  191. }
  192. return r.Sets, nil
  193. }
  194. func addToSetURL(c *Client, photoID, setID string) string {
  195. args := make(map[string]string)
  196. args["photo_id"] = photoID
  197. args["photoset_id"] = setID
  198. return makeURL(c, "flickr.photosets.addPhoto", args, true)
  199. }
  200. // Adds a photo to a photoset.
  201. func (c *Client) AddPhotoToSet(photoID, setID string) error {
  202. r := struct {
  203. Stat string `xml:"stat,attr"`
  204. Err flickrError `xml:"err"`
  205. }{}
  206. if err := flickrGet(c, addToSetURL(c, photoID, setID), &r); err != nil {
  207. return err
  208. }
  209. if r.Stat != "ok" {
  210. return r.Err.Err()
  211. }
  212. return nil
  213. }