perkeep/pkg/importer/flickr/flickr.go

195 lines
4.5 KiB
Go
Raw Normal View History

/*
Copyright 2013 The Camlistore Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package flickr implements an importer for flickr.com accounts.
package flickr
import (
"crypto/md5"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"sort"
"strings"
"camlistore.org/pkg/importer"
"camlistore.org/pkg/jsonconfig"
)
const (
authURL = "http://www.flickr.com/auth-72157636676651636"
apiURL = "http://api.flickr.com/services/rest/"
apiSecret = "6ed517d5f44946c9"
apiKey = "b5801cdbc870073e7b136f24fb50396f"
)
func init() {
importer.Register("flickr", newFromConfig)
}
type imp struct {
authToken string
userId string
}
func newFromConfig(cfg jsonconfig.Obj) (importer.Importer, error) {
// TODO(aa): miniToken config is temporary. There should be UI to auth using oauth.
miniToken := cfg.RequiredString("miniToken")
if err := cfg.Validate(); err != nil {
return nil, err
}
im := &imp{}
if miniToken != "" {
if err := im.authenticate(http.DefaultClient, miniToken); err != nil {
return nil, err
}
}
return im, nil
}
func (im *imp) CanHandleURL(url string) bool { return false }
func (im *imp) ImportURL(url string) error { panic("unused") }
func (im *imp) Prefix() string {
return fmt.Sprintf("flickr:%s", im.userId)
}
type photoMeta struct {
Id string
Title string
Ispublic int
Isfriend int
Isfamily int
Description struct {
Content string `json:"_content"`
}
Dateupload string
Datetaken string
Originalformat string
Lastupdate string
Latitude float32
Longitude float32
Tags string
Machinetags string `json:"machine_tags"`
Views string
Media string
URL string `json:"url_o"`
}
type searchPhotosResult struct {
Photos struct {
Page int
Pages int
Perpage int
Total int `json:",string"`
Photo []photoMeta
}
Stat string
}
func (im *imp) Run(h *importer.Host, intr importer.Interrupt) error {
if im.authToken == "" {
return fmt.Errorf("miniToken config key required. Go to %s to get one.", authURL)
}
resp := searchPhotosResult{}
if err := im.flickrRequest(h.HTTPClient(), map[string]string{
"method": "flickr.photos.search",
"user_id": "me",
"extras": "description, date_upload, date_taken, original_format, last_update, geo, tags, machine_tags, views, media, url_o"},
&resp); err != nil {
return err
}
for _, item := range resp.Photos.Photo {
camliIdFramgment := fmt.Sprintf("photo-%s", item.Id)
photoContentHint := item.Lastupdate
fmt.Println(camliIdFramgment, photoContentHint)
// TODO(aa): Stuff
}
return nil
}
type getFullAuthTokenResp struct {
Auth struct {
Token struct {
Content string `json:"_content"`
}
User struct {
Nsid string
}
}
Stat string
}
func (im *imp) authenticate(httpClient *http.Client, miniToken string) error {
resp := getFullAuthTokenResp{}
if err := im.flickrRequest(httpClient, map[string]string{
"method": "flickr.auth.getFullToken",
"mini_token": miniToken}, &resp); err != nil {
return err
}
im.userId = resp.Auth.User.Nsid
im.authToken = resp.Auth.Token.Content
return nil
}
func (im *imp) flickrRequest(httpClient *http.Client, params map[string]string, result interface{}) error {
params["api_key"] = apiKey
params["format"] = "json"
params["nojsoncallback"] = "1"
if im.authToken != "" {
params["auth_token"] = im.authToken
}
paramList := make([]string, 0, len(params))
for key, val := range params {
paramList = append(paramList, key+val)
}
sort.Strings(paramList)
hash := md5.New()
body := apiSecret + strings.Join(paramList, "")
io.WriteString(hash, body)
digest := hash.Sum(nil)
reqURL, _ := url.Parse(apiURL)
q := reqURL.Query()
for key, val := range params {
q.Set(key, val)
}
q.Set("api_sig", fmt.Sprintf("%x", digest))
reqURL.RawQuery = q.Encode()
res, err := httpClient.Get(reqURL.String())
if err != nil {
return err
}
if res.StatusCode != http.StatusOK {
return fmt.Errorf("Auth request failed with: %s", res.Status)
}
defer res.Body.Close()
return json.NewDecoder(res.Body).Decode(result)
}