PageRenderTime 54ms CodeModel.GetById 18ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

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