smugmug 0.0.1
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/HISTORY +2 -0
- data/LICENSE +19 -0
- data/MANIFEST +48 -0
- data/README +30 -0
- data/Rakefile +100 -0
- data/bin/smcli +225 -0
- data/bin/smugmug2sql +158 -0
- data/doc/API +310 -0
- data/doc/TODO +32 -0
- data/lib/net/httpz.rb +31 -0
- data/lib/smugmug.rb +179 -0
- data/lib/smugmug/album/info.rb +131 -0
- data/lib/smugmug/album/stats.rb +31 -0
- data/lib/smugmug/albums.rb +39 -0
- data/lib/smugmug/base.rb +104 -0
- data/lib/smugmug/cache.rb +33 -0
- data/lib/smugmug/config.rb +48 -0
- data/lib/smugmug/image/exif.rb +72 -0
- data/lib/smugmug/image/info.rb +88 -0
- data/lib/smugmug/image/stats.rb +32 -0
- data/lib/smugmug/images.rb +52 -0
- data/lib/smugmug/table.rb +133 -0
- data/lib/smugmug/util.rb +12 -0
- data/test/album.rb +359 -0
- data/test/config.rb +39 -0
- data/test/httpz.rb +120 -0
- data/test/image.rb +540 -0
- data/test/login.rb +24 -0
- data/test/runner.rb +83 -0
- data/test/servlet.rb +257 -0
- data/test/table.rb +113 -0
- data/xml/canned +212 -0
- data/xml/fail/empty.set.xml +4 -0
- data/xml/fail/invalid.apikey.xml +4 -0
- data/xml/fail/invalid.login.xml +4 -0
- data/xml/fail/invalid.method.xml +4 -0
- data/xml/fail/invalid.user.xml +4 -0
- data/xml/fail/system.error.xml +4 -0
- data/xml/standard/albums.get.xml +24 -0
- data/xml/standard/albums.getInfo.xml +38 -0
- data/xml/standard/albums.getStats.xml +43 -0
- data/xml/standard/categories.get.xml +213 -0
- data/xml/standard/images.get.xml +9 -0
- data/xml/standard/images.getEXIF.xml +34 -0
- data/xml/standard/images.getInfo.xml +29 -0
- data/xml/standard/images.getStats.xml +15 -0
- data/xml/standard/login.withHash.xml +7 -0
- data/xml/standard/login.withPassword.xml +10 -0
- metadata +103 -0
data/doc/API
ADDED
@@ -0,0 +1,310 @@
|
|
1
|
+
# -*- text -*-
|
2
|
+
# $Hg$
|
3
|
+
|
4
|
+
= Introduction
|
5
|
+
|
6
|
+
The majority of the SmugMug's function are auto-generated. This means rdoc
|
7
|
+
documentation is useless because none of the functions are visible. It is
|
8
|
+
probably still preferable to document the function within class files, but for
|
9
|
+
now I am going to document them here. This way everything is in one location.
|
10
|
+
|
11
|
+
== Conventions
|
12
|
+
|
13
|
+
* SmugMug refers to the company, or web site.
|
14
|
+
* smugmug refers to this library.
|
15
|
+
|
16
|
+
=== Method
|
17
|
+
==== Naming
|
18
|
+
|
19
|
+
SmugMug and ruby do not share the same naming convetions. For example, a
|
20
|
+
function to return an album's ID is called AlbumID by Smugmug, but ruby would
|
21
|
+
name it album_id. To keep both factions happy smugmug provides both naming
|
22
|
+
conventions. When data are boolean types the ruby method names have a trailing
|
23
|
+
question mark. This convention is used throughout ruby.
|
24
|
+
|
25
|
+
==== Return Types
|
26
|
+
|
27
|
+
There are four data types that are returned by SmugMug. They are boolean, int,
|
28
|
+
string, and float. The smugmug library ensures that all data returned is cast
|
29
|
+
to the appropriate type. For example, the data returned from smugmug.com for
|
30
|
+
AlbumID is a string, but smugmug returns a Fixnum (integer). This convention
|
31
|
+
is broken with dealing with EXIF data. Camera manufacturers may encode unit
|
32
|
+
information in their data, thereby preventing the casting of data. For
|
33
|
+
example, the method focal length for my Canon SD500 return 7mm.
|
34
|
+
|
35
|
+
The type information for the methods can be found on SmugMug's wiki at
|
36
|
+
http://smugmug.jot.com/WikiHome/API/Versions/1.1.1.
|
37
|
+
|
38
|
+
= SmugMug::SmugMug
|
39
|
+
|
40
|
+
The first time you use the library you need to login in SmugMug. This requires
|
41
|
+
a username and password, all subsequent login attempts do not require a
|
42
|
+
username, and password. The first time you login SmugMug returns your UserID,
|
43
|
+
and a password hash. These data are stored in ~/.smugmugrc by default, and
|
44
|
+
used by smugmug for subsequent login attempts.
|
45
|
+
|
46
|
+
== Login
|
47
|
+
|
48
|
+
=== Security
|
49
|
+
|
50
|
+
Logins are carried out over https, but all other operations are carried out
|
51
|
+
over http. This is currently not user configurable, but may be in future
|
52
|
+
versions.
|
53
|
+
|
54
|
+
=== First Login
|
55
|
+
|
56
|
+
The first login attempt requires and username, and password.
|
57
|
+
|
58
|
+
sm = SmugMug::SmugMug.new('user@example.net', 'secret')
|
59
|
+
|
60
|
+
=== Subsequent Login
|
61
|
+
|
62
|
+
Subsequent logins do not require a username/password if ~/.smugmugrc has good
|
63
|
+
data.
|
64
|
+
|
65
|
+
sm = SmugMug::SmugMug.new
|
66
|
+
|
67
|
+
=== Logging
|
68
|
+
|
69
|
+
All session data can be logged by for debugging purposes. By default, all
|
70
|
+
logging information is sent to /dev/null, or a StringIO if /dev/null does not
|
71
|
+
exist. (Note: Need a better solution that StringIO)
|
72
|
+
|
73
|
+
To create a logging instance do the following before logging into SmugMug.
|
74
|
+
|
75
|
+
$log = Logger.new('smugmug.log')
|
76
|
+
sm = SmugMug::SmugMug.new
|
77
|
+
|
78
|
+
NOTE: If logging is enabled personal information is captured, most notable the
|
79
|
+
username and password, or UserID and password hash depending how login was
|
80
|
+
handled.
|
81
|
+
|
82
|
+
= Albums
|
83
|
+
|
84
|
+
Use the each method to iterate over your albums. You can also use any?, and
|
85
|
+
size to determine if there are albums, and the number of albums. Because the
|
86
|
+
each method is provided, you can use any enumerable method.
|
87
|
+
|
88
|
+
sm = SmugMug::SmugMug.new
|
89
|
+
|
90
|
+
if sm.any?
|
91
|
+
puts "You have albums."
|
92
|
+
else
|
93
|
+
puts "You have no albums."
|
94
|
+
end
|
95
|
+
|
96
|
+
puts "There are #{sm.size} albums"
|
97
|
+
|
98
|
+
sm.each do |album|
|
99
|
+
puts albums.title
|
100
|
+
end
|
101
|
+
|
102
|
+
== Methods
|
103
|
+
|
104
|
+
Each albums has the following methods. The alias for each method is also shown
|
105
|
+
for completeness.
|
106
|
+
|
107
|
+
(Some of these functions are self explanatory, but others are not. I need to
|
108
|
+
clarify these.)
|
109
|
+
|
110
|
+
=== Standard
|
111
|
+
|
112
|
+
The following methods are available for Standard, Power, and Pro, and act on
|
113
|
+
the current album.
|
114
|
+
|
115
|
+
* AlbumID (album_id): returns the Album ID.
|
116
|
+
* HighlightID (hightlight_id): returns the hightlight ID.
|
117
|
+
* CategoryID (category_id): returns the category ID.
|
118
|
+
* SubCategoryID (sub_category_id): returns the sub category ID.
|
119
|
+
* Position (position): returns the position.
|
120
|
+
* SortMethod (sort_method): return sort method: Position, Caption, Filenames, Date Uploaded, Date Modified, Date Taken.
|
121
|
+
* SortDirection (sort_direction?): sort ascending (true), or descending (false).
|
122
|
+
* ImageCount (image_count): returns the number of images.
|
123
|
+
* Keywords (keywords): returns the keywords.
|
124
|
+
* Description (description): returns the description.
|
125
|
+
* LastUpdate (last_updated): returns the date and time when the album was last updated.
|
126
|
+
* CommunityID (community_id): returns the community id.
|
127
|
+
* Public (public?): show this gallery on your homepage?
|
128
|
+
* Password (password): returns the password.
|
129
|
+
* PasswordHint (password_hint): return password hint.
|
130
|
+
* CanRank (can_rank?): allow visitors to vote your photos most popular?
|
131
|
+
* Printable (printable?): can people buy prints of these photos?
|
132
|
+
* Filenames (filenames?): for photos without captions, show the filename instead of nothing?
|
133
|
+
* Comments (comments?): can visitors read and post comments?
|
134
|
+
* External (external?): allow external links?
|
135
|
+
* Originals (originals?): can people view your original full-size photos?
|
136
|
+
* FamilyEdit (family_edit?): can your family edit captions and keywords?
|
137
|
+
* FriendEdit (friend_edit?): can your friends edit captions and keywords?
|
138
|
+
* HideOwnder (hide_owner?): hide your name, navigation, and look and feel?
|
139
|
+
* EXIF (exif?): show extra camera info?
|
140
|
+
* Share (share?): do you want the 'share photo: links formus, blogs' link?
|
141
|
+
* Header (header?): (???)
|
142
|
+
* SmugSearchable (smug_searchable?): do you want your photos in SmugMug's search?
|
143
|
+
* WorldSearchable (world_searchable?): can Google find your smug mug?
|
144
|
+
|
145
|
+
puts album.image_count
|
146
|
+
puts album.SortMethod
|
147
|
+
|
148
|
+
=== Power
|
149
|
+
|
150
|
+
The following methods are available for Power, and Pro. (Need help with these.)
|
151
|
+
|
152
|
+
* Header (header?): (???)
|
153
|
+
* TemplateID (template_id): (???)
|
154
|
+
|
155
|
+
puts album.header
|
156
|
+
|
157
|
+
=== Pro
|
158
|
+
|
159
|
+
The following methods are available for Pro. (Need help with these.)
|
160
|
+
|
161
|
+
* Larges (larges?): (???)
|
162
|
+
* DefaultColor (default_color?): (???)
|
163
|
+
* Clean (clean?): (???)
|
164
|
+
* Protected (protected?): (???)
|
165
|
+
* Watermark (watermark?): (???)
|
166
|
+
|
167
|
+
puts album.Watermark
|
168
|
+
puts album.watermark?
|
169
|
+
|
170
|
+
== Stats
|
171
|
+
|
172
|
+
Every album has statistics associted with it for the current month. SmugMug
|
173
|
+
supports the retreival of statistics from up to two months previous, but
|
174
|
+
smugmug only supports the current month.
|
175
|
+
|
176
|
+
stats = album.stats
|
177
|
+
puts stats.bytes
|
178
|
+
puts album.stats.small
|
179
|
+
|
180
|
+
=== Methods
|
181
|
+
|
182
|
+
The following methods are supported for the an Album's statistics. (I need to
|
183
|
+
verify if these definitions are correct.)
|
184
|
+
|
185
|
+
* Bytes (bytes): return bytes downloaded for this album.
|
186
|
+
* Tiny (tiny): retuns the number of tiny images fetched.
|
187
|
+
* Thumb (thumb): retuns the number of thumb images fetched.
|
188
|
+
* Small (small): retuns the number of small images fetched.
|
189
|
+
* Medium (medium): retuns the number of medium images fetched.
|
190
|
+
* Large (large): retuns the number of large images fetched.
|
191
|
+
* Original (original): retuns the number of original images fetched.
|
192
|
+
|
193
|
+
Note: album statistics also have a status, but that information is not returned
|
194
|
+
by smugmug. (I am not really sure what status means.)
|
195
|
+
|
196
|
+
= Images
|
197
|
+
|
198
|
+
Just like the Album class the methods any?, size, and each are available.
|
199
|
+
These methods are attached to an album instance. Likewise, album instances
|
200
|
+
include Enumerable and can be used with any of the enumerable methods, such as
|
201
|
+
inject.
|
202
|
+
|
203
|
+
if album.any?
|
204
|
+
puts "This album contains images."
|
205
|
+
else
|
206
|
+
puts "This album contains no images."
|
207
|
+
end
|
208
|
+
|
209
|
+
puts "This album contains #{album.size} images."
|
210
|
+
puts "This album contains #{album.image_count} images."
|
211
|
+
|
212
|
+
album.each do |image|
|
213
|
+
puts "#{image.file_name} has MD5 Sum #{image.md5_sum}"
|
214
|
+
end
|
215
|
+
|
216
|
+
=== Methods
|
217
|
+
|
218
|
+
Just like with Album there are two functions to access the same values. One
|
219
|
+
based on SmugMug's name, and based on ruby's convention.
|
220
|
+
|
221
|
+
* AlbumID (album_id): returns this image's album ID.
|
222
|
+
* ImageID (image_id): returns this image's ID.
|
223
|
+
* Position (position): returns this image's position.
|
224
|
+
* Serial (serial): returns this image's serial.
|
225
|
+
* Size (size): returns this image's size in bytes.
|
226
|
+
* Width (width): return this image's width in pixels.
|
227
|
+
* Height (height): return this image's height in pixels.
|
228
|
+
* LastUpdated (last_updated): returns the date and time the last time this image was updated.
|
229
|
+
* Caption (caption): returns the caption of this image.
|
230
|
+
* FileName (file_name): returns this image's file name.
|
231
|
+
* MD5Sum (md5_sum): returns this image's MD5 sum.
|
232
|
+
* Watermark (watermark): returns this image's watermark.
|
233
|
+
* Format (format): returns this image's format, such as JPG.
|
234
|
+
* Keywords (keywords): returns this image's keywords.
|
235
|
+
* Date (date): returns the date and time this image was taken (???)
|
236
|
+
* OriginalURL (original_url): return a URL pointing this image's original.
|
237
|
+
* LargeURL (Large_url): return a URL pointing a large version of this image's.
|
238
|
+
* MediumURL (medium_url): return a URL pointing a medium version of this image's.
|
239
|
+
* SmallURL (small_url): return a URL pointing a small version of this image's.
|
240
|
+
* ThumbURL (thumb_url): return a URL pointing a thumb version of this image's.
|
241
|
+
* TinyURL (tiny_url): return a URL pointing a tiny version of this image's.
|
242
|
+
* AlbumURL (album_url): return a URL pointing to this image's album.
|
243
|
+
|
244
|
+
puts "This image is #{image.size} bytes."
|
245
|
+
|
246
|
+
== EXIF
|
247
|
+
|
248
|
+
The method exif is attached to every image instance, and returns the EXIF class
|
249
|
+
attached to this image.
|
250
|
+
|
251
|
+
exif = image.exif
|
252
|
+
puts "The camera that took this image was #{exif.model}."
|
253
|
+
|
254
|
+
=== Methods
|
255
|
+
|
256
|
+
EXIF methods are unique in that SmugMug does not guarantee that all methods are
|
257
|
+
available. This make sense because not all camera manfucaturers report the
|
258
|
+
same data. If you attempt to access data that does not exist the value nil is
|
259
|
+
returned.
|
260
|
+
|
261
|
+
if image.exif.iso.nil?
|
262
|
+
puts "Sorry, no EXIF data is available for 'ISO'"
|
263
|
+
end
|
264
|
+
|
265
|
+
All functions are in the context of this image.
|
266
|
+
|
267
|
+
* ImageId (image_id): returns the image ID.
|
268
|
+
* DateTime (date_time): returns the date and time.
|
269
|
+
* DateTimeOriginal (date_time_original): (???)
|
270
|
+
* DateTimeDigitized (date_time_digitized): returns the date and time when this image was digitized.
|
271
|
+
* Make (make): returns the make of the camera that took.
|
272
|
+
* Model (model): returns the model of the camera that took.
|
273
|
+
* ExposureTime (exposure_time): returns the exposure time.
|
274
|
+
* Aperture (aperture): returns the aperture.
|
275
|
+
* ISO (iso): returns the ISO.
|
276
|
+
* FocalLength (focal_lenght): returns the focal length.
|
277
|
+
* FocalLength35mmFilm (focal_length35mm_film): (???)
|
278
|
+
* CCDWidth (ccdwidth): return the CCD width
|
279
|
+
* CompressedBitsPerPixel (compressed_bits_per_pixel): returns the compressed bits per pixel.
|
280
|
+
* Flash (flash): (???)
|
281
|
+
* Metering (metering): (???)
|
282
|
+
* ExposureProgram (exposure_program): (???)
|
283
|
+
* SubjectDistance (subject_distance): return the distance to the subject.
|
284
|
+
* SubjectDistanceRange (subject_distance_range): return an range of the distance to the subject.
|
285
|
+
* SensingMethod (sensing_method): returns the sensing method.
|
286
|
+
* ColorSpace (color_space): returns the color space.
|
287
|
+
* Brightness (brightness): returns the brigtness.
|
288
|
+
|
289
|
+
exif = image.exif
|
290
|
+
puts "This image was taked on #{exif.date_time}."
|
291
|
+
|
292
|
+
== Stats
|
293
|
+
|
294
|
+
Image stats are identical to album stats except that they apply to a specific
|
295
|
+
image.
|
296
|
+
|
297
|
+
=== Methods
|
298
|
+
|
299
|
+
The following methods are supported for the an image's statistics. (I need to
|
300
|
+
verify if these definitions are correct.)
|
301
|
+
|
302
|
+
* Bytes (bytes): return bytes downloaded.
|
303
|
+
* Tiny (tiny): downloads of the tiny representation.
|
304
|
+
* Thumb (thumb): downloads of the thumb representation.
|
305
|
+
* Small (small): downloads of the small representation.
|
306
|
+
* Medium (medium): downloads of the medium representation.
|
307
|
+
* Large (large): downloads of the large representation.
|
308
|
+
* Original (original): downloads of the original.
|
309
|
+
|
310
|
+
puts "The original of this image was downloaded #{image.stats.original} times for #{Time.now.month}/#{Time.now.year}"
|
data/doc/TODO
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- text -*-
|
2
|
+
# $Hg$
|
3
|
+
|
4
|
+
== Documentation
|
5
|
+
|
6
|
+
* You'll never have enough!
|
7
|
+
|
8
|
+
== Library
|
9
|
+
|
10
|
+
* More testing
|
11
|
+
a. extend the SmugMug servlet
|
12
|
+
a. make login more realistic
|
13
|
+
a. automatically generate fake test data
|
14
|
+
a. use SQLite has the backend for the servlet
|
15
|
+
* Add write support
|
16
|
+
a. adding new albums
|
17
|
+
a. uploading new images
|
18
|
+
a. modifying album, and image attributes
|
19
|
+
* Add support for categories, and sub categories
|
20
|
+
* Add support for password protected galleries
|
21
|
+
* Add support for a date type to MethodTable.
|
22
|
+
* Add support for explicitly specifying an alias for a MethodTable.
|
23
|
+
* Add support for scrubbing the data generated by canned.
|
24
|
+
* Factor StatsBase out into base/stats.rb
|
25
|
+
|
26
|
+
== Scripts
|
27
|
+
|
28
|
+
* Add a script to query *all* of the SmugMug data and stick it in a
|
29
|
+
SQLite database.
|
30
|
+
* Add a script to verify the integrity of one's photos.
|
31
|
+
* Add a poor man's rsync script to upload missing photos to SmugMug.
|
32
|
+
* Add a script to move photos between SmugMug and Picasa.
|
data/lib/net/httpz.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# $Hg$
|
3
|
+
|
4
|
+
require 'net/https'
|
5
|
+
require 'stringio'
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
module Net
|
9
|
+
class HTTP
|
10
|
+
alias original_get get
|
11
|
+
def get(path, initheader = nil, dest = nil, &block)
|
12
|
+
headers = { 'Accept-Encoding' => 'gzip' }
|
13
|
+
headers.merge!(initheader) unless initheader.nil?
|
14
|
+
|
15
|
+
return original_get(path, headers, dest, &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class HTTPResponse
|
20
|
+
def gzip?
|
21
|
+
return false unless @header.has_key?('content-encoding')
|
22
|
+
return @header['content-encoding'].to_s == 'gzip'
|
23
|
+
end
|
24
|
+
|
25
|
+
alias original_body body
|
26
|
+
def body
|
27
|
+
data = original_body
|
28
|
+
return (gzip?) ? Zlib::GzipReader.new(StringIO.new(data)).read : data
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/smugmug.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# $Hg: smugmug.rb,v c21b2f4bec8c 2007/08/11 08:00:21 boumenot $
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'logger'
|
6
|
+
require 'net/httpz'
|
7
|
+
require 'rexml/document'
|
8
|
+
require 'stringio'
|
9
|
+
require 'time'
|
10
|
+
require 'uri'
|
11
|
+
require 'zlib'
|
12
|
+
|
13
|
+
require 'smugmug/albums'
|
14
|
+
require 'smugmug/cache'
|
15
|
+
require 'smugmug/config'
|
16
|
+
require 'smugmug/util'
|
17
|
+
|
18
|
+
module SmugMug
|
19
|
+
|
20
|
+
API_VERSION='1.1.1'
|
21
|
+
API_KEY='dydlCYDzmykqoLkVGgECEIP5WEuwWFzc'
|
22
|
+
URL="https://api.smugmug.com/hack/rest/#{API_VERSION}/"
|
23
|
+
|
24
|
+
VERSION = '0.0.1'
|
25
|
+
USERAGENT = "SmugMug Ruby Library - #{VERSION} " + '($HgRev: c21b2f4bec8ce187d5bcb5cd75a6076c40c0cf5f $)'
|
26
|
+
|
27
|
+
class SmugMugError < RuntimeError
|
28
|
+
end
|
29
|
+
|
30
|
+
class NotModified < SmugMugError
|
31
|
+
end
|
32
|
+
|
33
|
+
class SmugMug
|
34
|
+
include Enumerable
|
35
|
+
|
36
|
+
extend Forwardable
|
37
|
+
def_delegator :@config, :AccountType, :account_type
|
38
|
+
def_delegator :albums, :each, :each
|
39
|
+
def_delegator :albums, :any?, :any?
|
40
|
+
def_delegator :albums, :size, :size
|
41
|
+
|
42
|
+
attr_reader :base, :session_id
|
43
|
+
|
44
|
+
def initialize(*args)
|
45
|
+
if $log.nil?
|
46
|
+
$log = if File.exists?('/dev/null')
|
47
|
+
Logger.new('/dev/null')
|
48
|
+
else
|
49
|
+
Logger.new(StringIO.new)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
@base = ENV.has_key?('SMUGMUG_URL') ? ENV['SMUGMUG_URL'] : URL
|
54
|
+
@config = Config.new()
|
55
|
+
login(*args)
|
56
|
+
end
|
57
|
+
|
58
|
+
def fetch(params={})
|
59
|
+
query = build_url(params)
|
60
|
+
data = get(query)
|
61
|
+
doc = REXML::Document.new(data)
|
62
|
+
|
63
|
+
if doc.root.attributes['stat'] == 'fail'
|
64
|
+
raise SmugMugError,
|
65
|
+
"#{doc.root.elements['err'].attributes['code']}" +
|
66
|
+
"- #{doc.root.elements['err'].attributes['msg']}"
|
67
|
+
end
|
68
|
+
|
69
|
+
return doc
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def albums
|
75
|
+
@albums = Albums.new(self) if @albums.nil?
|
76
|
+
return @albums
|
77
|
+
end
|
78
|
+
|
79
|
+
def build_url(params)
|
80
|
+
query = base + '?'
|
81
|
+
|
82
|
+
$log.debug('SmugMug#build_url') { "Params:" }
|
83
|
+
params.each do |k,v|
|
84
|
+
$log.debug('SmugMug#build_url') { " #{k}=#{v}" }
|
85
|
+
query += "#{k}=" + URI.encode(v.to_s) + '&'
|
86
|
+
end
|
87
|
+
|
88
|
+
return query
|
89
|
+
end
|
90
|
+
|
91
|
+
public
|
92
|
+
|
93
|
+
def get(url, headers={})
|
94
|
+
# XXX: this is a hack, think of a better solution
|
95
|
+
url.sub!(/^https:/, 'http:') unless url =~ /method=smugmug\.login/
|
96
|
+
$log.info('SmugMug#get') { url }
|
97
|
+
|
98
|
+
headers['User-Agent'] = USERAGENT
|
99
|
+
uri = URI.parse(url)
|
100
|
+
|
101
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
102
|
+
|
103
|
+
if uri.scheme == 'https'
|
104
|
+
http.use_ssl = true
|
105
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
106
|
+
end
|
107
|
+
|
108
|
+
http.start {
|
109
|
+
response = http.get(uri.path + '?' + uri.query, headers)
|
110
|
+
|
111
|
+
$log.info('SmugMug#get') { "HTTP #{response.code} (#{response.message})" }
|
112
|
+
$log.debug('SmugMug#get') { "Headers (response):" }
|
113
|
+
response.each_header { |k,v| $log.debug('SmugMug#get') { " #{k}: #{v}" } }
|
114
|
+
|
115
|
+
case response.code.to_i
|
116
|
+
when 200
|
117
|
+
body = response.body
|
118
|
+
$log.debug('SmugMug#get') { body }
|
119
|
+
return body
|
120
|
+
when 304
|
121
|
+
$log.debug('SmugMug#get') { 'If-Modified-Since is false' }
|
122
|
+
raise NotModified, "#{url} has not changed"
|
123
|
+
else
|
124
|
+
raise SmugMugError, "GET of #{url} failed"
|
125
|
+
end
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def post(url, body, headers={})
|
130
|
+
end
|
131
|
+
|
132
|
+
def login(*args)
|
133
|
+
params = {}
|
134
|
+
if @config.PasswordHash.nil?
|
135
|
+
raise SmugMugError, "Login by password requires a email address, and password" if args.size != 2
|
136
|
+
|
137
|
+
params[:method] = 'smugmug.login.withPassword'
|
138
|
+
params[:EmailAddress] = args.shift
|
139
|
+
params[:Password] = args.shift
|
140
|
+
|
141
|
+
@config.EmailAddress = params[:EmailAddress]
|
142
|
+
else
|
143
|
+
params[:method] = 'smugmug.login.withHash'
|
144
|
+
%w{UserID PasswordHash}.each do |var|
|
145
|
+
params[var.to_sym] = @config.send(var.to_sym)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
params[:APIKey] = API_KEY
|
150
|
+
@login_doc = fetch(params).root.elements['Login']
|
151
|
+
|
152
|
+
@session_id = @login_doc.elements['SessionID'].text
|
153
|
+
|
154
|
+
Config::VARIABLES.each do |var|
|
155
|
+
@config.send("#{var}=".to_sym, @login_doc.elements[var].text) unless @login_doc.elements[var].nil?
|
156
|
+
end
|
157
|
+
@config.save
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
def main
|
164
|
+
unless ARGV.length == 2
|
165
|
+
puts "#{$0} Usage: <email> <password>"
|
166
|
+
Kernel.exit(1)
|
167
|
+
end
|
168
|
+
|
169
|
+
File.unlink('smugmug.log') if File.exists?('smugmug.log')
|
170
|
+
$log = Logger.new('smugmug.log')
|
171
|
+
sm = SmugMug::SmugMug.new(ARGV.shift, ARGV.shift)
|
172
|
+
sm.each do |album|
|
173
|
+
puts "#{album.id} -> #{album.title} (#{album.ImageCount})"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
if __FILE__ == $0
|
178
|
+
main()
|
179
|
+
end
|