flickr_fu 0.1.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.gitignore +13 -0
  2. data/README +38 -1
  3. data/Rakefile +64 -13
  4. data/VERSION.yml +4 -0
  5. data/flickr_fu.gemspec +114 -32
  6. data/lib/flickr/auth.rb +8 -1
  7. data/lib/flickr/base.rb +75 -26
  8. data/lib/flickr/contact.rb +16 -0
  9. data/lib/flickr/contacts.rb +55 -0
  10. data/lib/flickr/errors.rb +20 -0
  11. data/lib/flickr/geo.rb +42 -0
  12. data/lib/flickr/license.rb +10 -0
  13. data/lib/flickr/location.rb +15 -0
  14. data/lib/flickr/photo.rb +130 -107
  15. data/lib/flickr/photos.rb +29 -11
  16. data/lib/flickr/photoset.rb +37 -0
  17. data/lib/flickr/photosets.rb +39 -0
  18. data/lib/flickr/token.rb +1 -1
  19. data/lib/flickr/urls.rb +44 -0
  20. data/lib/flickr_fu.rb +25 -2
  21. data/spec/fixtures/flickr/contacts/get_list-fail-99.xml +4 -0
  22. data/spec/fixtures/flickr/contacts/get_public_list-0.xml +7 -0
  23. data/spec/fixtures/flickr/photos/geo/get_location-0.xml +11 -0
  24. data/spec/fixtures/flickr/photos/geo/get_location-fail-2.xml +4 -0
  25. data/spec/fixtures/flickr/photos/get_info-0.xml +41 -0
  26. data/spec/fixtures/flickr/photos/get_info-1.xml +19 -0
  27. data/spec/fixtures/flickr/photos/get_sizes-0.xml +10 -0
  28. data/spec/fixtures/flickr/photos/get_sizes-1.xml +8 -0
  29. data/spec/fixtures/flickr/photos/licenses/get_info.xml +12 -0
  30. data/spec/fixtures/flickr/photosets/get_list-0.xml +13 -0
  31. data/spec/fixtures/flickr/photosets/get_photos-0.xml +7 -0
  32. data/spec/fixtures/flickr/test/echo-0.xml +5 -0
  33. data/spec/fixtures/flickr/test/null-fail-99.xml +4 -0
  34. data/spec/fixtures/flickr/urls/get_group-0.xml +4 -0
  35. data/spec/fixtures/flickr/urls/get_group-fail-1.xml +4 -0
  36. data/spec/fixtures/flickr/urls/get_user_photos-0.xml +4 -0
  37. data/spec/fixtures/flickr/urls/get_user_photos-fail-1.xml +4 -0
  38. data/spec/fixtures/flickr/urls/get_user_photos-fail-2.xml +4 -0
  39. data/spec/fixtures/flickr/urls/get_user_profile-0.xml +4 -0
  40. data/spec/fixtures/flickr/urls/get_user_profile-fail-1.xml +4 -0
  41. data/spec/fixtures/flickr/urls/get_user_profile-fail-2.xml +4 -0
  42. data/spec/fixtures/flickr/urls/lookup_group-0.xml +6 -0
  43. data/spec/fixtures/flickr/urls/lookup_group-fail-1.xml +4 -0
  44. data/spec/fixtures/flickr/urls/lookup_user-0.xml +6 -0
  45. data/spec/fixtures/flickr/videos/get_info-0.xml +31 -0
  46. data/spec/fixtures/flickr/videos/get_sizes-0.xml +13 -0
  47. data/spec/flickr/base_spec.rb +97 -0
  48. data/spec/flickr/contacts_spec.rb +47 -0
  49. data/spec/flickr/errors_spec.rb +21 -0
  50. data/spec/flickr/geo_spec.rb +20 -0
  51. data/spec/flickr/photo_spec.rb +140 -0
  52. data/spec/flickr/photos_spec.rb +50 -0
  53. data/spec/flickr/photosets_spec.rb +49 -0
  54. data/spec/flickr/test_spec.rb +34 -0
  55. data/spec/flickr/urls_spec.rb +99 -0
  56. data/spec/spec.opts +4 -0
  57. data/spec/spec_helper.rb +20 -0
  58. metadata +66 -7
