neves-ruby_picasa 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +22 -0
- data/README.txt +79 -0
- data/lib/ruby_picasa/types.rb +315 -0
- data/lib/ruby_picasa.rb +397 -0
- data/spec/ruby_picasa/types_spec.rb +303 -0
- data/spec/ruby_picasa_spec.rb +382 -0
- data/spec/sample/album.atom +141 -0
- data/spec/sample/recent.atom +111 -0
- data/spec/sample/search.atom +99 -0
- data/spec/sample/user.atom +107 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +13 -0
- metadata +76 -0
data/History.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
=== 0.2.1 / 2009-03-05
|
2
|
+
|
3
|
+
* Now fully documented.
|
4
|
+
* Full spec suite with 100% coverage.
|
5
|
+
* Even easier to use the api.
|
6
|
+
* Move thumbnail and url to Base so it can be used for Photos and Albums.
|
7
|
+
* Enable url to optionally return width and height for image_tag.
|
8
|
+
* Use the numeric gphoto:id as id and rename tho standard atom feed id to feed_id
|
9
|
+
* Fixed handling of cropped thumbnails and automatically query for valid thumbnail sizes.
|
10
|
+
* Stopped using the wrong url to #load albums. Renamed method to #feed.
|
11
|
+
|
12
|
+
=== 0.2.0 / 2009-02-25
|
13
|
+
|
14
|
+
* First public release.
|
15
|
+
|
16
|
+
=== 0.1.0 / 2009-02-21
|
17
|
+
|
18
|
+
* First working version.
|
19
|
+
|
20
|
+
=== 0.0.0 / 2009-02-21
|
21
|
+
|
22
|
+
* Needed to access Picasa.
|
data/README.txt
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
= ruby_picasa
|
2
|
+
|
3
|
+
* http://github.com/pangloss/ruby_picasa
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Provides a super easy to use object layer for authenticating and accessing
|
8
|
+
Picasa through their API.
|
9
|
+
|
10
|
+
== FEATURES:
|
11
|
+
|
12
|
+
* Simplifies the process of obtaining both a temporary and a permanent AuthSub
|
13
|
+
token.
|
14
|
+
* Very easy to use API.
|
15
|
+
* Allows access to both public and private User, Album and Photo data.
|
16
|
+
* Uses Objectify::Xml to define the XML object-relational layer with a very
|
17
|
+
easy to understand DSL. See www.github.com/pangloss/objectify_xml
|
18
|
+
|
19
|
+
== PROBLEMS:
|
20
|
+
|
21
|
+
* None known.
|
22
|
+
|
23
|
+
== SYNOPSIS:
|
24
|
+
|
25
|
+
# 1. Authorize application for access (in a rails controller)
|
26
|
+
#
|
27
|
+
redirect_to RubyPicasa.authorization_url(auth_result_url)
|
28
|
+
|
29
|
+
# 2. Extract the Picasa token from the request Picasa sends back to your app
|
30
|
+
# and create a permanent AuthSub token. Returns an initialized Picasa
|
31
|
+
# session. (Called from the Rails action for auth_result_url above)
|
32
|
+
picasa = RubyPicasa.authorize_request(self.request)
|
33
|
+
|
34
|
+
# 3. Access the data you are interested in
|
35
|
+
@album = picasa.user.albums.first
|
36
|
+
@photo = @album.photos.first
|
37
|
+
|
38
|
+
# 4. Display your photos
|
39
|
+
image_tag @photo.url
|
40
|
+
image_tag @photo.url('160c') # Picasa thumbnail names are predefined
|
41
|
+
|
42
|
+
== PUBLIC ALBUMS:
|
43
|
+
# 1. Create an public user by its id:
|
44
|
+
#
|
45
|
+
user = Picasa.public_user("user_id")
|
46
|
+
|
47
|
+
== REQUIREMENTS:
|
48
|
+
|
49
|
+
* objectify_xml
|
50
|
+
|
51
|
+
== INSTALL:
|
52
|
+
|
53
|
+
* gem install ruby-picasa
|
54
|
+
* gem install pangloss-ruby-picasa --source http://gems.github.com
|
55
|
+
|
56
|
+
== LICENSE:
|
57
|
+
|
58
|
+
(The MIT License)
|
59
|
+
|
60
|
+
Copyright (c) 2009 Darrick Wiebe
|
61
|
+
|
62
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
63
|
+
a copy of this software and associated documentation files (the
|
64
|
+
'Software'), to deal in the Software without restriction, including
|
65
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
66
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
67
|
+
permit persons to whom the Software is furnished to do so, subject to
|
68
|
+
the following conditions:
|
69
|
+
|
70
|
+
The above copyright notice and this permission notice shall be
|
71
|
+
included in all copies or substantial portions of the Software.
|
72
|
+
|
73
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
74
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
75
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
76
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
77
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
78
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
79
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,315 @@
|
|
1
|
+
module RubyPicasa
|
2
|
+
# attributes :url, :height, :width
|
3
|
+
class PhotoUrl < Objectify::ElementParser
|
4
|
+
attributes :url, :height, :width
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
class ThumbnailUrl < PhotoUrl
|
9
|
+
|
10
|
+
# The name of the current thumbnail. For possible names, see Photo#url
|
11
|
+
def thumb_name
|
12
|
+
name = url.scan(%r{/s([^/]+)/[^/]+$}).flatten.compact.first
|
13
|
+
if name
|
14
|
+
name.sub(/-/, '')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Note that in all defined classes I'm ignoring values I don't happen to need
|
21
|
+
# or know about. Please do add support for the ones I've missed. Be sure to
|
22
|
+
# declare which namespaces are supported with the namespaces method. Any
|
23
|
+
# elements defined in other namespaces are automatically ignored.
|
24
|
+
#
|
25
|
+
# Base class for User, Photo and Album types, not used independently.
|
26
|
+
#
|
27
|
+
# attribute :id, 'gphoto:id'
|
28
|
+
# attribute :feed_id, 'id'
|
29
|
+
# attributes :updated, :title
|
30
|
+
#
|
31
|
+
# has_many :links, Objectify::Atom::Link, 'link'
|
32
|
+
# has_one :content, PhotoUrl, 'media:content'
|
33
|
+
# has_many :thumbnails, ThumbnailUrl, 'media:thumbnail'
|
34
|
+
# has_one :author, Objectify::Atom::Author, 'author'
|
35
|
+
class Base < Objectify::DocumentParser
|
36
|
+
namespaces :openSearch, :gphoto, :media
|
37
|
+
flatten 'media:group'
|
38
|
+
|
39
|
+
attribute :id, 'gphoto:id'
|
40
|
+
attribute :feed_id, 'id'
|
41
|
+
attributes :updated, :title
|
42
|
+
|
43
|
+
has_many :links, Objectify::Atom::Link, 'link'
|
44
|
+
has_one :content, PhotoUrl, 'media:content'
|
45
|
+
has_many :thumbnails, ThumbnailUrl, 'media:thumbnail'
|
46
|
+
has_one :author, Objectify::Atom::Author, 'author'
|
47
|
+
|
48
|
+
# Return the link object with a matching rel attribute value. +rel+ can be
|
49
|
+
# either a fully matching string or a regular expression.
|
50
|
+
def link(rel)
|
51
|
+
links.find { |l| rel === l.rel }
|
52
|
+
end
|
53
|
+
|
54
|
+
def session=(session)
|
55
|
+
@session = session
|
56
|
+
end
|
57
|
+
|
58
|
+
# Should return the Picasa instance that retrieved this data.
|
59
|
+
def session
|
60
|
+
if @session
|
61
|
+
@session
|
62
|
+
else
|
63
|
+
@session = parent.session if parent
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Retrieves the data feed at the url of the current record.
|
68
|
+
def feed(options = {})
|
69
|
+
session.get_url(link('http://schemas.google.com/g/2005#feed').href, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
# If the results are paginated, retrieve the next page.
|
73
|
+
def next
|
74
|
+
if link = link('next')
|
75
|
+
session.get_url(link.href)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# If the results are paginated, retrieve the previous page.
|
80
|
+
def previous
|
81
|
+
if link = link('previous')
|
82
|
+
session.get_url(link.href)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Thumbnail names are by image width in pixels. Sizes up to 160 may be
|
87
|
+
# either cropped (square) or uncropped:
|
88
|
+
#
|
89
|
+
# cropped: 32c, 48c, 64c, 72c, 144c, 160c
|
90
|
+
# uncropped: 32u, 48u, 64u, 72u, 144u, 160u
|
91
|
+
#
|
92
|
+
# The rest of the image sizes should be specified by the desired width
|
93
|
+
# alone. Widths up to 800px may be embedded on a webpage:
|
94
|
+
#
|
95
|
+
# embeddable: 200, 288, 320, 400, 512, 576, 640, 720, 800
|
96
|
+
# not embeddable: 912, 1024, 1152, 1280, 1440, 1600
|
97
|
+
#
|
98
|
+
# if a options is set to true or a hash is given, the width and height of
|
99
|
+
# the image will be added to the hash and returned. Useful for passing to
|
100
|
+
# the rails image_tag helper as follows:
|
101
|
+
#
|
102
|
+
# image_tag(*image.url('72c', { :class => 'thumb' }))
|
103
|
+
#
|
104
|
+
# which results in:
|
105
|
+
#
|
106
|
+
# <img href="..." class="thumb" width="72" height="72">
|
107
|
+
#
|
108
|
+
def url(thumb_name = nil, options = nil)
|
109
|
+
url = nil
|
110
|
+
if thumb_name.is_a? Hash
|
111
|
+
options = thumb_name
|
112
|
+
thumb_name = nil
|
113
|
+
end
|
114
|
+
options = {} if options and not options.is_a? Hash
|
115
|
+
if thumb_name
|
116
|
+
if thumb = thumbnail(thumb_name)
|
117
|
+
url = thumb.url
|
118
|
+
options = { :width => thumb.width, :height => thumb.height }.merge(options) if options
|
119
|
+
end
|
120
|
+
else
|
121
|
+
url = content.url
|
122
|
+
options = { :width => content.width, :height => content.height }.merge(options) if options
|
123
|
+
end
|
124
|
+
if options
|
125
|
+
[url, options]
|
126
|
+
else
|
127
|
+
url
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# See +url+ for possible image sizes
|
132
|
+
def thumbnail(thumb_name)
|
133
|
+
raise PicasaError, 'Invalid thumbnail size' unless Photo::VALID.include?(thumb_name.to_s)
|
134
|
+
thumb = thumbnails.find { |t| t.thumb_name == thumb_name }
|
135
|
+
if thumb
|
136
|
+
thumb
|
137
|
+
elsif session
|
138
|
+
f = feed(:thumbsize => thumb_name)
|
139
|
+
if f
|
140
|
+
f.thumbnails.first
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# convert a thumbnail from a size to another, example:
|
146
|
+
# tb(160) converts this thumbnail url:
|
147
|
+
# http://lh5.ggpht.com/_8t0hxCMjpcM/SZ7OKoCAAsI/AAAAAAAABYA/_lUCR6-iV2g/s144-c/Calibration_Control_Image.jpg
|
148
|
+
# into this one with new size:
|
149
|
+
# http://lh5.ggpht.com/_8t0hxCMjpcM/SZ7OKoCAAsI/AAAAAAAABYA/_lUCR6-iV2g/s160/Calibration_Control_Image.jpg
|
150
|
+
# the thumb does not need to exist on the xml
|
151
|
+
def tb(new_size, crop = false)
|
152
|
+
crop = crop ? "-c" : ""
|
153
|
+
parts = thumbnails.first.url.split "/"
|
154
|
+
parts[-2] = "s" + new_size.to_s + crop
|
155
|
+
parts.join "/"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
# Includes attributes and associations defined on Base, plus:
|
161
|
+
#
|
162
|
+
# attributes :total_results, # represents total number of albums
|
163
|
+
# :start_index,
|
164
|
+
# :items_per_page,
|
165
|
+
# :thumbnail
|
166
|
+
# has_many :entries, :Album, 'entry'
|
167
|
+
class User < Base
|
168
|
+
attributes :total_results, # represents total number of albums
|
169
|
+
:start_index,
|
170
|
+
:items_per_page,
|
171
|
+
:thumbnail
|
172
|
+
has_many :entries, :Album, 'entry'
|
173
|
+
|
174
|
+
# The current page of albums associated to the user.
|
175
|
+
def albums
|
176
|
+
entries
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
# Includes attributes and associations defined on Base and User, plus:
|
182
|
+
#
|
183
|
+
# has_many :entries, :Photo, 'entry'
|
184
|
+
class RecentPhotos < User
|
185
|
+
has_many :entries, :Photo, 'entry'
|
186
|
+
|
187
|
+
# The current page of recently updated photos associated to the user.
|
188
|
+
def photos
|
189
|
+
entries
|
190
|
+
end
|
191
|
+
|
192
|
+
undef albums
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
# Includes attributes and associations defined on Base, plus:
|
197
|
+
#
|
198
|
+
# attributes :published,
|
199
|
+
# :summary,
|
200
|
+
# :rights,
|
201
|
+
# :name,
|
202
|
+
# :access,
|
203
|
+
# :numphotos, # number of pictures in this album
|
204
|
+
# :total_results, # number of pictures matching this 'search'
|
205
|
+
# :start_index,
|
206
|
+
# :items_per_page,
|
207
|
+
# :allow_downloads
|
208
|
+
# has_many :entries, :Photo, 'entry'
|
209
|
+
class Album < Base
|
210
|
+
attributes :published,
|
211
|
+
:summary,
|
212
|
+
:rights,
|
213
|
+
:name,
|
214
|
+
:access,
|
215
|
+
:numphotos, # number of pictures in this album
|
216
|
+
:total_results, # number of pictures matching this 'search'
|
217
|
+
:start_index,
|
218
|
+
:items_per_page,
|
219
|
+
:allow_downloads
|
220
|
+
has_many :entries, :Photo, 'entry'
|
221
|
+
|
222
|
+
# True if this album's rights are set to public
|
223
|
+
def public?
|
224
|
+
rights == 'public'
|
225
|
+
end
|
226
|
+
|
227
|
+
# True if this album's rights are set to private
|
228
|
+
def private?
|
229
|
+
rights == 'private'
|
230
|
+
end
|
231
|
+
|
232
|
+
# The current page of photos in the album.
|
233
|
+
def photos(options = {})
|
234
|
+
if entries.blank? and !@photos_requested
|
235
|
+
@photos_requested = true
|
236
|
+
if session and data = feed
|
237
|
+
self.entries = data.entries
|
238
|
+
else
|
239
|
+
[]
|
240
|
+
end
|
241
|
+
else
|
242
|
+
entries
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
class Search < Album
|
249
|
+
# The current page of photos matching the search.
|
250
|
+
def photos(options = {})
|
251
|
+
super
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
# Includes attributes and associations defined on Base, plus:
|
257
|
+
#
|
258
|
+
# attributes :published,
|
259
|
+
# :summary,
|
260
|
+
# :version, # can use to determine if need to update...
|
261
|
+
# :position,
|
262
|
+
# :albumid, # useful from the recently updated feed for instance.
|
263
|
+
# :width,
|
264
|
+
# :height,
|
265
|
+
# :description,
|
266
|
+
# :keywords,
|
267
|
+
# :credit
|
268
|
+
# attribute :unique_id, 'exif:imageUniqueID'
|
269
|
+
# attribute :exif_distance, 'exif:distance'
|
270
|
+
# attribute :exif_exposure, 'exif:exposure'
|
271
|
+
# attribute :exif_flash, 'exif:flash'
|
272
|
+
# attribute :exif_focallength, 'exif:focallength'
|
273
|
+
# attribute :exif_fstop, 'exif:fstop'
|
274
|
+
# attribute :exif_iso, 'exif:iso'
|
275
|
+
# attribute :exif_make, 'exif:make'
|
276
|
+
# attribute :exif_model, 'exif:model'
|
277
|
+
# attribute :exif_time, 'exif:time'
|
278
|
+
# has_one :author, Objectify::Atom::Author, 'author'
|
279
|
+
class Photo < Base
|
280
|
+
CROPPED = %w[ 32c 48c 64c 72c 144c 160c ]
|
281
|
+
UNCROPPED = %w[ 32u 48u 64u 72u 144u 160u 32 48 64 72 144 160 ]
|
282
|
+
MEDIUM = %w[ 200 288 320 400 512 576 640 720 800 ]
|
283
|
+
LARGE = %w[ 912 1024 1152 1280 1440 1600 ]
|
284
|
+
VALID = CROPPED + UNCROPPED + MEDIUM + LARGE
|
285
|
+
|
286
|
+
namespace :exif
|
287
|
+
|
288
|
+
attributes :published,
|
289
|
+
:summary,
|
290
|
+
:version, # can use to determine if need to update...
|
291
|
+
:position,
|
292
|
+
:albumid, # useful from the recently updated feed for instance.
|
293
|
+
:width,
|
294
|
+
:height,
|
295
|
+
:description,
|
296
|
+
:keywords,
|
297
|
+
:credit
|
298
|
+
|
299
|
+
flatten 'exif:tags'
|
300
|
+
attribute :unique_id, 'exif:imageUniqueID'
|
301
|
+
attribute :exif_distance, 'exif:distance'
|
302
|
+
attribute :exif_exposure, 'exif:exposure'
|
303
|
+
attribute :exif_flash, 'exif:flash'
|
304
|
+
attribute :exif_focallength, 'exif:focallength'
|
305
|
+
attribute :exif_fstop, 'exif:fstop'
|
306
|
+
attribute :exif_iso, 'exif:iso'
|
307
|
+
attribute :exif_make, 'exif:make'
|
308
|
+
attribute :exif_model, 'exif:model'
|
309
|
+
attribute :exif_time, 'exif:time'
|
310
|
+
|
311
|
+
has_one :author, Objectify::Atom::Author, 'author'
|
312
|
+
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|