flickr-fu 0.1.3
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/lib/flickr/auth.rb +69 -0
- data/lib/flickr/base.rb +94 -0
- data/lib/flickr/comment.rb +16 -0
- data/lib/flickr/note.rb +16 -0
- data/lib/flickr/photo.rb +266 -0
- data/lib/flickr/photo_response.rb +37 -0
- data/lib/flickr/photos.rb +205 -0
- data/lib/flickr/size.rb +16 -0
- data/lib/flickr/token.rb +22 -0
- data/lib/flickr/uploader.rb +106 -0
- data/lib/flickr_fu.rb +23 -0
- metadata +72 -0
data/lib/flickr/auth.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
class Flickr::Auth < Flickr::Base
|
2
|
+
def initialize(flickr)
|
3
|
+
@flickr = flickr
|
4
|
+
end
|
5
|
+
|
6
|
+
# get or return a frob to use for authentication
|
7
|
+
def frob
|
8
|
+
@frob ||= get_frob
|
9
|
+
end
|
10
|
+
|
11
|
+
# generates the authorization url to allow access to a flickr account.
|
12
|
+
#
|
13
|
+
# Params
|
14
|
+
# * perms (Optional)
|
15
|
+
# sets the permision level to grant on the flickr account.
|
16
|
+
# :read - permission to read private information (DEFAULT)
|
17
|
+
# :write - permission to add, edit and delete photo metadata (includes 'read')
|
18
|
+
# :delete - permission to delete photos (includes 'write' and 'read')
|
19
|
+
#
|
20
|
+
def url(perms = :read)
|
21
|
+
options = {:api_key => @flickr.api_key, :perms => perms, :frob => self.frob}
|
22
|
+
@flickr.sign_request(options)
|
23
|
+
Flickr::Base::AUTH_ENDPOINT + "?" + options.collect{|k,v| "#{k}=#{v}"}.join('&')
|
24
|
+
end
|
25
|
+
|
26
|
+
# gets the token object for the current frob
|
27
|
+
#
|
28
|
+
# Params
|
29
|
+
# * pass_through (Optional)
|
30
|
+
# Boolean value that determines if a call will be made to flickr to find a taken for the current frob if empty
|
31
|
+
#
|
32
|
+
def token(pass_through = true)
|
33
|
+
@token ||= get_token(pass_through) rescue nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# saves the current token to the cache file if token exists
|
37
|
+
#
|
38
|
+
# Param
|
39
|
+
# * filename (Optional)
|
40
|
+
# filename of the cache file. defaults to the file passed into Flickr.new
|
41
|
+
#
|
42
|
+
def cache_token(filename = @flickr.token_cache)
|
43
|
+
if filename and self.token
|
44
|
+
cache_file = File.open(filename, 'w+')
|
45
|
+
cache_file.puts self.token.to_yaml
|
46
|
+
cache_file.close
|
47
|
+
true
|
48
|
+
else
|
49
|
+
false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def get_frob
|
55
|
+
rsp = @flickr.send_request('flickr.auth.getFrob')
|
56
|
+
|
57
|
+
rsp.frob.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_token(pass_through)
|
61
|
+
if @flickr.token_cache and File.exists?(@flickr.token_cache)
|
62
|
+
YAML.load_file(@flickr.token_cache)
|
63
|
+
elsif pass_through
|
64
|
+
rsp = @flickr.send_request('flickr.auth.getToken', {:frob => self.frob})
|
65
|
+
|
66
|
+
Token.new(:token => rsp.auth.token.to_s, :permisions => rsp.auth.perms.to_s, :user_id => rsp.auth.user[:nsid], :username => rsp.auth.user[:username], :user_real_name => rsp.auth.user[:fullname])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/flickr/base.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module Flickr
|
2
|
+
def self.new(*params)
|
3
|
+
Flickr::Base.new(*params)
|
4
|
+
end
|
5
|
+
|
6
|
+
class Base
|
7
|
+
attr_reader :api_key, :api_secret, :token_cache
|
8
|
+
|
9
|
+
REST_ENDPOINT = 'http://api.flickr.com/services/rest/'
|
10
|
+
AUTH_ENDPOINT = 'http://flickr.com/services/auth/'
|
11
|
+
UPLOAD_ENDPOINT = 'http://api.flickr.com/services/upload/'
|
12
|
+
|
13
|
+
# create a new flickr object
|
14
|
+
#
|
15
|
+
# Params
|
16
|
+
# * config_file (Required)
|
17
|
+
# yaml file to load configuration from
|
18
|
+
# * token_cache (Optional)
|
19
|
+
# location of the token cache file. This will override the setting in the config file
|
20
|
+
#
|
21
|
+
# Config Example (yaml file)
|
22
|
+
#
|
23
|
+
# ---
|
24
|
+
# key: YOUR_API_KEY
|
25
|
+
# secret: YOUR_API_SECRET
|
26
|
+
# token_cache: token.yml
|
27
|
+
#
|
28
|
+
def initialize(config_file, token_cache = nil)
|
29
|
+
config = YAML.load_file(config_file)
|
30
|
+
|
31
|
+
@api_key = config['key']
|
32
|
+
@api_secret = config['secret']
|
33
|
+
@token_cache = token_cache || config['token_cache']
|
34
|
+
|
35
|
+
raise 'flickr config file must contain an api key and secret' unless @api_key and @api_secret
|
36
|
+
end
|
37
|
+
|
38
|
+
# sends a request to the flcikr REST api
|
39
|
+
#
|
40
|
+
# Params
|
41
|
+
# * method (Required)
|
42
|
+
# name of the flickr method (ex. flickr.photos.search)
|
43
|
+
# * options (Optional)
|
44
|
+
# hash of query parameters, you do not need to include api_key, api_sig or auth_token because these are added automatically
|
45
|
+
# * http_method (Optional)
|
46
|
+
# choose between a GET and POST http request. Valid options are:
|
47
|
+
# :get (DEFAULT)
|
48
|
+
# :post
|
49
|
+
# * endpoint (Optional)
|
50
|
+
# url of the api endpoint
|
51
|
+
#
|
52
|
+
def send_request(method, options = {}, http_method = :get, endpoint = REST_ENDPOINT)
|
53
|
+
options.merge!(:api_key => @api_key, :method => method)
|
54
|
+
sign_request(options)
|
55
|
+
|
56
|
+
if http_method == :get
|
57
|
+
api_call = endpoint + "?" + options.collect{|k,v| "#{k}=#{v}"}.join('&')
|
58
|
+
rsp = Net::HTTP.get(URI.parse(api_call))
|
59
|
+
else
|
60
|
+
rsp = Net::HTTP.post_form(URI.parse(REST_ENDPOINT), options).body
|
61
|
+
end
|
62
|
+
|
63
|
+
xm = XmlMagic.new(rsp)
|
64
|
+
|
65
|
+
if xm[:stat] == 'ok'
|
66
|
+
xm
|
67
|
+
else
|
68
|
+
raise "#{xm.err[:code]}: #{xm.err[:msg]}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# alters your api parameters to include a signiture and authorization token
|
73
|
+
#
|
74
|
+
# Params
|
75
|
+
# * options (Required)
|
76
|
+
# the hash of parameters to be passed to the send_request
|
77
|
+
# * authorize (Optional)
|
78
|
+
# boolean value to determine if the call with include an auth_token (Defaults to true)
|
79
|
+
#
|
80
|
+
def sign_request(options, authorize = true)
|
81
|
+
options.merge!(:auth_token => self.auth.token(false).to_s) if authorize and self.auth.token(false)
|
82
|
+
options.merge!(:api_sig => Digest::MD5.hexdigest(@api_secret + options.keys.sort_by{|k| k.to_s}.collect{|k| k.to_s + options[k].to_s}.join)) if @api_secret
|
83
|
+
end
|
84
|
+
|
85
|
+
# creates and/or returns the Flickr::Photos object
|
86
|
+
def photos() @photos ||= Photos.new(self) end
|
87
|
+
|
88
|
+
# creates and/or returns the Flickr::Auth object
|
89
|
+
def auth() @auth ||= Auth.new(self) end
|
90
|
+
|
91
|
+
# creates and/or returns the Flickr::Uploader object
|
92
|
+
def uploader() @uploader ||= Uploader.new(self) end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# wrapping class to hold a flickr comment
|
2
|
+
#
|
3
|
+
class Flickr::Photos::Comment
|
4
|
+
attr_accessor :id, :comment, :author, :author_name, :created_at, :permalink
|
5
|
+
|
6
|
+
# create a new instance of a flickr comment.
|
7
|
+
#
|
8
|
+
# Params
|
9
|
+
# * attributes (Required)
|
10
|
+
# a hash of attributes used to set the initial values of the comment object
|
11
|
+
def initialize(attributes)
|
12
|
+
attributes.each do |k,v|
|
13
|
+
send("#{k}=", v)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/flickr/note.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# wrapping class to hold a flickr note
|
2
|
+
#
|
3
|
+
class Flickr::Photos::Note
|
4
|
+
attr_accessor :id, :note, :author, :author_name, :x, :y, :width, :height
|
5
|
+
|
6
|
+
# create a new instance of a flickr note.
|
7
|
+
#
|
8
|
+
# Params
|
9
|
+
# * attributes (Required)
|
10
|
+
# a hash of attributes used to set the initial values of the note object
|
11
|
+
def initialize(attributes)
|
12
|
+
attributes.each do |k,v|
|
13
|
+
send("#{k}=", v)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/flickr/photo.rb
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
# wrapping class to hold an flickr photo
|
2
|
+
#
|
3
|
+
class Flickr::Photos::Photo
|
4
|
+
attr_accessor :id, :owner, :secret, :server, :farm, :title, :is_public, :is_friend, :is_family # standard attributes
|
5
|
+
attr_accessor :license, :uploaded_at, :taken_at, :owner_name, :icon_server, :original_format, :updated_at, :geo, :tags, :machine_tags, :o_dims, :views # extra attributes
|
6
|
+
attr_accessor :info_added, :description, :original_secret, :owner_username, :owner_realname, :url_photopage, :notes # info attributes
|
7
|
+
attr_accessor :sizes_added, :sizes, :url_square, :url_thumbnail, :url_small, :url_medium, :url_large, :url_original # size attributes
|
8
|
+
attr_accessor :comments_added, :comments # comment attributes
|
9
|
+
|
10
|
+
# create a new instance of a flickr photo.
|
11
|
+
#
|
12
|
+
# Params
|
13
|
+
# * flickr (Required)
|
14
|
+
# the flickr object
|
15
|
+
# * attributes (Required)
|
16
|
+
# a hash of attributes used to set the initial values of the photo object
|
17
|
+
def initialize(flickr, attributes)
|
18
|
+
@flickr = flickr
|
19
|
+
attributes.each do |k,v|
|
20
|
+
send("#{k}=", v)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# retreive the url to the image stored on flickr
|
25
|
+
#
|
26
|
+
# == Params
|
27
|
+
# * size (Optional)
|
28
|
+
# the size of the image to return. Optional sizes are:
|
29
|
+
# :square - square 75x75
|
30
|
+
# :thumbnail - 100 on longest side
|
31
|
+
# :small - 240 on longest side
|
32
|
+
# :medium - 500 on longest side
|
33
|
+
# :large - 1024 on longest side (only exists for very large original images)
|
34
|
+
# :original - original image, either a jpg, gif or png, depending on source format
|
35
|
+
#
|
36
|
+
def url(size = :medium)
|
37
|
+
attach_sizes
|
38
|
+
send("url_#{size}")
|
39
|
+
end
|
40
|
+
|
41
|
+
# save the current photo to the local computer
|
42
|
+
#
|
43
|
+
# == Params
|
44
|
+
# * filename (Required)
|
45
|
+
# name of the new file omiting the extention (ex. photo_1)
|
46
|
+
# * size (Optional)
|
47
|
+
# the size of the image to return. Optional sizes are:
|
48
|
+
# :small - square 75x75
|
49
|
+
# :thumbnail - 100 on longest side
|
50
|
+
# :small - 240 on longest side
|
51
|
+
# :medium - 500 on longest side
|
52
|
+
# :large - 1024 on longest side (only exists for very large original images)
|
53
|
+
# :original - original image, either a jpg, gif or png, depending on source format
|
54
|
+
#
|
55
|
+
def save_as(filename, size = :medium)
|
56
|
+
format = size.to_sym == :original ? self.original_format : 'jpg'
|
57
|
+
filename = "#{filename}.#{format}"
|
58
|
+
|
59
|
+
if File.exists?(filename) or not self.url(size)
|
60
|
+
false
|
61
|
+
else
|
62
|
+
f = File.new(filename, 'w+')
|
63
|
+
f.puts open(self.url(size)).read
|
64
|
+
f.close
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Add tags to a photo.
|
70
|
+
#
|
71
|
+
# Params
|
72
|
+
# * tags (Required)
|
73
|
+
# comma seperated list of tags
|
74
|
+
#
|
75
|
+
def add_tags(tags)
|
76
|
+
rsp = @flickr.send_request('flickr.photos.addTags', {:photo_id => self.id, :tags => tags}, :post)
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add comment to a photo as the currently authenticated user.
|
81
|
+
#
|
82
|
+
# Params
|
83
|
+
# * message (Required)
|
84
|
+
# text of the comment
|
85
|
+
#
|
86
|
+
def add_comment(message)
|
87
|
+
rsp = @flickr.send_request('flickr.photos.comments.addComment', {:photo_id => self.id, :comment_text => message}, :post)
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
# Add a note to a photo. Coordinates and sizes are in pixels, based on the 500px image size shown on individual photo pages.
|
92
|
+
#
|
93
|
+
# Params
|
94
|
+
# * message (Required)
|
95
|
+
# The text of the note
|
96
|
+
# * x (Required)
|
97
|
+
# The left coordinate of the note
|
98
|
+
# * y (Required)
|
99
|
+
# The top coordinate of the note
|
100
|
+
# * w (Required)
|
101
|
+
# The width of the note
|
102
|
+
# * h (Required)
|
103
|
+
# The height of the note
|
104
|
+
#
|
105
|
+
def add_note(message, x, y, w, h)
|
106
|
+
rsp = @flickr.send_request('flickr.photos.notes.add', {:photo_id => self.id, :note_x => x, :note_y => y, :note_w => w, :note_h => h, :note_text => message}, :post)
|
107
|
+
true
|
108
|
+
end
|
109
|
+
|
110
|
+
def description # :nodoc:
|
111
|
+
attach_info
|
112
|
+
@description
|
113
|
+
end
|
114
|
+
|
115
|
+
def original_secret # :nodoc:
|
116
|
+
attach_info
|
117
|
+
@original_secret
|
118
|
+
end
|
119
|
+
|
120
|
+
def owner_username # :nodoc:
|
121
|
+
attach_info
|
122
|
+
@owner_username
|
123
|
+
end
|
124
|
+
|
125
|
+
def owner_realname # :nodoc:
|
126
|
+
attach_info
|
127
|
+
@owner_realname
|
128
|
+
end
|
129
|
+
|
130
|
+
def url_photopage # :nodoc:
|
131
|
+
attach_info
|
132
|
+
@url_photopage
|
133
|
+
end
|
134
|
+
|
135
|
+
def comments # :nodoc:
|
136
|
+
attach_comments
|
137
|
+
@comments
|
138
|
+
end
|
139
|
+
|
140
|
+
def sizes # :nodoc:
|
141
|
+
attach_sizes
|
142
|
+
@sizes
|
143
|
+
end
|
144
|
+
|
145
|
+
def notes # :nodoc:
|
146
|
+
attach_info
|
147
|
+
@notes
|
148
|
+
end
|
149
|
+
|
150
|
+
protected
|
151
|
+
def url_square # :nodoc:
|
152
|
+
attach_sizes
|
153
|
+
@url_square
|
154
|
+
end
|
155
|
+
|
156
|
+
def url_thumbnail # :nodoc:
|
157
|
+
attach_sizes
|
158
|
+
@url_thumbnail
|
159
|
+
end
|
160
|
+
|
161
|
+
def url_small # :nodoc:
|
162
|
+
attach_sizes
|
163
|
+
@url_small
|
164
|
+
end
|
165
|
+
|
166
|
+
def url_medium # :nodoc:
|
167
|
+
attach_sizes
|
168
|
+
@url_medium
|
169
|
+
end
|
170
|
+
|
171
|
+
def url_large # :nodoc:
|
172
|
+
attach_sizes
|
173
|
+
@url_large
|
174
|
+
end
|
175
|
+
|
176
|
+
def url_original # :nodoc:
|
177
|
+
attach_sizes
|
178
|
+
@url_original
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
attr_accessor :comment_count
|
183
|
+
|
184
|
+
# convert the size to the key used in the flickr url
|
185
|
+
def size_key(size)
|
186
|
+
case size.to_sym
|
187
|
+
when :square : 's'
|
188
|
+
when :thumb, :thumbnail : 't'
|
189
|
+
when :small : 'm'
|
190
|
+
when :medium : '-'
|
191
|
+
when :large : 'b'
|
192
|
+
when :original : 'o'
|
193
|
+
else ''
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# loads photo info when a field is requested that requires additional info
|
198
|
+
def attach_info
|
199
|
+
unless self.info_added
|
200
|
+
rsp = @flickr.send_request('flickr.photos.getInfo', :photo_id => self.id, :secret => self.secret)
|
201
|
+
|
202
|
+
self.info_added = true
|
203
|
+
self.description = rsp.photo.description.to_s
|
204
|
+
self.original_secret = rsp.photo[:originalsecret]
|
205
|
+
self.owner_username = rsp.photo.owner[:username]
|
206
|
+
self.owner_realname = rsp.photo.owner[:realname]
|
207
|
+
self.url_photopage = rsp.photo.urls.url.to_s
|
208
|
+
self.comment_count = rsp.photo.comments.to_s.to_i
|
209
|
+
|
210
|
+
self.notes = []
|
211
|
+
|
212
|
+
rsp.photo.notes.note.each do |note|
|
213
|
+
self.notes << Flickr::Photos::Note.new(:id => note[:id],
|
214
|
+
:note => note.to_s,
|
215
|
+
:author => note[:author],
|
216
|
+
:author_name => note[:authorname],
|
217
|
+
:x => note[:x],
|
218
|
+
:y => note[:y],
|
219
|
+
:width => note[:w],
|
220
|
+
:height => note[:h])
|
221
|
+
end if rsp.photo.notes.note
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# loads picture sizes only after one has been requested
|
226
|
+
def attach_sizes
|
227
|
+
unless self.sizes_added
|
228
|
+
rsp = @flickr.send_request('flickr.photos.getSizes', :photo_id => self.id)
|
229
|
+
|
230
|
+
self.sizes_added = true
|
231
|
+
self.sizes = []
|
232
|
+
|
233
|
+
rsp.sizes.size.each do |size|
|
234
|
+
send("url_#{size[:label].downcase}=", size[:source])
|
235
|
+
|
236
|
+
self.sizes << Flickr::Photos::Size.new(:label => size[:label],
|
237
|
+
:width => size[:width],
|
238
|
+
:height => size[:height],
|
239
|
+
:source => size[:source],
|
240
|
+
:url => size[:url])
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# loads comments once they have been requested
|
246
|
+
def attach_comments
|
247
|
+
if @comment_count == 0
|
248
|
+
self.comments = []
|
249
|
+
self.comments_added = true
|
250
|
+
elsif not self.comments_added
|
251
|
+
rsp = @flickr.send_request('flickr.photos.comments.getList', :photo_id => self.id)
|
252
|
+
|
253
|
+
self.comments = []
|
254
|
+
self.comments_added = true
|
255
|
+
|
256
|
+
rsp.comments.comment.each do |comment|
|
257
|
+
self.comments << Flickr::Photos::Comment.new(:id => comment[:id],
|
258
|
+
:comment => comment.to_s,
|
259
|
+
:author => comment[:author],
|
260
|
+
:author_name => comment[:authorname],
|
261
|
+
:permalink => comment[:permalink],
|
262
|
+
:created_at => (Time.at(comment[:datecreate].to_i) rescue nil))
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# wrapping class to hold a photos response from the flickr api
|
2
|
+
#
|
3
|
+
class Flickr::Photos::PhotoResponse
|
4
|
+
attr_accessor :page, :pages, :per_page, :total, :photos, :api, :method, :options
|
5
|
+
|
6
|
+
# creates an object to hold the search response.
|
7
|
+
#
|
8
|
+
# Params
|
9
|
+
# * attributes (Required)
|
10
|
+
# a hash of attributes used to set the initial values of the response object
|
11
|
+
def initialize(attributes)
|
12
|
+
attributes.each do |k,v|
|
13
|
+
send("#{k}=", v)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add a Flickr::Photos::Photo object to the photos array. It does nothing if you pass a non photo object
|
18
|
+
def <<(photo)
|
19
|
+
self.photos ||= []
|
20
|
+
self.photos << photo if photo.is_a?(Flickr::Photos::Photo)
|
21
|
+
end
|
22
|
+
|
23
|
+
# gets the next page from flickr if there are anymore pages in the current photos object
|
24
|
+
def next_page
|
25
|
+
api.send(self.method.split('.').last, options.merge(:page => self.page.to_i + 1)) if self.page.to_i < self.pages.to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
# gets the previous page from flickr if there is a previous page in the current photos object
|
29
|
+
def previous_page
|
30
|
+
api.send(self.method.split('.').last, options.merge(:page => self.page.to_i - 1)) if self.page.to_i > 1
|
31
|
+
end
|
32
|
+
|
33
|
+
# passes all unknown request to the photos array if it responds to the method
|
34
|
+
def method_missing(method, *args, &block)
|
35
|
+
self.photos.respond_to?(method) ? self.photos.send(method, *args, &block) : super
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
class Flickr::Photos < Flickr::Base
|
2
|
+
def initialize(flickr)
|
3
|
+
@flickr = flickr
|
4
|
+
end
|
5
|
+
|
6
|
+
# Return a list of photos matching some criteria. Only photos visible to the calling user will be returned. To return private or semi-private photos,
|
7
|
+
# the caller must be authenticated with 'read' permissions, and have permission to view the photos. Unauthenticated calls will only return public photos.
|
8
|
+
#
|
9
|
+
# == Authentication
|
10
|
+
# This method does not require authentication.
|
11
|
+
#
|
12
|
+
# == Options
|
13
|
+
# * user_id (Optional)
|
14
|
+
# The NSID of the user who's photo to search. If this parameter isn't passed then everybody's public photos will be searched. A value of "me" will
|
15
|
+
# search against the calling user's photos for authenticated calls.
|
16
|
+
# * tags (Optional)
|
17
|
+
# A comma-delimited list of tags. Photos with one or more of the tags listed will be returned.
|
18
|
+
# * tag_mode (Optional)
|
19
|
+
# Either 'any' for an OR combination of tags, or 'all' for an AND combination. Defaults to 'any' if not specified.
|
20
|
+
# * text (Optional)
|
21
|
+
# A free text search. Photos who's title, description or tags contain the text will be returned.
|
22
|
+
# * min_upload_date (Optional)
|
23
|
+
# Minimum upload date. Photos with an upload date greater than or equal to this value will be returned. The date should be in the form of a unix timestamp.
|
24
|
+
# * max_upload_date (Optional)
|
25
|
+
# Maximum upload date. Photos with an upload date less than or equal to this value will be returned. The date should be in the form of a unix timestamp.
|
26
|
+
# * min_taken_date (Optional)
|
27
|
+
# Minimum taken date. Photos with an taken date greater than or equal to this value will be returned. The date should be in the form of a mysql datetime.
|
28
|
+
# * max_taken_date (Optional)
|
29
|
+
# Maximum taken date. Photos with an taken date less than or equal to this value will be returned. The date should be in the form of a mysql datetime.
|
30
|
+
# * license (Optional)
|
31
|
+
# The license id for photos (for possible values see the flickr.photos.licenses.getInfo method). Multiple licenses may be comma-separated.
|
32
|
+
# * sort (Optional)
|
33
|
+
# The order in which to sort returned photos. Deafults to date-posted-desc. The possible values are: date-posted-asc, date-posted-desc, date-taken-asc,
|
34
|
+
# date-taken-desc, interestingness-desc, interestingness-asc, and relevance.
|
35
|
+
# * privacy_filter (Optional)
|
36
|
+
# Return photos only matching a certain privacy level. This only applies when making an authenticated call to view photos you own. Valid values are:
|
37
|
+
# 1 public photos
|
38
|
+
# 2 private photos visible to friends
|
39
|
+
# 3 private photos visible to family
|
40
|
+
# 4 private photos visible to friends & family
|
41
|
+
# 5 completely private photos
|
42
|
+
# * bbox (Optional)
|
43
|
+
# A comma-delimited list of 4 values defining the Bounding Box of the area that will be searched.
|
44
|
+
#
|
45
|
+
# The 4 values represent the bottom-left corner of the box and the top-right corner, minimum_longitude, minimum_latitude, maximum_longitude, maximum_latitude.
|
46
|
+
#
|
47
|
+
# Longitude has a range of -180 to 180 , latitude of -90 to 90. Defaults to -180, -90, 180, 90 if not specified.
|
48
|
+
#
|
49
|
+
# Unlike standard photo queries, geo (or bounding box) queries will only return 250 results per page.
|
50
|
+
#
|
51
|
+
# Geo queries require some sort of limiting agent in order to prevent the database from crying. This is basically like the check against "parameterless searches"
|
52
|
+
# for queries without a geo component.
|
53
|
+
#
|
54
|
+
# A tag, for instance, is considered a limiting agent as are user defined min_date_taken and min_date_upload parameters — If no limiting factor is passed we
|
55
|
+
# return only photos added in the last 12 hours (though we may extend the limit in the future).
|
56
|
+
# * accuracy (Optional)
|
57
|
+
# Recorded accuracy level of the location information. Current range is 1-16 :
|
58
|
+
# World level is 1
|
59
|
+
# Country is ~3
|
60
|
+
# Region is ~6
|
61
|
+
# City is ~11
|
62
|
+
# Street is ~16
|
63
|
+
# Defaults to maximum value if not specified.
|
64
|
+
# * safe_search (Optional)
|
65
|
+
# Safe search setting:
|
66
|
+
# 1 for safe.
|
67
|
+
# 2 for moderate.
|
68
|
+
# 3 for restricted.
|
69
|
+
# (Please note: Un-authed calls can only see Safe content.)
|
70
|
+
# * content_type (Optional)
|
71
|
+
# Content Type setting:
|
72
|
+
# 1 for photos only.
|
73
|
+
# 2 for screenshots only.
|
74
|
+
# 3 for 'other' only.
|
75
|
+
# 4 for photos and screenshots.
|
76
|
+
# 5 for screenshots and 'other'.
|
77
|
+
# 6 for photos and 'other'.
|
78
|
+
# 7 for photos, screenshots, and 'other' (all).
|
79
|
+
# * machine_tags (Optional)
|
80
|
+
# Aside from passing in a fully formed machine tag, there is a special syntax for searching on specific properties :
|
81
|
+
# Find photos using the 'dc' namespace : "machine_tags" => "dc:"
|
82
|
+
# Find photos with a title in the 'dc' namespace : "machine_tags" => "dc:title="
|
83
|
+
# Find photos titled "mr. camera" in the 'dc' namespace : "machine_tags" => "dc:title=\"mr. camera\"
|
84
|
+
# Find photos whose value is "mr. camera" : "machine_tags" => "*:*=\"mr. camera\""
|
85
|
+
# Find photos that have a title, in any namespace : "machine_tags" => "*:title="
|
86
|
+
# Find photos that have a title, in any namespace, whose value is "mr. camera" : "machine_tags" => "*:title=\"mr. camera\""
|
87
|
+
# Find photos, in the 'dc' namespace whose value is "mr. camera" : "machine_tags" => "dc:*=\"mr. camera\""
|
88
|
+
# Multiple machine tags may be queried by passing a comma-separated list. The number of machine tags you can pass in a single query depends on
|
89
|
+
# the tag mode (AND or OR) that you are querying with. "AND" queries are limited to (16) machine tags. "OR" queries are limited to (8).
|
90
|
+
# * machine_tag_mode (Required)
|
91
|
+
# Either 'any' for an OR combination of tags, or 'all' for an AND combination. Defaults to 'any' if not specified.
|
92
|
+
# * group_id (Optional)
|
93
|
+
# The id of a group who's pool to search. If specified, only matching photos posted to the group's pool will be returned.
|
94
|
+
# * woe_id (Optional)
|
95
|
+
# A 32-bit identifier that uniquely represents spatial entities. (not used if bbox argument is present). Experimental.
|
96
|
+
#
|
97
|
+
# Geo queries require some sort of limiting agent in order to prevent the database from crying. This is basically like the check against "parameterless searches"
|
98
|
+
# for queries without a geo component.
|
99
|
+
#
|
100
|
+
# A tag, for instance, is considered a limiting agent as are user defined min_date_taken and min_date_upload parameters &emdash; If no limiting factor is passed
|
101
|
+
# we return only photos added in the last 12 hours (though we may extend the limit in the future).
|
102
|
+
# * place_id (Optional)
|
103
|
+
# A Flickr place id. (not used if bbox argument is present). Experimental.
|
104
|
+
#
|
105
|
+
# Geo queries require some sort of limiting agent in order to prevent the database from crying. This is basically like the check against "parameterless searches"
|
106
|
+
# for queries without a geo component.
|
107
|
+
#
|
108
|
+
# A tag, for instance, is considered a limiting agent as are user defined min_date_taken and min_date_upload parameters &emdash; If no limiting factor is passed
|
109
|
+
# we return only photos added in the last 12 hours (though we may extend the limit in the future).
|
110
|
+
# * per_page (Optional)
|
111
|
+
# Number of photos to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is 500.
|
112
|
+
# * page (Optional)
|
113
|
+
# The page of results to return. If this argument is omitted, it defaults to 1.
|
114
|
+
#
|
115
|
+
def search(options)
|
116
|
+
options.merge!({:extras => "license,date_upload,date_taken,owner_name,icon_server,original_format,last_update,geo,tags,machine_tags,o_dims,views"})
|
117
|
+
|
118
|
+
rsp = @flickr.send_request('flickr.photos.search', options)
|
119
|
+
|
120
|
+
returning PhotoResponse.new(:page => rsp.photos[:page].to_i,
|
121
|
+
:pages => rsp.photos[:pages].to_i,
|
122
|
+
:per_page => rsp.photos[:perpage].to_i,
|
123
|
+
:total => rsp.photos[:total].to_i,
|
124
|
+
:photos => [], :api => self,
|
125
|
+
:method => 'flickr.photos.search',
|
126
|
+
:options => options) do |photos|
|
127
|
+
rsp.photos.photo.each do |photo|
|
128
|
+
attributes = {:id => photo[:id],
|
129
|
+
:owner => photo[:owner],
|
130
|
+
:secret => photo[:secret],
|
131
|
+
:server => photo[:server],
|
132
|
+
:farm => photo[:farm],
|
133
|
+
:title => photo[:title],
|
134
|
+
:is_public => photo[:ispublic],
|
135
|
+
:is_friend => photo[:isfriend],
|
136
|
+
:is_family => photo[:isfamily],
|
137
|
+
:license => photo[:license],
|
138
|
+
:uploaded_at => (Time.at(photo[:dateupload].to_i) rescue nil),
|
139
|
+
:taken_at => (Time.parse(photo[:datetaken]) rescue nil),
|
140
|
+
:owner_name => photo[:ownername],
|
141
|
+
:icon_server => photo[:icon_server],
|
142
|
+
:original_format => photo[:originalformat],
|
143
|
+
:updated_at => (Time.at(photo[:lastupdate].to_i) rescue nil),
|
144
|
+
:geo => photo[:geo],
|
145
|
+
:tags => photo[:tags],
|
146
|
+
:machine_tags => photo[:machine_tags],
|
147
|
+
:o_dims => photo[:o_dims],
|
148
|
+
:views => photo[:views].to_i}
|
149
|
+
|
150
|
+
photos << Photo.new(@flickr, attributes)
|
151
|
+
end if rsp.photos.photo
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns a list of the latest public photos uploaded to flickr.
|
156
|
+
#
|
157
|
+
# == Authentication
|
158
|
+
# This method does not require authentication.
|
159
|
+
#
|
160
|
+
# == Options
|
161
|
+
# * per_page (Optional)
|
162
|
+
# Number of photos to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is 500.
|
163
|
+
# * page (Optional)
|
164
|
+
# The page of results to return. If this argument is omitted, it defaults to 1.
|
165
|
+
#
|
166
|
+
def get_recent(options)
|
167
|
+
options.merge!({:extras => "license,date_upload,date_taken,owner_name,icon_server,original_format,last_update,geo,tags,machine_tags,o_dims,views"})
|
168
|
+
|
169
|
+
rsp = @flickr.send_request('flickr.photos.getRecent', options)
|
170
|
+
|
171
|
+
returning PhotoResponse.new(:page => rsp.photos[:page].to_i,
|
172
|
+
:pages => rsp.photos[:pages].to_i,
|
173
|
+
:per_page => rsp.photos[:perpage].to_i,
|
174
|
+
:total => rsp.photos[:total].to_i,
|
175
|
+
:photos => [], :api => self,
|
176
|
+
:method => 'flickr.photos.getRecent',
|
177
|
+
:options => options) do |photos|
|
178
|
+
rsp.photos.photo.each do |photo|
|
179
|
+
attributes = {:id => photo[:id],
|
180
|
+
:owner => photo[:owner],
|
181
|
+
:secret => photo[:secret],
|
182
|
+
:server => photo[:server],
|
183
|
+
:farm => photo[:farm],
|
184
|
+
:title => photo[:title],
|
185
|
+
:is_public => photo[:ispublic],
|
186
|
+
:is_friend => photo[:isfriend],
|
187
|
+
:is_family => photo[:isfamily],
|
188
|
+
:license => photo[:license],
|
189
|
+
:uploaded_at => (Time.at(photo[:dateupload].to_i) rescue nil),
|
190
|
+
:taken_at => (Time.parse(photo[:datetaken]) rescue nil),
|
191
|
+
:owner_name => photo[:ownername],
|
192
|
+
:icon_server => photo[:icon_server],
|
193
|
+
:original_format => photo[:originalformat],
|
194
|
+
:updated_at => (Time.at(photo[:lastupdate].to_i) rescue nil),
|
195
|
+
:geo => photo[:geo],
|
196
|
+
:tags => photo[:tags],
|
197
|
+
:machine_tags => photo[:machine_tags],
|
198
|
+
:o_dims => photo[:o_dims],
|
199
|
+
:views => photo[:views].to_i}
|
200
|
+
|
201
|
+
photos << Photo.new(@flickr, attributes)
|
202
|
+
end if rsp.photos.photo
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
data/lib/flickr/size.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# wrapping class to hold a flickr size
|
2
|
+
#
|
3
|
+
class Flickr::Photos::Size
|
4
|
+
attr_accessor :label, :width, :height, :source, :url
|
5
|
+
|
6
|
+
# create a new instance of a flickr size.
|
7
|
+
#
|
8
|
+
# Params
|
9
|
+
# * attributes (Required)
|
10
|
+
# a hash of attributes used to set the initial values of the size object
|
11
|
+
def initialize(attributes)
|
12
|
+
attributes.each do |k,v|
|
13
|
+
send("#{k}=", v)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/flickr/token.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# wrapping class to hold a flickr size
|
2
|
+
#
|
3
|
+
class Flickr::Auth::Token
|
4
|
+
attr_accessor :token, :permisions, :user_id, :username, :user_real_name
|
5
|
+
|
6
|
+
# create a new instance of a flickr auth token.
|
7
|
+
#
|
8
|
+
# Params
|
9
|
+
# * attributes (Required)
|
10
|
+
# a hash of attributes used to set the initial values of the token object
|
11
|
+
def initialize(attributes)
|
12
|
+
attributes.each do |k,v|
|
13
|
+
send("#{k}=", v)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# overide the default to_s to output the text of the token
|
18
|
+
#
|
19
|
+
def to_s
|
20
|
+
self.token.to_s
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
class Flickr::Uploader < Flickr::Base
|
2
|
+
def initialize(flickr)
|
3
|
+
@flickr = flickr
|
4
|
+
end
|
5
|
+
|
6
|
+
# upload a photo to flickr
|
7
|
+
#
|
8
|
+
# NOT WORKING ... FILE UPLOADS IN NET::HTTP SUX
|
9
|
+
#
|
10
|
+
# Params
|
11
|
+
# * filename (Required)
|
12
|
+
# path to the file to upload
|
13
|
+
# * options (Optional)
|
14
|
+
# options to attach to the photo (See Below)
|
15
|
+
#
|
16
|
+
# Options
|
17
|
+
# * title (Optional)
|
18
|
+
# The title of the photo.
|
19
|
+
# * description (Optional)
|
20
|
+
# A description of the photo. May contain some limited HTML.
|
21
|
+
# * tags (Optional)
|
22
|
+
# A space-seperated list of tags to apply to the photo.
|
23
|
+
# * privacy (Optional)
|
24
|
+
# Specifies who can view the photo. valid valus are:
|
25
|
+
# :public
|
26
|
+
# :private
|
27
|
+
# :friends
|
28
|
+
# :family
|
29
|
+
# :friends_and_family
|
30
|
+
# * safety_level (Optional)
|
31
|
+
# sets the safety level of the photo. valid values are:
|
32
|
+
# :safe
|
33
|
+
# :moderate
|
34
|
+
# :restricted
|
35
|
+
# * content_type (Optional)
|
36
|
+
# tells what type of image you are uploading. valid values are:
|
37
|
+
# :photo
|
38
|
+
# :screenshot
|
39
|
+
# :other
|
40
|
+
# * hidden (Optional)
|
41
|
+
# boolean that determines if the photo shows up in global searches
|
42
|
+
#
|
43
|
+
def upload(filename, options = {})
|
44
|
+
photo = File.new(filename, 'r').read
|
45
|
+
mimetype = MIME::Types.of(filename)
|
46
|
+
|
47
|
+
upload_options = {}
|
48
|
+
@flickr.sign_request(upload_options)
|
49
|
+
|
50
|
+
form = Flickr::Uploader::MultiPartForm.new
|
51
|
+
|
52
|
+
upload_options.each do |k,v|
|
53
|
+
form.parts << Flickr::Uploader::FormPart.new(k.to_s, v.to_s)
|
54
|
+
end
|
55
|
+
|
56
|
+
form.parts << Flickr::Uploader::FormPart.new('photo', photo, mimetype, filename)
|
57
|
+
|
58
|
+
headers = {"Content-Type" => "multipart/form-data; boundary=" + form.boundary}
|
59
|
+
|
60
|
+
rsp = Net::HTTP.start('api.flickr.com').post("/services/upload/", form.to_s, headers).body
|
61
|
+
|
62
|
+
xm = XmlMagic.new(rsp)
|
63
|
+
|
64
|
+
if xm[:stat] == 'ok'
|
65
|
+
xm
|
66
|
+
else
|
67
|
+
raise "#{xm.err[:code]}: #{xm.err[:msg]}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Flickr::Uploader::FormPart
|
73
|
+
attr_reader :data, :mime_type, :attributes, :filename
|
74
|
+
|
75
|
+
def initialize(name, data, mime_type = nil, filename = nil)
|
76
|
+
@attributes = {}
|
77
|
+
@attributes['name'] = name
|
78
|
+
@data = data
|
79
|
+
@mime_type = mime_type
|
80
|
+
@filename = filename
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_s
|
84
|
+
([ "Content-Disposition: form-data" ] +
|
85
|
+
attributes.map{|k,v| "#{k}=\"#{v}\""}).
|
86
|
+
join('; ') + "\r\n"+
|
87
|
+
(@mime_type ? "Content-Type: #{@mime_type}\r\n" : '')+
|
88
|
+
"\r\n#{data}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Flickr::Uploader::MultiPartForm
|
93
|
+
attr_accessor :boundary, :parts
|
94
|
+
|
95
|
+
def initialize(boundary=nil)
|
96
|
+
@boundary = boundary ||
|
97
|
+
"----------------------------Ruby#{rand(1000000000000)}"
|
98
|
+
@parts = []
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_s
|
102
|
+
"--#@boundary\r\n"+
|
103
|
+
parts.map{|p| p.to_s}.join("\r\n--#@boundary\r\n")+
|
104
|
+
"\r\n--#@boundary--\r\n"
|
105
|
+
end
|
106
|
+
end
|
data/lib/flickr_fu.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'xml_magic'
|
3
|
+
require 'net/http'
|
4
|
+
require 'cgi'
|
5
|
+
require 'uri'
|
6
|
+
require 'mime/types'
|
7
|
+
require 'digest/md5'
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
# base must load first
|
11
|
+
%w(base auth token photos photo photo_response comment note size uploader).each do |file|
|
12
|
+
require File.join(File.dirname(__FILE__), 'flickr', file)
|
13
|
+
end
|
14
|
+
|
15
|
+
include CommonThread::XML
|
16
|
+
|
17
|
+
class Object
|
18
|
+
# returning allows you to pass an object to a block that you can manipulate returning the manipulated object
|
19
|
+
def returning(value)
|
20
|
+
yield(value)
|
21
|
+
value
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flickr-fu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Wyrosdick
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-04-09 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: xml-magic
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
description:
|
25
|
+
email: ben@commonthread.com
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files: []
|
31
|
+
|
32
|
+
files:
|
33
|
+
- lib/flickr
|
34
|
+
- lib/flickr/auth.rb
|
35
|
+
- lib/flickr/base.rb
|
36
|
+
- lib/flickr/comment.rb
|
37
|
+
- lib/flickr/note.rb
|
38
|
+
- lib/flickr/photo.rb
|
39
|
+
- lib/flickr/photo_response.rb
|
40
|
+
- lib/flickr/photos.rb
|
41
|
+
- lib/flickr/size.rb
|
42
|
+
- lib/flickr/token.rb
|
43
|
+
- lib/flickr/uploader.rb
|
44
|
+
- lib/flickr_fu.rb
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://www.commonthread.com/projects/flickr-fu/rdoc
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project: flickr-fu
|
67
|
+
rubygems_version: 1.0.1
|
68
|
+
signing_key:
|
69
|
+
specification_version: 2
|
70
|
+
summary: Provides a ruby interface to flickr via the REST api
|
71
|
+
test_files: []
|
72
|
+
|