@@ -0,0 +1,16 @@
1
+ # wrapping class to hold a flickr contact
2
+ #
3
+ class Flickr::Contacts::Contact
4
+ attr_accessor :nsid, :friend, :family, :iconfarm, :iconserver, :location, :username, :ignored, :realname, :path_alias
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 contact object
11
+ def initialize(attributes)
12
+ attributes.each do |k,v|
13
+ send("#{k}=", v)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,55 @@
1
+ class Flickr::Contacts < Flickr::Base
2
+ def initialize(flickr)
3
+ @flickr = flickr
4
+ end
5
+
6
+ # Get a user's public contact list.
7
+ #
8
+ # Params
9
+ # * id (Required)
10
+ # the nsid of the user to get information for
11
+ #
12
+ def get_public_list(id, options={})
13
+ options.merge!({:user_id => id})
14
+ rsp = @flickr.send_request('flickr.contacts.getPublicList', options)
15
+ collect_contacts(rsp)
16
+ end
17
+
18
+
19
+ # Get the authorized user's contact list.
20
+ #
21
+ def get_list(options={})
22
+ rsp = @flickr.send_request('flickr.contacts.getList', options)
23
+ collect_contacts(rsp)
24
+ end
25
+
26
+
27
+ protected
28
+ def collect_contacts(rsp)
29
+ contacts = []
30
+ return contacts unless rsp
31
+ if rsp.contacts.contact
32
+ rsp.contacts.contact.each do |contact|
33
+ attributes = create_attributes(contact)
34
+ contacts << Contact.new(attributes)
35
+ end
36
+ end
37
+ return contacts
38
+ end
39
+
40
+ def create_attributes(contact)
41
+ {
42
+ :nsid => contact[:nsid],
43
+ :path_alias => contact[:path_alias],
44
+ :username => contact[:username],
45
+ :iconfarm => contact[:iconfarm],
46
+ :iconserver => contact[:iconserver],
47
+ :ignored => contact[:ignored],
48
+ :friend => contact[:friend],
49
+ :family => contact[:family],
50
+ :realname => contact[:realname],
51
+ :location => contact[:location]
52
+ }
53
+ end
54
+
55
+ end
@@ -0,0 +1,20 @@
1
+ module Flickr
2
+ class Error < RuntimeError
3
+ attr_accessor :code
4
+ end
5
+
6
+
7
+ class Errors
8
+
9
+ # Method used for raising the appropriate error class for a given error code.
10
+ # Currently raises only Flickr::Error
11
+ def self.error_for(code, message)
12
+ raise RuntimeError.new("Internal error. Flickr API error not identified or unknown error.") if (code.nil? || message.nil? || message.empty?)
13
+ raise RuntimeError.new("Internal error. Unknown error.") if code.to_i == 0 # We assume that error code 0 is never returned
14
+ e = Flickr::Error.new("#{code}: #{message}")
15
+ e.code = code
16
+ raise e
17
+ end
18
+ end
19
+
20
+ end
data/lib/flickr/geo.rb ADDED
@@ -0,0 +1,42 @@
1
+ class Flickr::Photos::Geo < Flickr::Base
2
+
3
+ def initialize(flickr)
4
+ @flickr = flickr
5
+ end
6
+
7
+ # Get the geo data (latitude and longitude and the accuracy level) of a photo.
8
+ #
9
+ # Params
10
+ # * photo_id (Required)
11
+ #
12
+ # Returns Flickr::Photos::Location object containing photo location
13
+ # or nil if photo is not geotagged.
14
+ def get_location(photo_id)
15
+ # begin
16
+ rsp = @flickr.send_request('flickr.photos.geo.getLocation', {:photo_id => photo_id})
17
+ Flickr::Photos::Location.new(:latitude => rsp.photo.location[:latitude].to_f,
18
+ :longitude => rsp.photo.location[:longitude].to_f, :accuracy => rsp.photo.location[:accuracy].to_i)
19
+ end
20
+
21
+ # Sets the geo data(latitude and longitude and the accuracy level) of a photo.
22
+ #
23
+ # Params
24
+ # * photo_id (Required)
25
+ # * latittude (Requried)
26
+ # * longitude (Required)
27
+ # * accuracy (Optional)
28
+ #
29
+ # Returns true if successful, raises an error otherwise.
30
+ def set_location(photo_id, lat, lon, accuracy = nil)
31
+ request_options = {:photo_id => photo_id, :lat => lat, :lon => lon}
32
+ request_options[:accuracy] = accuracy if !accuracy.nil?
33
+ @flickr.send_request('flickr.photos.geo.setLocation', request_options, :post)
34
+ true
35
+ end
36
+
37
+ def remove_location(photo_id)
38
+ request_options = {:photo_id => photo_id}
39
+ @flickr.send_request('flickr.photos.geo.removeLocation', request_options, :post)
40
+ true
41
+ end
42
+ end
@@ -11,4 +11,14 @@ class Flickr::Photos::License
11
11
  send("#{k}=", v)
