flickraw 0.8.4 → 0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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`
|