flickraw 0.8.4 → 0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +28 -43
- data/examples/auth.rb +6 -7
- data/examples/search.rb +22 -0
- data/examples/sinatra.rb +22 -0
- data/examples/upload.rb +7 -12
- data/flickraw_rdoc.rb +2 -5
- data/lib/flickraw.rb +199 -105
- data/rakefile +5 -26
- data/test/test.rb +14 -2
- data/test/test_upload.rb +6 -1
- metadata +12 -26
- data/examples/flickr_KDE.rb +0 -23
data/README.rdoc
CHANGED
@@ -5,26 +5,25 @@ It maps exactly the methods described in {the official api documentation}[http:/
|
|
5
5
|
It also tries to present the data returned in a simple and intuitive way.
|
6
6
|
The methods are fetched from flickr when loading the library by using introspection capabilities. So it is always up-to-date with regards to new methods added by flickr.
|
7
7
|
|
8
|
-
The
|
9
|
-
|
10
|
-
The github repository: http://github.com/hanklords/flickraw/tree/master
|
8
|
+
The github repository: http://github.com/hanklords/flickraw
|
11
9
|
|
12
10
|
= Installation
|
13
11
|
Type this in a console (you might need to be superuser)
|
14
12
|
gem install flickraw
|
15
13
|
|
16
14
|
This will recreate the documentation by fetching the methods descriptions from flickr and then virtually plugging them in standard rdoc documentation.
|
17
|
-
|
15
|
+
$ cd flickraw
|
16
|
+
$ rake rdoc
|
18
17
|
|
19
18
|
= Features
|
20
19
|
|
21
|
-
* Small single file: flickraw.rb is less than
|
20
|
+
* Small single file: flickraw.rb is less than 400 lines
|
21
|
+
* Minimal dependencies
|
22
22
|
* Complete support of flickr API. This doesn't require an update of the library
|
23
23
|
* Ruby syntax similar to the flickr api
|
24
24
|
* Flickr authentication
|
25
25
|
* Photo upload
|
26
26
|
* Proxy support
|
27
|
-
* Delayed library loading (for rails users)
|
28
27
|
* Flickr URLs helpers
|
29
28
|
|
30
29
|
= Usage
|
@@ -42,14 +41,14 @@ This will recreate the documentation by fetching the methods descriptions from f
|
|
42
41
|
secret = list[0].secret
|
43
42
|
info = flickr.photos.getInfo :photo_id => id, :secret => secret
|
44
43
|
|
45
|
-
info.title # => "PICT986"
|
46
|
-
info.dates.taken # => "2006-07-06 15:16:18"
|
44
|
+
puts info.title # => "PICT986"
|
45
|
+
puts info.dates.taken # => "2006-07-06 15:16:18"
|
47
46
|
|
48
47
|
|
49
48
|
sizes = flickr.photos.getSizes :photo_id => id
|
50
49
|
|
51
50
|
original = sizes.find {|s| s.label == 'Original' }
|
52
|
-
original.width # => "800"
|
51
|
+
puts original.width # => "800" -- may fail if they have no original marked image
|
53
52
|
|
54
53
|
== Authentication
|
55
54
|
|
@@ -58,32 +57,34 @@ This will recreate the documentation by fetching the methods descriptions from f
|
|
58
57
|
FlickRaw.api_key="... Your API key ..."
|
59
58
|
FlickRaw.shared_secret="... Your shared secret ..."
|
60
59
|
|
61
|
-
|
62
|
-
auth_url =
|
60
|
+
token = flickr.get_request_token(:perms => 'delete')
|
61
|
+
auth_url = flickr.get_authorize_url(token['oauth_token'], :perms => 'delete')
|
63
62
|
|
64
63
|
puts "Open this url in your process to complete the authication process : #{auth_url}"
|
65
|
-
puts "
|
66
|
-
|
64
|
+
puts "Copy here the number given when you complete the process."
|
65
|
+
verify = gets.strip
|
67
66
|
|
68
67
|
begin
|
69
|
-
|
68
|
+
flickr.get_access_token(token['oauth_token'], token['oauth_token_secret'], verify)
|
70
69
|
login = flickr.test.login
|
71
|
-
puts "You are now authenticated as #{login.username} with token #{
|
70
|
+
puts "You are now authenticated as #{login.username} with token #{flickr.access_token} and secret #{flickr.access_secret}"
|
72
71
|
rescue FlickRaw::FailedResponse => e
|
73
72
|
puts "Authentication failed : #{e.msg}"
|
74
73
|
end
|
75
74
|
|
76
|
-
|
75
|
+
If the user has already been authenticated, you can reuse the access token and access secret:
|
77
76
|
|
78
77
|
require 'flickraw'
|
79
78
|
|
80
79
|
FlickRaw.api_key="... Your API key ..."
|
81
80
|
FlickRaw.shared_secret="... Your shared secret ..."
|
82
81
|
|
83
|
-
|
82
|
+
flickr.access_token = "... Your access token ..."
|
83
|
+
flickr.access_secret = "... Your access secret ..."
|
84
84
|
|
85
85
|
# From here you are logged:
|
86
|
-
|
86
|
+
login = flickr.test.login
|
87
|
+
puts "You are now authenticated as #{login.username}"
|
87
88
|
|
88
89
|
== Upload
|
89
90
|
|
@@ -97,27 +98,10 @@ You don't need to do that each time, you can reuse your token:
|
|
97
98
|
# You need to be authentified to do that, see the previous examples.
|
98
99
|
flickr.upload_photo PHOTO_PATH, :title => "Title", :description => "This is the description"
|
99
100
|
|
100
|
-
==
|
101
|
-
|
102
|
-
You can pass some options to modify flickraw behavior:
|
103
|
-
|
104
|
-
FlickRawOptions = {
|
105
|
-
"api_key" => "... Your API key ...",
|
106
|
-
"shared_secret" => "... Your shared secret ...",
|
107
|
-
"auth_token" => "... Your saved token..." # if you set this you will be automatically loggued
|
108
|
-
"lazyload" => true, # This delay the loading of the library until the first call
|
109
|
-
|
110
|
-
# Proxy support. alternatively, you can use the http_proxy environment variable
|
111
|
-
"proxy_host" => "proxy_host",
|
112
|
-
"proxy_port" => "proxy_port",
|
113
|
-
"proxy_user" => "proxy_user",
|
114
|
-
"proxy_password" => "proxy_password",
|
115
|
-
|
116
|
-
"timeout" => 5, # Set the request timeout
|
117
|
-
"auth_token" => "SAVED_TOKEN" # Set the initial token
|
118
|
-
}
|
101
|
+
== Proxy
|
119
102
|
|
120
103
|
require 'flickraw'
|
104
|
+
FlickRaw.proxy = "http://user:pass@proxy.example.com:3129/"
|
121
105
|
|
122
106
|
== Flickr URL Helpers
|
123
107
|
|
@@ -136,22 +120,22 @@ There are some helpers to build flickr urls :
|
|
136
120
|
=== url_photopage
|
137
121
|
|
138
122
|
info = flickr.photos.getInfo(:photo_id => "3839885270")
|
139
|
-
FlickRaw.url_photopage(
|
123
|
+
FlickRaw.url_photopage(info) # => "http://www.flickr.com/photos/41650587@N02/3839885270"
|
140
124
|
|
141
125
|
=== url_photoset, url_photosets
|
142
126
|
|
143
127
|
info = flickr.photos.getInfo(:photo_id => "3839885270")
|
144
|
-
FlickRaw.url_photosets(
|
128
|
+
FlickRaw.url_photosets(info) # => "http://www.flickr.com/photos/41650587@N02/sets/"
|
145
129
|
|
146
|
-
=== url_short
|
130
|
+
=== url_short, url_short_m, url_short_s, url_short_t
|
147
131
|
|
148
132
|
info = flickr.photos.getInfo(:photo_id => "3839885270")
|
149
|
-
FlickRaw.url_short(
|
133
|
+
FlickRaw.url_short(info) # => "http://flic.kr/p/6Rjq7s"
|
150
134
|
|
151
135
|
=== url_photostream
|
152
136
|
|
153
137
|
info = flickr.photos.getInfo(:photo_id => "3839885270")
|
154
|
-
FlickRaw.url_photostream(
|
138
|
+
FlickRaw.url_photostream(info) # => "http://www.flickr.com/photos/41650587@N02/"
|
155
139
|
|
156
140
|
|
157
141
|
See the _examples_ directory to find more examples.
|
@@ -167,9 +151,10 @@ instead of
|
|
167
151
|
require 'flickraw'
|
168
152
|
|
169
153
|
This way it it doesn't fetch available flickr methods each time it is loaded.
|
170
|
-
The flickraw-cached gem is on
|
154
|
+
The flickraw-cached gem is on rubygems.org and should be up-to-date with regard to flickr api most of the time.
|
171
155
|
|
172
156
|
= Notes
|
157
|
+
|
173
158
|
If you want to use the api authenticated with several user at the same time, you must pass the authentication token explicitely each time.
|
174
159
|
This is because it is keeping the authentication token internally.
|
175
160
|
As an alternative, you can create new Flickr objects besides Kernel.flickr which is created for you. Use Flickr.new to this effect.
|
data/examples/auth.rb
CHANGED
@@ -8,18 +8,17 @@ SHARED_SECRET=''
|
|
8
8
|
FlickRaw.api_key=API_KEY
|
9
9
|
FlickRaw.shared_secret=SHARED_SECRET
|
10
10
|
|
11
|
-
|
12
|
-
auth_url =
|
11
|
+
token = flickr.get_request_token(:perms => 'delete')
|
12
|
+
auth_url = flickr.get_authorize_url(token['oauth_token'], :perms => 'delete')
|
13
13
|
|
14
14
|
puts "Open this url in your process to complete the authication process : #{auth_url}"
|
15
|
-
puts "
|
16
|
-
|
15
|
+
puts "Copy here the number given when you complete the process."
|
16
|
+
verify = gets.strip
|
17
17
|
|
18
18
|
begin
|
19
|
-
flickr.
|
19
|
+
flickr.get_access_token(token['oauth_token'], token['oauth_token_secret'], verify)
|
20
20
|
login = flickr.test.login
|
21
|
-
puts "You are now authenticated as #{login.username}"
|
21
|
+
puts "You are now authenticated as #{login.username} with token #{flickr.access_token} and secret #{flickr.access_secret}"
|
22
22
|
rescue FlickRaw::FailedResponse => e
|
23
23
|
puts "Authentication failed : #{e.msg}"
|
24
24
|
end
|
25
|
-
|
data/examples/search.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'flickraw'
|
2
|
+
|
3
|
+
# search for pictures taken within 60 miles of new brunswick, between 1890-1920
|
4
|
+
|
5
|
+
# FlickRaw.api_key="..."
|
6
|
+
# FlickRaw.shared_secret="..."
|
7
|
+
|
8
|
+
new_b = flickr.places.find :query => "new brunswick"
|
9
|
+
latitude = new_b[0]['latitude'].to_f
|
10
|
+
longitude = new_b[0]['longitude'].to_f
|
11
|
+
|
12
|
+
# within 60 miles of new brunswick, let's use a bbox
|
13
|
+
radius = 1
|
14
|
+
args = {}
|
15
|
+
args[:bbox] = "#{longitude - radius},#{latitude - radius},#{longitude + radius},#{latitude + radius}"
|
16
|
+
|
17
|
+
# requires a limiting factor, so let's give it one
|
18
|
+
args[:min_taken_date] = '1890-01-01 00:00:00'
|
19
|
+
args[:max_taken_date] = '1920-01-01 00:00:00'
|
20
|
+
args[:accuracy] = 1 # the default is street only granularity [16], which most images aren't...
|
21
|
+
discovered_pictures = flickr.photos.search args
|
22
|
+
discovered_pictures.each{|p| url = FlickRaw.url p; puts url}
|
data/examples/sinatra.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'flickraw'
|
2
|
+
require 'sinatra'
|
3
|
+
|
4
|
+
FlickRaw.api_key = API_KEY
|
5
|
+
FlickRaw.shared_secret = SHARED_SECRET
|
6
|
+
enable :sessions
|
7
|
+
|
8
|
+
get '/authenticate' do
|
9
|
+
token = flickr.get_request_token(:oauth_callback => to('authenticated'))
|
10
|
+
session[:token] = token
|
11
|
+
redirect flickr.get_authorize_url(token['oauth_token'], :perms => 'delete')
|
12
|
+
end
|
13
|
+
|
14
|
+
get '/authenticated' do
|
15
|
+
token = session[:token]
|
16
|
+
flickr.get_access_token(token['oauth_token'], token['oauth_token_secret'], params['oauth_verifier'])
|
17
|
+
login = flickr.test.login
|
18
|
+
%{
|
19
|
+
You are now authenticated as <b>#{login.username}</b>
|
20
|
+
with token <b>#{flickr.access_token}</b> and secret <b>#{flickr.access_secret}</b>.
|
21
|
+
}
|
22
|
+
end
|
data/examples/upload.rb
CHANGED
@@ -2,21 +2,16 @@ require 'flickraw'
|
|
2
2
|
|
3
3
|
# This is how to upload photos on flickr.
|
4
4
|
# You need to be authentified to do that.
|
5
|
+
|
5
6
|
API_KEY=''
|
6
7
|
SHARED_SECRET=''
|
8
|
+
ACCESS_TOKEN=''
|
9
|
+
ACCESS _SECRET=''
|
7
10
|
PHOTO_PATH='photo.jpg'
|
8
11
|
|
9
|
-
FlickRaw.api_key=API_KEY
|
10
|
-
FlickRaw.shared_secret=SHARED_SECRET
|
11
|
-
|
12
|
-
|
13
|
-
auth_url = FlickRaw.auth_url :frob => frob, :perms => 'write'
|
14
|
-
|
15
|
-
puts "Open this url in your process to complete the authication process : #{auth_url}"
|
16
|
-
puts "Press Enter when you are finished."
|
17
|
-
STDIN.getc
|
18
|
-
|
19
|
-
flickr.auth.getToken :frob => frob
|
20
|
-
login = flickr.test.login
|
12
|
+
FlickRaw.api_key = API_KEY
|
13
|
+
FlickRaw.shared_secret = SHARED_SECRET
|
14
|
+
flickr.access_token = ACCESS_TOKEN
|
15
|
+
flickr.access_secret = ACCESS_SECRET
|
21
16
|
|
22
17
|
flickr.upload_photo PHOTO_PATH, :title => 'Title', :description => 'This is the description'
|
data/flickraw_rdoc.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "rdoc"
|
2
2
|
require "rdoc/parser/ruby"
|
3
|
+
require "nokogiri"
|
3
4
|
require "cgi"
|
4
5
|
|
5
6
|
FLICKR_API_URL='http://www.flickr.com/services/api'
|
@@ -66,11 +67,7 @@ module RDoc
|
|
66
67
|
end
|
67
68
|
|
68
69
|
def flickr_method_comment(info)
|
69
|
-
description =
|
70
|
-
# description.gsub!( /<\/?(\w+)>/ ) {|b|
|
71
|
-
# return b if ['em', 'b', 'tt'].include? $1
|
72
|
-
# return ''
|
73
|
-
# }
|
70
|
+
description = Nokogiri::HTML(info.method.description.to_s).text
|
74
71
|
|
75
72
|
if info.respond_to? :arguments
|
76
73
|
args = info.arguments.select { |arg| arg.name != 'api_key' }
|
data/lib/flickraw.rb
CHANGED
@@ -20,30 +20,140 @@
|
|
20
20
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
21
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
|
23
|
-
|
24
23
|
require 'net/http'
|
25
|
-
require 'digest/md5'
|
26
24
|
require 'json'
|
27
25
|
|
28
|
-
FlickRawOptions = {} if not Object.const_defined? :FlickRawOptions # :nodoc:
|
29
|
-
if ENV['http_proxy'] and not FlickRawOptions['proxy_host']
|
30
|
-
proxy = URI.parse ENV['http_proxy']
|
31
|
-
FlickRawOptions.update('proxy_host' => proxy.host, 'proxy_port' => proxy.port, 'proxy_user' => proxy.user, 'proxy_password' => proxy.password)
|
32
|
-
end
|
33
|
-
|
34
26
|
module FlickRaw
|
35
|
-
VERSION='0.
|
27
|
+
VERSION='0.9'
|
28
|
+
USER_AGENT = "Flickraw/#{VERSION}"
|
29
|
+
|
30
|
+
FLICKR_OAUTH_REQUEST_TOKEN='http://www.flickr.com/services/oauth/request_token'.freeze
|
31
|
+
FLICKR_OAUTH_AUTHORIZE='http://www.flickr.com/services/oauth/authorize'.freeze
|
32
|
+
FLICKR_OAUTH_ACCESS_TOKEN='http://www.flickr.com/services/oauth/access_token'.freeze
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
REPLACE_PATH='/services/replace/'.freeze
|
34
|
+
REST_PATH='http://api.flickr.com/services/rest/'.freeze
|
35
|
+
UPLOAD_PATH='http://api.flickr.com/services/upload/'.freeze
|
36
|
+
REPLACE_PATH='http://api.flickr.com/services/replace/'.freeze
|
41
37
|
|
42
|
-
AUTH_PATH='http://flickr.com/services/auth/?'.freeze
|
43
38
|
PHOTO_SOURCE_URL='http://farm%s.static.flickr.com/%s/%s_%s%s.%s'.freeze
|
44
39
|
URL_PROFILE='http://www.flickr.com/people/'.freeze
|
45
40
|
URL_PHOTOSTREAM='http://www.flickr.com/photos/'.freeze
|
46
41
|
URL_SHORT='http://flic.kr/p/'.freeze
|
42
|
+
|
43
|
+
class OAuth # :nodoc: all
|
44
|
+
class FailedResponse < StandardError
|
45
|
+
def initialize(str)
|
46
|
+
@response = OAuth.parse_response(str)
|
47
|
+
super(@response['oauth_problem'])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def escape(v); URI.escape(v.to_s, /[^a-zA-Z0-9\-\.\_\~]/) end
|
53
|
+
def parse_response(text); Hash[text.split("&").map {|s| s.split("=")}] end
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_accessor :user_agent
|
57
|
+
attr_reader :proxy
|
58
|
+
def proxy=(url); @proxy = URI.parse(url || '') end
|
59
|
+
|
60
|
+
def initialize(consumer_key, consumer_secret)
|
61
|
+
@consumer_key, @consumer_secret = consumer_key, consumer_secret
|
62
|
+
self.proxy = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def sign(method, url, params, token_secret = nil, consumer_secret = @consumer_secret)
|
66
|
+
params_norm = params.map {|k,v| OAuth.escape(k) + "=" + OAuth.escape(v) }.sort.join("&")
|
67
|
+
text = method.to_s.upcase + "&" + OAuth.escape(url) + "&" + OAuth.escape(params_norm)
|
68
|
+
key = consumer_secret.to_s + "&" + token_secret.to_s
|
69
|
+
digest = OpenSSL::Digest::Digest.new("sha1")
|
70
|
+
[OpenSSL::HMAC.digest(digest, key, text)].pack('m0').gsub(/\n$/,'')
|
71
|
+
end
|
72
|
+
|
73
|
+
def authorization_header(url, params)
|
74
|
+
params_norm = params.map {|k,v| OAuth.escape(k) + "=\"" + OAuth.escape(v) + "\""}.sort.join(", ")
|
75
|
+
"OAuth realm=\"" + url.to_s + "\", " + params_norm
|
76
|
+
end
|
77
|
+
|
78
|
+
def gen_timestamp; Time.now.to_i end
|
79
|
+
def gen_nonce; [OpenSSL::Random.random_bytes(32)].pack('m0').gsub(/\n$/,'') end
|
80
|
+
def gen_default_params
|
81
|
+
{ :oauth_version => "1.0", :oauth_signature_method => "HMAC-SHA1",
|
82
|
+
:oauth_consumer_key => @consumer_key, :oauth_nonce => gen_nonce,
|
83
|
+
:oauth_timestamp => gen_timestamp }
|
84
|
+
end
|
85
|
+
|
86
|
+
def request_token(url, oauth_params = {})
|
87
|
+
r = post_form(url, nil, {:oauth_callback => "oob"}.merge(oauth_params))
|
88
|
+
OAuth.parse_response(r.body)
|
89
|
+
end
|
90
|
+
|
91
|
+
def authorize_url(url, oauth_params = {})
|
92
|
+
params_norm = oauth_params.map {|k,v| OAuth.escape(k) + "=" + OAuth.escape(v)}.sort.join("&")
|
93
|
+
url = URI.parse(url)
|
94
|
+
url.query = url.query ? url.query + "&" + params_norm : params_norm
|
95
|
+
url.to_s
|
96
|
+
end
|
97
|
+
|
98
|
+
def access_token(url, token_secret, oauth_params = {})
|
99
|
+
r = post_form(url, token_secret, oauth_params)
|
100
|
+
OAuth.parse_response(r.body)
|
101
|
+
end
|
102
|
+
|
103
|
+
def post_form(url, token_secret, oauth_params = {}, params = {})
|
104
|
+
oauth_params = gen_default_params.merge(oauth_params)
|
105
|
+
oauth_params[:oauth_signature] = sign(:post, url, params.merge(oauth_params), token_secret)
|
106
|
+
url = URI.parse(url)
|
107
|
+
r = Net::HTTP.start(url.host, url.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password) { |http|
|
108
|
+
request = Net::HTTP::Post.new(url.path)
|
109
|
+
request['User-Agent'] = @user_agent if @user_agent
|
110
|
+
request['Authorization'] = authorization_header(url, oauth_params)
|
111
|
+
request.form_data = params
|
112
|
+
http.request(request)
|
113
|
+
}
|
114
|
+
|
115
|
+
raise FailedResponse.new(r.body) if r.is_a? Net::HTTPClientError
|
116
|
+
r
|
117
|
+
end
|
118
|
+
|
119
|
+
def post_multipart(url, token_secret, oauth_params = {}, params = {})
|
120
|
+
oauth_params = gen_default_params.merge(oauth_params)
|
121
|
+
params_signed = params.reject {|k,v| v.is_a? File}.merge(oauth_params)
|
122
|
+
oauth_params[:oauth_signature] = sign(:post, url, params_signed, token_secret)
|
123
|
+
url = URI.parse(url)
|
124
|
+
r = Net::HTTP.start(url.host, url.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password) { |http|
|
125
|
+
boundary = "FlickRaw#{gen_nonce}"
|
126
|
+
request = Net::HTTP::Post.new(url.path)
|
127
|
+
request['User-Agent'] = @user_agent if @user_agent
|
128
|
+
request['Content-type'] = "multipart/form-data, boundary=#{boundary}"
|
129
|
+
request['Authorization'] = authorization_header(url, oauth_params)
|
130
|
+
|
131
|
+
request.body = ''
|
132
|
+
params.each { |k, v|
|
133
|
+
if v.is_a? File
|
134
|
+
basename = File.basename(v.path).to_s
|
135
|
+
basename = basename.encode("utf-8").force_encoding("ascii-8bit") if RUBY_VERSION >= "1.9"
|
136
|
+
filename = basename
|
137
|
+
request.body << "--#{boundary}\r\n" <<
|
138
|
+
"Content-Disposition: form-data; name=\"#{k}\"; filename=\"#{filename}\"\r\n" <<
|
139
|
+
"Content-Transfer-Encoding: binary\r\n" <<
|
140
|
+
"Content-Type: image/jpeg\r\n\r\n" <<
|
141
|
+
v.read << "\r\n"
|
142
|
+
else
|
143
|
+
request.body << "--#{boundary}\r\n" <<
|
144
|
+
"Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n" <<
|
145
|
+
"#{v}\r\n"
|
146
|
+
end
|
147
|
+
}
|
148
|
+
|
149
|
+
request.body << "--#{boundary}--"
|
150
|
+
http.request(request)
|
151
|
+
}
|
152
|
+
|
153
|
+
raise FailedResponse.new(r.body) if r.is_a? Net::HTTPClientError
|
154
|
+
r
|
155
|
+
end
|
156
|
+
end
|
47
157
|
|
48
158
|
class Response
|
49
159
|
def self.build(h, type) # :nodoc:
|
@@ -89,6 +199,7 @@ module FlickRaw
|
|
89
199
|
def inspect; @a.inspect end
|
90
200
|
def size; @a.size end
|
91
201
|
def marshal_dump; [@h, @flickr_type, @a] end
|
202
|
+
alias length size
|
92
203
|
end
|
93
204
|
|
94
205
|
class FailedResponse < StandardError
|
@@ -150,25 +261,53 @@ module FlickRaw
|
|
150
261
|
|
151
262
|
# Root class of the flickr api hierarchy.
|
152
263
|
class Flickr < Request
|
264
|
+
# Authenticated access token
|
265
|
+
attr_accessor :access_token
|
266
|
+
|
267
|
+
# Authenticated access token secret
|
268
|
+
attr_accessor :access_secret
|
269
|
+
|
153
270
|
def self.build(methods); methods.each { |m| build_request m } end
|
154
271
|
|
155
|
-
def initialize
|
272
|
+
def initialize # :nodoc:
|
273
|
+
raise "No API key or secret defined !" if FlickRaw.api_key.nil? or FlickRaw.shared_secret.nil?
|
274
|
+
@oauth_consumer = OAuth.new(FlickRaw.api_key, FlickRaw.shared_secret)
|
275
|
+
@oauth_consumer.proxy = FlickRaw.proxy
|
276
|
+
@oauth_consumer.user_agent = USER_AGENT
|
277
|
+
|
156
278
|
Flickr.build(call('flickr.reflection.getMethods')) if Flickr.flickr_objects.empty?
|
157
279
|
super self
|
158
|
-
@token = token
|
159
280
|
end
|
160
|
-
|
281
|
+
|
161
282
|
# This is the central method. It does the actual request to the flickr server.
|
162
283
|
#
|
163
284
|
# Raises FailedResponse if the response status is _failed_.
|
164
285
|
def call(req, args={}, &block)
|
165
|
-
|
166
|
-
http_response
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
286
|
+
http_response = @oauth_consumer.post_form(REST_PATH, @access_secret, {:oauth_token => @access_token}, build_args(args, req))
|
287
|
+
process_response(req, http_response.body)
|
288
|
+
end
|
289
|
+
|
290
|
+
# Get an oauth request token.
|
291
|
+
#
|
292
|
+
# token = flickr.get_request_token(:oauth_callback => "http://example.com")
|
293
|
+
def get_request_token(args = {})
|
294
|
+
request_token = @oauth_consumer.request_token(FLICKR_OAUTH_REQUEST_TOKEN, args)
|
295
|
+
end
|
296
|
+
|
297
|
+
# Get the oauth authorize url.
|
298
|
+
#
|
299
|
+
# auth_url = flickr.get_authorize_url(token['oauth_token'], :perms => 'delete')
|
300
|
+
def get_authorize_url(token, args = {})
|
301
|
+
@oauth_consumer.authorize_url(FLICKR_OAUTH_AUTHORIZE, args.merge(:oauth_token => token))
|
302
|
+
end
|
303
|
+
|
304
|
+
# Get an oauth access token.
|
305
|
+
#
|
306
|
+
# flickr.get_access_token(token['oauth_token'], token['oauth_token_secret'], oauth_verifier)
|
307
|
+
def get_access_token(token, secret, verify)
|
308
|
+
access_token = @oauth_consumer.access_token(FLICKR_OAUTH_ACCESS_TOKEN, secret, :oauth_token => token, :oauth_verifier => verify)
|
309
|
+
@access_token, @access_secret = access_token['oauth_token'], access_token['oauth_token_secret']
|
310
|
+
access_token
|
172
311
|
end
|
173
312
|
|
174
313
|
# Use this to upload the photo in _file_.
|
@@ -186,97 +325,58 @@ module FlickRaw
|
|
186
325
|
def replace_photo(file, args={}); upload_flickr(REPLACE_PATH, file, args) end
|
187
326
|
|
188
327
|
private
|
189
|
-
def build_args(args={},
|
190
|
-
full_args = {
|
191
|
-
full_args[
|
192
|
-
full_args[:auth_token] = @token if @token
|
328
|
+
def build_args(args={}, method = nil)
|
329
|
+
full_args = {'format' => 'json', :nojsoncallback => "1"}
|
330
|
+
full_args['method'] = method if method
|
193
331
|
args.each {|k, v|
|
194
332
|
v = v.to_s.encode("utf-8").force_encoding("ascii-8bit") if RUBY_VERSION >= "1.9"
|
195
|
-
full_args[k.
|
333
|
+
full_args[k.to_s] = v
|
196
334
|
}
|
197
|
-
full_args[:api_sig] = FlickRaw.api_sig(full_args) if FlickRaw.shared_secret
|
198
335
|
full_args
|
199
336
|
end
|
200
337
|
|
201
|
-
def process_response(req,
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
338
|
+
def process_response(req, response)
|
339
|
+
if response =~ /^<\?xml / # upload_photo returns xml data whatever we ask
|
340
|
+
if response[/stat="(\w+)"/, 1] == 'fail'
|
341
|
+
msg = response[/msg="([^"]+)"/, 1]
|
342
|
+
code = response[/code="([^"]+)"/, 1]
|
343
|
+
raise FailedResponse.new(msg, code, req)
|
344
|
+
end
|
345
|
+
|
346
|
+
type = response[/<(\w+)/, 1]
|
347
|
+
h = {
|
348
|
+
"secret" => response[/secret="([^"]+)"/, 1],
|
349
|
+
"originalsecret" => response[/originalsecret="([^"]+)"/, 1],
|
350
|
+
"_content" => response[/>([^<]+)<\//, 1]
|
351
|
+
}.delete_if {|k,v| v.nil? }
|
352
|
+
|
353
|
+
Response.build h, type
|
354
|
+
else
|
355
|
+
json = JSON.load(response.empty? ? "{}" : response)
|
356
|
+
raise FailedResponse.new(json['message'], json['code'], req) if json.delete('stat') == 'fail'
|
357
|
+
type, json = json.to_a.first if json.size == 1 and json.all? {|k,v| v.is_a? Hash}
|
210
358
|
|
211
|
-
|
212
|
-
|
213
|
-
http.read_timeout = FlickRawOptions['timeout'] if FlickRawOptions.key?('timeout')
|
214
|
-
yield http
|
215
|
-
}
|
359
|
+
Response.build json, type
|
360
|
+
end
|
216
361
|
end
|
217
362
|
|
218
363
|
def upload_flickr(method, file, args={})
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
query = ''
|
224
|
-
|
225
|
-
file = file.to_s.encode("utf-8").force_encoding("ascii-8bit") if RUBY_VERSION >= "1.9"
|
226
|
-
build_args(args).each { |a, v|
|
227
|
-
query <<
|
228
|
-
"--#{boundary}\r\n" <<
|
229
|
-
"Content-Disposition: form-data; name=\"#{a}\"\r\n\r\n" <<
|
230
|
-
"#{v}\r\n"
|
231
|
-
}
|
232
|
-
query <<
|
233
|
-
"--#{boundary}\r\n" <<
|
234
|
-
"Content-Disposition: form-data; name=\"photo\"; filename=\"#{file}\"\r\n" <<
|
235
|
-
"Content-Transfer-Encoding: binary\r\n" <<
|
236
|
-
"Content-Type: image/jpeg\r\n\r\n" <<
|
237
|
-
photo <<
|
238
|
-
"\r\n" <<
|
239
|
-
"--#{boundary}--"
|
240
|
-
|
241
|
-
http_response = open_flickr {|http| http.post(method, query, header) }
|
242
|
-
xml = http_response.body
|
243
|
-
if xml[/stat="(\w+)"/, 1] == 'fail'
|
244
|
-
msg = xml[/msg="([^"]+)"/, 1]
|
245
|
-
code = xml[/code="([^"]+)"/, 1]
|
246
|
-
raise FailedResponse.new(msg, code, 'flickr.upload')
|
247
|
-
end
|
248
|
-
type = xml[/<(\w+)/, 1]
|
249
|
-
h = {
|
250
|
-
"secret" => xml[/secret="([^"]+)"/, 1],
|
251
|
-
"originalsecret" => xml[/originalsecret="([^"]+)"/, 1],
|
252
|
-
"_content" => xml[/>([^<]+)<\//, 1]
|
253
|
-
}.delete_if {|k,v| v.nil? }
|
254
|
-
Response.build(h, type)
|
364
|
+
args = build_args(args)
|
365
|
+
args['photo'] = open(file, 'rb')
|
366
|
+
http_response = @oauth_consumer.post_multipart(method, @access_secret, {:oauth_token => @access_token}, args)
|
367
|
+
process_response(method, http_response.body)
|
255
368
|
end
|
256
369
|
end
|
257
370
|
|
258
371
|
class << self
|
259
372
|
# Your flickr API key, see http://www.flickr.com/services/api/keys for more information
|
260
|
-
|
261
|
-
|
262
|
-
|
373
|
+
attr_accessor :api_key
|
374
|
+
|
263
375
|
# The shared secret of _api_key_, see http://www.flickr.com/services/api/keys for more information
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
def auth_url(args={})
|
269
|
-
full_args = {:api_key => api_key, :perms => 'read'}
|
270
|
-
args.each {|k, v| full_args[k.to_sym] = v }
|
271
|
-
full_args[:api_sig] = api_sig(full_args) if shared_secret
|
272
|
-
|
273
|
-
AUTH_PATH + full_args.collect { |a, v| "#{a}=#{v}" }.join('&')
|
274
|
-
end
|
275
|
-
|
276
|
-
# Returns the signature of hsh. This is meant to be passed in the _api_sig_ parameter.
|
277
|
-
def api_sig(hsh)
|
278
|
-
Digest::MD5.hexdigest(FlickRaw.shared_secret + hsh.sort{|a, b| a[0].to_s <=> b[0].to_s }.flatten.join)
|
279
|
-
end
|
376
|
+
attr_accessor :shared_secret
|
377
|
+
|
378
|
+
# Use a proxy
|
379
|
+
attr_accessor :proxy
|
280
380
|
|
281
381
|
BASE58_ALPHABET="123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ".freeze
|
282
382
|
def base58(id)
|
@@ -324,9 +424,3 @@ end
|
|
324
424
|
# recent_photos = flickr.photos.getRecent
|
325
425
|
# puts recent_photos[0].title
|
326
426
|
def flickr; $flickraw ||= FlickRaw::Flickr.new end
|
327
|
-
|
328
|
-
# Load the methods if the option lazyload is not specified
|
329
|
-
begin
|
330
|
-
flickr
|
331
|
-
rescue
|
332
|
-
end if not FlickRawOptions['lazyload']
|
data/rakefile
CHANGED
@@ -1,36 +1,15 @@
|
|
1
1
|
require 'rake/clean'
|
2
2
|
require 'rake/rdoctask'
|
3
|
-
require 'rake/packagetask'
|
4
|
-
require 'rake/gempackagetask'
|
5
3
|
require 'rake/testtask'
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
require './flickraw_rdoc' if RUBY_VERSION >= "1.9"
|
5
|
+
lib = File.dirname(__FILE__)
|
6
|
+
$:.unshift lib unless $:.include?(lib)
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
spec = Gem::Specification.new do |s|
|
14
|
-
s.summary = "Flickr library with a syntax close to the syntax described on http://www.flickr.com/services/api"
|
15
|
-
s.name = "flickraw"
|
16
|
-
s.author = "Mael Clerambault"
|
17
|
-
s.email = "maelclerambault@yahoo.fr"
|
18
|
-
s.homepage = "http://hanklords.github.com/flickraw/"
|
19
|
-
s.version = FlickRaw::VERSION
|
20
|
-
s.files = PKG_FILES
|
21
|
-
s.add_dependency 'json', '>= 1.1.1'
|
22
|
-
end
|
23
|
-
|
24
|
-
Rake::GemPackageTask.new(spec).define
|
25
|
-
|
26
|
-
desc "Create default gemspec file"
|
27
|
-
file "flickraw.gemspec" do
|
28
|
-
open("flickraw.gemspec", "w") {|g| g.puts spec.to_ruby }
|
29
|
-
end
|
30
|
-
task :gem => ["flickraw.gemspec"]
|
31
|
-
CLOBBER.add "flickraw.gemspec"
|
8
|
+
require 'lib/flickraw'
|
9
|
+
require 'flickraw_rdoc' if RUBY_VERSION >= "1.9"
|
32
10
|
|
33
11
|
Rake::RDocTask.new do |rd|
|
12
|
+
rd.main = "README.rdoc"
|
34
13
|
rd.rdoc_files.include "README.rdoc", "lib/flickraw.rb"
|
35
14
|
end
|
36
15
|
|
data/test/test.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
+
lib = File.expand_path('../../lib/', __FILE__)
|
4
|
+
$:.unshift lib unless $:.include?(lib)
|
5
|
+
|
3
6
|
require 'test/unit'
|
4
7
|
require 'flickraw'
|
5
8
|
|
9
|
+
# FlickRaw.shared_secret = # Shared secret
|
10
|
+
# flickr.auth.checkToken :auth_token => # Auth token
|
11
|
+
|
6
12
|
class Basic < Test::Unit::TestCase
|
7
13
|
def test_request
|
8
14
|
flickr_objects = %w{activity auth blogs collections commons contacts
|
9
15
|
favorites galleries groups interestingness machinetags panda
|
10
|
-
people photos photosets places prefs reflection stats tags
|
16
|
+
people photos photosets places prefs push reflection stats tags
|
11
17
|
test urls
|
12
18
|
}
|
13
19
|
assert_equal FlickRaw::Flickr.flickr_objects, flickr_objects
|
@@ -25,6 +31,7 @@ class Basic < Test::Unit::TestCase
|
|
25
31
|
flickr.auth.getFrob
|
26
32
|
flickr.auth.getFullToken
|
27
33
|
flickr.auth.getToken
|
34
|
+
flickr.auth.oauth.getAccessToken
|
28
35
|
flickr.blogs.getList
|
29
36
|
flickr.blogs.getServices
|
30
37
|
flickr.blogs.postPhoto
|
@@ -35,6 +42,7 @@ class Basic < Test::Unit::TestCase
|
|
35
42
|
flickr.contacts.getListRecentlyUploaded
|
36
43
|
flickr.contacts.getPublicList
|
37
44
|
flickr.favorites.add
|
45
|
+
flickr.favorites.getContext
|
38
46
|
flickr.favorites.getList
|
39
47
|
flickr.favorites.getPublicList
|
40
48
|
flickr.favorites.remove
|
@@ -162,6 +170,10 @@ class Basic < Test::Unit::TestCase
|
|
162
170
|
flickr.prefs.getHidden
|
163
171
|
flickr.prefs.getPrivacy
|
164
172
|
flickr.prefs.getSafetyLevel
|
173
|
+
flickr.push.getSubscriptions
|
174
|
+
flickr.push.getTopics
|
175
|
+
flickr.push.subscribe
|
176
|
+
flickr.push.unsubscribe
|
165
177
|
flickr.reflection.getMethodInfo
|
166
178
|
flickr.reflection.getMethods
|
167
179
|
flickr.stats.getCollectionDomains
|
@@ -199,7 +211,7 @@ class Basic < Test::Unit::TestCase
|
|
199
211
|
}
|
200
212
|
found_methods = flickr.reflection.getMethods
|
201
213
|
assert_instance_of FlickRaw::ResponseList, found_methods
|
202
|
-
assert_equal known_methods, found_methods.to_a
|
214
|
+
assert_equal known_methods.sort, found_methods.to_a.sort
|
203
215
|
end
|
204
216
|
|
205
217
|
def test_list
|
data/test/test_upload.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
+
lib = File.expand_path('../../lib/', __FILE__)
|
4
|
+
$:.unshift lib unless $:.include?(lib)
|
5
|
+
|
3
6
|
require 'test/unit'
|
4
7
|
require 'flickraw'
|
5
8
|
|
9
|
+
# FlickRaw.api_key = # API key
|
6
10
|
# FlickRaw.shared_secret = # Shared secret
|
7
|
-
# flickr.
|
11
|
+
# flickr.access_token = # Auth token
|
12
|
+
# flickr.access_secret = # Auth token secret
|
8
13
|
|
9
14
|
class Upload < Test::Unit::TestCase
|
10
15
|
def test_upload
|
metadata
CHANGED
@@ -4,9 +4,8 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
|
9
|
-
version: 0.8.4
|
7
|
+
- 9
|
8
|
+
version: "0.9"
|
10
9
|
platform: ruby
|
11
10
|
authors:
|
12
11
|
- Mael Clerambault
|
@@ -14,24 +13,10 @@ autorequire:
|
|
14
13
|
bindir: bin
|
15
14
|
cert_chain: []
|
16
15
|
|
17
|
-
date: 2011-
|
16
|
+
date: 2011-09-20 00:00:00 +02:00
|
18
17
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
21
|
-
name: json
|
22
|
-
prerelease: false
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
-
none: false
|
25
|
-
requirements:
|
26
|
-
- - ">="
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
segments:
|
29
|
-
- 1
|
30
|
-
- 1
|
31
|
-
- 1
|
32
|
-
version: 1.1.1
|
33
|
-
type: :runtime
|
34
|
-
version_requirements: *id001
|
18
|
+
dependencies: []
|
19
|
+
|
35
20
|
description:
|
36
21
|
email: maelclerambault@yahoo.fr
|
37
22
|
executables: []
|
@@ -41,17 +26,18 @@ extensions: []
|
|
41
26
|
extra_rdoc_files: []
|
42
27
|
|
43
28
|
files:
|
44
|
-
- lib/flickraw.rb
|
45
|
-
- flickraw_rdoc.rb
|
46
|
-
- LICENSE
|
47
|
-
- README.rdoc
|
48
|
-
- rakefile
|
49
|
-
- examples/flickr_KDE.rb
|
50
29
|
- examples/auth.rb
|
51
30
|
- examples/interestingness.rb
|
31
|
+
- examples/search.rb
|
32
|
+
- examples/sinatra.rb
|
52
33
|
- examples/upload.rb
|
53
34
|
- test/test_upload.rb
|
54
35
|
- test/test.rb
|
36
|
+
- lib/flickraw.rb
|
37
|
+
- flickraw_rdoc.rb
|
38
|
+
- LICENSE
|
39
|
+
- README.rdoc
|
40
|
+
- rakefile
|
55
41
|
has_rdoc: true
|
56
42
|
homepage: http://hanklords.github.com/flickraw/
|
57
43
|
licenses: []
|
data/examples/flickr_KDE.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
# Chooses a photo from the current interesting
|
3
|
-
# photo and set it as the background image on
|
4
|
-
# your first KDE desktop.
|
5
|
-
|
6
|
-
require 'flickraw'
|
7
|
-
require 'open-uri'
|
8
|
-
DESKTOP=1
|
9
|
-
|
10
|
-
list = flickr.interestingness.getList
|
11
|
-
photo = list[rand(100)]
|
12
|
-
sizes = flickr.photos.getSizes(:photo_id => photo.id)
|
13
|
-
original = sizes.find {|s| s.label == 'Original' }
|
14
|
-
|
15
|
-
url = original.source
|
16
|
-
file = File.basename url
|
17
|
-
full_path = File.join(Dir.pwd, file)
|
18
|
-
|
19
|
-
open url do |remote|
|
20
|
-
open(file, 'wb') { |local| local << remote.read }
|
21
|
-
end
|
22
|
-
|
23
|
-
`dcop kdesktop KBackgroundIface setWallpaper #{DESKTOP} #{full_path} 7`
|