12
12
  end
13
13
  end
14
+
15
+ def == o
16
+ return false unless o.respond_to?(:id) && o.respond_to?(:name) && o.respond_to?(:url)
17
+ return true if id == o.id && name == o.name && url == o.url
18
+ false
19
+ end
20
+
21
+ def eql? o
22
+ return self == o
23
+ end
14
24
  end
@@ -0,0 +1,15 @@
1
+ class Flickr::Photos::Location
2
+
3
+ attr_accessor :latitude, :longitude, :accuracy
4
+
5
+ # Create an instance of Flickr::Photos::Location
6
+ #
7
+ # Params
8
+ # * attributes (Required)
9
+ # a hash of attributes used to set the initial values of the Location object
10
+ def initialize(attributes)
11
+ attributes.each do |k,v|
12
+ send("#{k}=", v)
13
+ end
14
+ end
15
+ end
data/lib/flickr/photo.rb CHANGED
@@ -1,11 +1,9 @@
1
1
  # wrapping class to hold an flickr photo
2
- #
3
2
  class Flickr::Photos::Photo
4
3
  attr_accessor :id, :owner, :secret, :server, :farm, :title, :is_public, :is_friend, :is_family # standard attributes
5
4
  attr_accessor :license_id, :uploaded_at, :taken_at, :owner_name, :icon_server, :original_format, :updated_at, :geo, :tags, :machine_tags, :o_dims, :views, :media # extra attributes
6
5
  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
6
+ attr_accessor :comments # comment attributes
9
7
 
10
8
  # create a new instance of a flickr photo.
11
9
  #
@@ -21,8 +19,31 @@ class Flickr::Photos::Photo
21
19
  end
22
20
  end
23
21
 
22
+ # Alias to image_url method
23
+ def url(size = :medium)
24
+ image_url(size)
25
+ end
26
+
27
+ # returns an instance of Flickr::Photos::Size for the required size
28
+ #
29
+ # Params
30
+ # * size (Optional)
31
+ # the size of the size instance to return. Optional sizes are:
32
+ # :square - square 75x75
33
+ # :thumbnail - 100 on longest side
34
+ # :small - 240 on longest side
35
+ # :medium - 500 on longest side
36
+ # :large - 1024 on longest side (only exists for very large original images)
37
+ # :original - original image, either a jpg, gif or png, depending on source format
38
+ # Examples
39
+ # Photo.photo_size(:square).source
40
+ # Photo.photo_size(:large).width
41
+ def photo_size(size = :medium)
42
+ size_hash.fetch(size.to_s, size_hash['medium'])
43
+ end
44
+
24
45
  # retreive the url to the image stored on flickr
25
- #
46
+ #
26
47
  # == Params
27
48
  # * size (Optional)
28
49
  # the size of the image to return. Optional sizes are:
@@ -32,10 +53,30 @@ class Flickr::Photos::Photo
32
53
  # :medium - 500 on longest side
33
54
  # :large - 1024 on longest side (only exists for very large original images)
34
55
  # :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}")
56
+ #
57
+ def image_url(size = :medium)
58
+ # It turns out that flickr always stores all the sizes of the picture even when getSizes call returns otherwise.
59
+ # Not calling getSizes is also very important for performance reasons.
60
+ # Retrieving 30 search results means calling the API 31 times if you call getSizes every time.
61
+ # Mind that you still need to call getSizes if you go out for the original image.
62
+ if size == :original
63
+ size_hash[size.to_s].source if size_hash.has_key? size.to_s
64
+ else
65
+ key = "_#{size_key(size.to_sym)}"
66
+ key = "" if key == "_"
67
+ "http://farm#{farm}.static.flickr.com/#{server}/#{id}_#{secret}#{key}.jpg"
68
+ end
69
+ end
70
+
71
+ def photopage_url
72
+ # Keeping the same convention as image_url (foo_url)
73
+ url_photopage
74
+ end
75
+
76
+ def video_url
77
+ if size_hash['video player']
78
+ size_hash['video player'].source
79
+ end
39
80
  end
40
81
 
41
82
  # save the current photo to the local computer
@@ -53,7 +94,7 @@ class Flickr::Photos::Photo
53
94
  # :original - original image, either a jpg, gif or png, depending on source format
54
95
  #
55
96
  def save_as(filename, size = :medium)
56
- format = size.to_sym == :original ? self.original_format : 'jpg'
97
+ format = size.to_sym == :original ? (self.original_format || 'jpg') : 'jpg'
57
98
  filename = "#{filename}.#{format}"
58
99
 
59
100
  if File.exists?(filename) or not self.url(size)
@@ -73,7 +114,7 @@ class Flickr::Photos::Photo
73
114
  # comma seperated list of tags
74
115
  #
75
116
  def add_tags(tags)
76
- rsp = @flickr.send_request('flickr.photos.addTags', {:photo_id => self.id, :tags => tags}, :post)
117
+ @flickr.send_request('flickr.photos.addTags', {:photo_id => self.id, :tags => tags}, :post)
77
118
  true
78
119
  end
79
120
 
@@ -84,7 +125,7 @@ class Flickr::Photos::Photo
84
125
  # text of the comment
85
126
  #
86
127
  def add_comment(message)
87
- rsp = @flickr.send_request('flickr.photos.comments.addComment', {:photo_id => self.id, :comment_text => message}, :post)
128
+ @flickr.send_request('flickr.photos.comments.addComment', {:photo_id => self.id, :comment_text => message}, :post)
88
129
  true
89
130
  end
90
131
 
@@ -103,7 +144,7 @@ class Flickr::Photos::Photo
103
144
  # The height of the note
104
145
  #
105
146
  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)
147
+ @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
148
  true
108
149
  end
109
150
 
@@ -114,7 +155,7 @@ class Flickr::Photos::Photo
114
155
  # The amount of degrees by which to rotate the photo (clockwise) from it's current orientation. Valid values are 90, 180 and 270.
115
156
  #
116
157
  def rotate(degrees)
117
- rsp = @flickr.send_request('flickr.photos.transform.rotate', {:photo_id => self.id, :degrees => degrees}, :post)
158
+ @flickr.send_request('flickr.photos.transform.rotate', {:photo_id => self.id, :degrees => degrees}, :post)
118
159
  true
119
160
  end
120
161
 
@@ -123,6 +164,29 @@ class Flickr::Photos::Photo
123
164
  def license
124
165
  @flickr.photos.licenses[self.license_id]
125
166
  end
167
+
168
+ # Returns the location of the photo (if available)
169
+ # or nil if photo is not geo-tagged.
170
+ def location
171
+ begin
172
+ @location ||= @flickr.photos.geo.get_location(self.id)
173
+ rescue Flickr::Error => e
174
+ if e.code == 2 # 2: Photo has no location information.
175
+ return nil
176
+ else
177
+ raise e
178
+ end
179
+ end
180
+ end
181
+
182
+ def location= location
183
+ if !location.nil?
184
+ @flickr.photos.geo.set_location(self.id, location.latitude, location.longitude, location.accuracy)
185
+ else
186
+ @flickr.photos.geo.remove_location(self.id)
187
+ end
188
+ @location = location
189
+ end
126
190
 
127
191
  # Sets the license for a photo.
128
192
  #
@@ -130,7 +194,7 @@ class Flickr::Photos::Photo
130
194
  # * license_id (Required)
131
195
  # The license to apply, or 0 (zero) to remove the current license.
132
196
  def set_license(license_id)
133
- rsp = @flickr.send_request('flickr.photos.licenses.setLicense', {:photo_id => self.id, :license_id => license_id}, :post)
197
+ @flickr.send_request('flickr.photos.licenses.setLicense', {:photo_id => self.id, :license_id => license_id}, :post)
134
198
  true
135
199
  end
136
200
 
@@ -160,13 +224,39 @@ class Flickr::Photos::Photo
160
224
  end
161
225
 
162
226
  def comments # :nodoc:
163
- attach_comments
164
- @comments
227
+ @comments ||= begin
228
+ if @comment_count == 0
229
+ self.comments = []
230
+ else
231
+ rsp = @flickr.send_request('flickr.photos.comments.getList', :photo_id => self.id)
232
+
233
+ self.comments = []
234
+
235
+ rsp.comments.comment.each do |comment|
236
+ self.comments << Flickr::Photos::Comment.new(:id => comment[:id],
237
+ :comment => comment.to_s,
238
+ :author => comment[:author],
239
+ :author_name => comment[:authorname],
240
+ :permalink => comment[:permalink],
241
+ :created_at => (Time.at(comment[:datecreate].to_i) rescue nil))
242
+ end
243
+ end
244
+
245
+ self.comments
246
+ end
165
247
  end
166
248
 
167
249
  def sizes # :nodoc:
168
- attach_sizes
169
- @sizes
250
+ @sizes ||= begin
251
+ rsp = @flickr.send_request('flickr.photos.getSizes', :photo_id => self.id)
252
+
253
+ _sizes = []
254
+ rsp.sizes.size.each do |size|
255
+ _sizes << Flickr::Photos::Size.new(:label => size[:label], :width => size[:width],
256
+ :height => size[:height], :source => size[:source], :url => size[:url])
257
+ end
258
+ _sizes
259
+ end
170
260
  end
171
261
 
172
262
  def notes # :nodoc:
@@ -175,34 +265,14 @@ class Flickr::Photos::Photo
175
265
  end
176
266
 
177
267
  protected
178
- def url_square # :nodoc:
179
- attach_sizes
180
- @url_square
181
- end
182
-
183
- def url_thumbnail # :nodoc:
184
- attach_sizes
185
- @url_thumbnail
186
- end
187
-
188
- def url_small # :nodoc:
189
- attach_sizes
190
- @url_small
191
- end
192
-
193
- def url_medium # :nodoc:
194
- attach_sizes
195
- @url_medium
196
- end
197
-
198
- def url_large # :nodoc:
199
- attach_sizes
200
- @url_large
201
- end
202
-
203
- def url_original # :nodoc:
204
- attach_sizes
205
- @url_original
268
+ def size_hash
269
+ @size_hash ||= begin
270
+ hash = {}
271
+ sizes.each do |size|
272
+ hash[size.label.downcase] = size
273
+ end
274
+ hash
275
+ end
206
276
  end
207
277
 
208
278
  private
@@ -211,12 +281,12 @@ class Flickr::Photos::Photo
211
281
  # convert the size to the key used in the flickr url
212
282
  def size_key(size)
213
283
  case size.to_sym
214
- when :square : 's'
215
- when :thumb, :thumbnail : 't'
216
- when :small : 'm'
217
- when :medium : '-'
218
- when :large : 'b'
219
- when :original : 'o'
284
+ when :square then 's'
285
+ when :thumb, :thumbnail then 't'
286
+ when :small then 'm'
287
+ when :medium then ''
288
+ when :large then 'b'
289
+ when :original then 'o'
220
290
  else ''
221
291
  end
222
292
  end
@@ -227,7 +297,7 @@ class Flickr::Photos::Photo
227
297
  rsp = @flickr.send_request('flickr.photos.getInfo', :photo_id => self.id, :secret => self.secret)
228
298
 
229
299
  self.info_added = true
230
- self.description = rsp.photo.description.to_s
300
+ self.description = rsp.photo.description.to_s.strip
231
301
  self.original_secret = rsp.photo[:originalsecret]
232
302
  self.owner_username = rsp.photo.owner[:username]
233
303
  self.owner_realname = rsp.photo.owner[:realname]
@@ -238,61 +308,14 @@ class Flickr::Photos::Photo
238
308
 
239
309
  rsp.photo.notes.note.each do |note|
240
310
  self.notes << Flickr::Photos::Note.new(:id => note[:id],
241
- :note => note.to_s,
242
- :author => note[:author],
243
- :author_name => note[:authorname],
244
- :x => note[:x],
245
- :y => note[:y],
246
- :width => note[:w],
247
- :height => note[:h])
311
+ :note => note.to_s,
312
+ :author => note[:author],
313
+ :author_name => note[:authorname],
314
+ :x => note[:x],
315
+ :y => note[:y],
316
+ :width => note[:w],
317
+ :height => note[:h])
248
318
  end if rsp.photo.notes.note
249
319
  end
250
320
  end
251
-
252
- # loads picture sizes only after one has been requested
253
- def attach_sizes
254
- unless self.sizes_added
255
- rsp = @flickr.send_request('flickr.photos.getSizes', :photo_id => self.id)
256
-
257
- self.sizes_added = true
258
- self.sizes = []
259
-
260
- # TODO: investigate the new video features and integrate better
261
- rsp.sizes.size.each do |size|
262
- method = "url_#{size[:label].downcase}="
263
- next unless respond_to? method
264
- send(method, size[:source])
265
-
266
- # send("url_#{size[:label].downcase}=", size[:source])
267
-
268
- self.sizes << Flickr::Photos::Size.new(:label => size[:label],
269
- :width => size[:width],
270
- :height => size[:height],
271
- :source => size[:source],
272
- :url => size[:url])
273
- end
274
- end
275
- end
276
-
277
- # loads comments once they have been requested
278
- def attach_comments
279
- if @comment_count == 0
280
- self.comments = []
281
- self.comments_added = true
282
- elsif not self.comments_added
283
- rsp = @flickr.send_request('flickr.photos.comments.getList', :photo_id => self.id)
284
-
285
- self.comments = []
286
- self.comments_added = true
287
-
288
- rsp.comments.comment.each do |comment|
289
- self.comments << Flickr::Photos::Comment.new(:id => comment[:id],
290
- :comment => comment.to_s,
291
- :author => comment[:author],
292
- :author_name => comment[:authorname],
293
- :permalink => comment[:permalink],
294
- :created_at => (Time.at(comment[:datecreate].to_i) rescue nil))
295
- end
296
- end
297
- end
298
321
  end