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.
Files changed (49) hide show
  1. data/HISTORY +2 -0
  2. data/LICENSE +19 -0
  3. data/MANIFEST +48 -0
  4. data/README +30 -0
  5. data/Rakefile +100 -0
  6. data/bin/smcli +225 -0
  7. data/bin/smugmug2sql +158 -0
  8. data/doc/API +310 -0
  9. data/doc/TODO +32 -0
  10. data/lib/net/httpz.rb +31 -0
  11. data/lib/smugmug.rb +179 -0
  12. data/lib/smugmug/album/info.rb +131 -0
  13. data/lib/smugmug/album/stats.rb +31 -0
  14. data/lib/smugmug/albums.rb +39 -0
  15. data/lib/smugmug/base.rb +104 -0
  16. data/lib/smugmug/cache.rb +33 -0
  17. data/lib/smugmug/config.rb +48 -0
  18. data/lib/smugmug/image/exif.rb +72 -0
  19. data/lib/smugmug/image/info.rb +88 -0
  20. data/lib/smugmug/image/stats.rb +32 -0
  21. data/lib/smugmug/images.rb +52 -0
  22. data/lib/smugmug/table.rb +133 -0
  23. data/lib/smugmug/util.rb +12 -0
  24. data/test/album.rb +359 -0
  25. data/test/config.rb +39 -0
  26. data/test/httpz.rb +120 -0
  27. data/test/image.rb +540 -0
  28. data/test/login.rb +24 -0
  29. data/test/runner.rb +83 -0
  30. data/test/servlet.rb +257 -0
  31. data/test/table.rb +113 -0
  32. data/xml/canned +212 -0
  33. data/xml/fail/empty.set.xml +4 -0
  34. data/xml/fail/invalid.apikey.xml +4 -0
  35. data/xml/fail/invalid.login.xml +4 -0
  36. data/xml/fail/invalid.method.xml +4 -0
  37. data/xml/fail/invalid.user.xml +4 -0
  38. data/xml/fail/system.error.xml +4 -0
  39. data/xml/standard/albums.get.xml +24 -0
  40. data/xml/standard/albums.getInfo.xml +38 -0
  41. data/xml/standard/albums.getStats.xml +43 -0
  42. data/xml/standard/categories.get.xml +213 -0
  43. data/xml/standard/images.get.xml +9 -0
  44. data/xml/standard/images.getEXIF.xml +34 -0
  45. data/xml/standard/images.getInfo.xml +29 -0
  46. data/xml/standard/images.getStats.xml +15 -0
  47. data/xml/standard/login.withHash.xml +7 -0
  48. data/xml/standard/login.withPassword.xml +10 -0
  49. metadata +103 -0
data/xml/canned ADDED
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env ruby
2
+ # $Hg$
3
+
4
+ require 'net/https'
5
+ require 'rexml/document'
6
+ require 'optparse'
7
+ require 'uri'
8
+
9
+ API_KEY='dydlCYDzmykqoLkVGgECEIP5WEuwWFzc'
10
+ USERAGENT='$HgRev$'
11
+
12
+ class File
13
+ def File.write(filename, data, offset=0)
14
+ File.open(filename, 'w') do |fh|
15
+ fh.seek(offset)
16
+ fh.write(data)
17
+ end
18
+ end
19
+ end
20
+
21
+ class Tuna
22
+ BASE='://api.smugmug.com/hack/rest/1.1.1/'
23
+
24
+ attr_reader :album_id, :image_id, :session_id
25
+ attr_accessor :headers, :password, :username
26
+
27
+ def initialize()
28
+ @headers = {}
29
+ end
30
+
31
+ def album_id=(val) headers[:AlbumID] = val ; end
32
+ def image_id=(val) headers[:ImageID] = val ; end
33
+ def session_id=(val) headers[:SessionID] = val ; end
34
+
35
+ def get(method, params={})
36
+ params['method'] = method
37
+ params.merge!(headers)
38
+
39
+ url = (method =~ /login/) ? 'https' : 'http'
40
+ url += BASE + '?'
41
+ params.each { |k,v| url += "#{k}=" + URI.encode(v.to_s) + '&' }
42
+
43
+ uri = URI.parse(url)
44
+
45
+ http = Net::HTTP.new(uri.host, uri.port)
46
+ if uri.scheme == 'https'
47
+ http.use_ssl = true
48
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
49
+ end
50
+
51
+ http.start {
52
+ response = http.get(uri.path + '?' + uri.query, {'User-Agent' => USERAGENT})
53
+ raise "fetch of #{uri} failed, code #{response.code}" unless response.code.to_i == 200
54
+
55
+ @doc = response.body
56
+ }
57
+
58
+ return @doc
59
+ end
60
+
61
+ def walk
62
+ data = {}
63
+ methods = %w{login_withPassword login_withHash
64
+ albums_get albums_get_heavy albums_getInfo albums_getStats
65
+ images_get images_get_heavy images_getInfo images_getStats images_getEXIF images_getURLs
66
+ categories_get
67
+ }
68
+
69
+ methods.each do |method|
70
+ puts "Processing #{method} ..."
71
+ data[method] = self.send(method.to_sym)
72
+ end
73
+
74
+ return data
75
+ end
76
+
77
+ ## login ###########################################################
78
+
79
+ def login_withPassword
80
+ return unless session_id.nil?
81
+ resp = get('smugmug.login.withPassword',
82
+ {:EmailAddress => username, :Password => password, :APIKey => API_KEY})
83
+
84
+ doc = REXML::Document.new(resp)
85
+
86
+ @account_type = doc.root.elements['Login/AccountType'].text
87
+ @user_id = doc.root.elements['Login/UserID'].text
88
+ @password_hash = doc.root.elements['Login/PasswordHash'].text
89
+
90
+ return resp
91
+ end
92
+ def login_withHash
93
+ return unless session_id.nil?
94
+ resp = get('smugmug.login.withHash',
95
+ {:UserID => @user_id, :PasswordHash => @password_hash, :APIKey => API_KEY})
96
+
97
+ doc = REXML::Document.new(resp)
98
+ @session_id = doc.root.elements['Login/SessionID'].text
99
+
100
+ headers['SessionID'] = @session_id
101
+ return resp
102
+ end
103
+
104
+ ## albums ##########################################################
105
+
106
+ def albums_get
107
+ resp = get('smugmug.albums.get')
108
+
109
+ if album_id.nil?
110
+ doc = REXML::Document.new(resp)
111
+ @album_id = REXML::XPath.first(doc, '//Album/attribute::id')
112
+ headers['AlbumID'] = @album_id
113
+ end
114
+
115
+ return resp
116
+ end
117
+ def albums_get_heavy
118
+ return get('smugmug.albums.get', {:Heavy => 1})
119
+ end
120
+ def albums_getInfo
121
+ return get('smugmug.albums.getInfo', {:AlbumID => album_id})
122
+ end
123
+ def albums_getStats
124
+ return get('smugmug.albums.getStats',
125
+ {:AlbumID => album_id, :Month => Time.now.month, :Year => Time.now.year})
126
+ end
127
+
128
+ ## images ##########################################################
129
+
130
+ def images_get
131
+ resp = get('smugmug.images.get', {:AlbumID => album_id})
132
+
133
+ if image_id.nil?
134
+ doc = REXML::Document.new(resp)
135
+ @image_id = REXML::XPath.first(doc, '//Image/attribute::id')
136
+ headers[:ImageID] = @image_id
137
+ end
138
+
139
+ return resp
140
+ end
141
+ def images_get_heavy
142
+ return get('smugmug.images.get', {:AlbumID => album_id, :Heavy => 1})
143
+ end
144
+ def images_getInfo
145
+ return get('smugmug.images.getInfo', {:ImageID => image_id})
146
+ end
147
+ def images_getStats
148
+ return get('smugmug.images.getStats', {:ImageID => image_id})
149
+ end
150
+ def images_getEXIF
151
+ return get('smugmug.images.getEXIF', {:ImageID => image_id})
152
+ end
153
+ def images_getURLs
154
+ return get('smugmug.images.getURLs', {:ImageID => image_id})
155
+ end
156
+
157
+ ## categories ######################################################
158
+
159
+ def categories_get
160
+ return get('smugmug.categories.get')
161
+ end
162
+ end
163
+
164
+
165
+ def main()
166
+ options = {}
167
+
168
+ OptionParser.new do |opts|
169
+ opts.banner = "Usage: canned [options]"
170
+
171
+ opts.on("-p", "--password PASSWORD", "SmugMug password") do |v|
172
+ options[:password] = v
173
+ end
174
+ opts.on("-u", "--username USERNAME", "SmugMug email address") do |v|
175
+ options[:username] = v
176
+ end
177
+ opts.on("-s", "--session-id SessionID", "Use a specific SessionID") do |v|
178
+ options[:session_id] = v
179
+ end
180
+ opts.on("-i", "--image-id ImageID", "Use a specific ImageID") do |v|
181
+ options[:image_id] = v
182
+ end
183
+ opts.on("-a", "--album-id AlbumID", "Use a specific AlbumID") do |v|
184
+ options[:album_id] = v
185
+ end
186
+ opts.on_tail("-h", "--help", "Show this message") do
187
+ puts opts
188
+ Kernel.exit
189
+ end
190
+ end.parse!
191
+
192
+ unless options.has_key?(:username) or options.has_key?(:password) or options.has_key?(:session_id)
193
+ puts "%Error: Must specify --username and --password, or --session-id!"
194
+ raise SystemExit
195
+ end
196
+
197
+ if options.has_key?(:username) ^ options.has_key?(:password)
198
+ puts "%Error: Must specify both --username and --password"
199
+ raise SystemExit
200
+ end
201
+
202
+ tuna = Tuna.new
203
+ options.each do |k,v|
204
+ tuna.send("#{k.to_s.gsub('-', '_')}=".to_sym, v)
205
+ end
206
+
207
+ tuna.walk.each { |k,v| File.write("#{k.sub('smugmug.', '').gsub('_', '.')}.xml", v) }
208
+ end
209
+
210
+ if __FILE__ == $0
211
+ main()
212
+ end
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <rsp stat="fail">
3
+ <err code="15" msg="empty set - no subcategories found"/>
4
+ </rsp>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <rsp stat="fail">
3
+ <err code="18" msg="invalid API key"/>
4
+ </rsp>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <rsp stat="fail">
3
+ <err code="1" msg="invalid login"/>
4
+ </rsp>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <rsp stat="fail">
3
+ <err code="17" msg="invalid method"/>
4
+ </rsp>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <rsp stat="fail">
3
+ <err code="4" msg="invalid user"/>
4
+ </rsp>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <rsp stat="fail">
3
+ <err code="5" msg="system error"/>
4
+ </rsp>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <rsp stat="ok">
3
+ <method>smugmug.albums.get</method>
4
+ <Albums>
5
+ <Album id="1001">
6
+ <Title>Gallery One</Title>
7
+ <Category id="47">
8
+ <Title>Children</Title>
9
+ </Category>
10
+ </Album>
11
+ <Album id="1002">
12
+ <Title>Gallery Two</Title>
13
+ <Category id="47">
14
+ <Title>Children</Title>
15
+ </Category>
16
+ </Album>
17
+ <Album id="1003">
18
+ <Title>Gallery Three</Title>
19
+ <Category id="44">
20
+ <Title>Parties</Title>
21
+ </Category>
22
+ </Album>
23
+ </Albums>
24
+ </rsp>
@@ -0,0 +1,38 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <rsp stat="ok">
3
+ <method>smugmug.albums.getInfo</method>
4
+ <Albums>
5
+ <Album id="1001">
6
+ <Highlight id="0" />
7
+ <Category id="47" />
8
+ <SubCategory id="0" />
9
+ <Community id="0" />
10
+ <Position>1</Position>
11
+ <ImageCount>3</ImageCount>
12
+ <Title>Gallery One</Title>
13
+ <Description>description</Description>
14
+ <Keywords>keywords</Keywords>
15
+ <Public>1</Public>
16
+ <Password />
17
+ <PasswordHint />
18
+ <Printable>1</Printable>
19
+ <Filenames>0</Filenames>
20
+ <Comments>1</Comments>
21
+ <External>0</External>
22
+ <Originals>1</Originals>
23
+ <EXIF>1</EXIF>
24
+ <Share>1</Share>
25
+ <SortMethod>Position</SortMethod>
26
+ <SortDirection>0</SortDirection>
27
+ <LastUpdated>1970-01-01 00:00:00</LastUpdated>
28
+ <FamilyEdit>0</FamilyEdit>
29
+ <FriendEdit>0</FriendEdit>
30
+ <HideOwner>0</HideOwner>
31
+ <CanRank>1</CanRank>
32
+ <Clean>0</Clean>
33
+ <Geography>1</Geography>
34
+ <SmugSearchable>0</SmugSearchable>
35
+ <WorldSearchable>0</WorldSearchable>
36
+ </Album>
37
+ </Albums>
38
+ </rsp>
@@ -0,0 +1,43 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <rsp stat="ok">
3
+ <method>smugmug.albums.getStats</method>
4
+ <Album id="1001">
5
+ <Status>Open</Status>
6
+ <Bytes>234659</Bytes>
7
+ <Tiny>20</Tiny>
8
+ <Thumb>2</Thumb>
9
+ <Small>0</Small>
10
+ <Medium>2</Medium>
11
+ <Large>0</Large>
12
+ <Original>0</Original>
13
+ <Images>
14
+ <Image id="1001">
15
+ <Bytes>0</Bytes>
16
+ <Tiny>0</Tiny>
17
+ <Thumb>0</Thumb>
18
+ <Small>0</Small>
19
+ <Medium>0</Medium>
20
+ <Large>0</Large>
21
+ <Original>0</Original>
22
+ </Image>
23
+ <Image id="1002">
24
+ <Bytes>0</Bytes>
25
+ <Tiny>0</Tiny>
26
+ <Thumb>0</Thumb>
27
+ <Small>0</Small>
28
+ <Medium>0</Medium>
29
+ <Large>0</Large>
30
+ <Original>0</Original>
31
+ </Image>
32
+ <Image id="1003">
33
+ <Bytes>0</Bytes>
34
+ <Tiny>0</Tiny>
35
+ <Thumb>0</Thumb>
36
+ <Small>0</Small>
37
+ <Medium>0</Medium>
38
+ <Large>0</Large>
39
+ <Original>0</Original>
40
+ </Image>
41
+ </Images>
42
+ </Album>
43
+ </rsp>
@@ -0,0 +1,213 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <rsp stat="ok">
3
+ <method>smugmug.categories.get</method>
4
+ <Categories>
5
+ <Category id="0">
6
+ <Title>Other</Title>
7
+ </Category>
8
+ <Category id="41">
9
+ <Title>Airplanes</Title>
10
+ </Category>
11
+ <Category id="1">
12
+ <Title>Animals</Title>
13
+ </Category>
14
+ <Category id="25">
15
+ <Title>Aquariums</Title>
16
+ </Category>
17
+ <Category id="2">
18
+ <Title>Architecture</Title>
19
+ </Category>
20
+ <Category id="3">
21
+ <Title>Art</Title>
22
+ </Category>
23
+ <Category id="43">
24
+ <Title>Arts and Crafts</Title>
25
+ </Category>
26
+ <Category id="39">
27
+ <Title>Births</Title>
28
+ </Category>
29
+ <Category id="4">
30
+ <Title>Boats</Title>
31
+ </Category>
32
+ <Category id="5">
33
+ <Title>Business</Title>
34
+ </Category>
35
+ <Category id="6">
36
+ <Title>Cars</Title>
37
+ </Category>
38
+ <Category id="47">
39
+ <Title>Children</Title>
40
+ </Category>
41
+ <Category id="7">
42
+ <Title>Competitions</Title>
43
+ </Category>
44
+ <Category id="26">
45
+ <Title>Computers</Title>
46
+ </Category>
47
+ <Category id="158734">
48
+ <Title>Construction</Title>
49
+ </Category>
50
+ <Category id="48">
51
+ <Title>Dance</Title>
52
+ </Category>
53
+ <Category id="40">
54
+ <Title>Electronics</Title>
55
+ </Category>
56
+ <Category id="8">
57
+ <Title>Events</Title>
58
+ </Category>
59
+ <Category id="9">
60
+ <Title>Family</Title>
61
+ </Category>
62
+ <Category id="10">
63
+ <Title>Fashion</Title>
64
+ </Category>
65
+ <Category id="51">
66
+ <Title>Flowers</Title>
67
+ </Category>
68
+ <Category id="57">
69
+ <Title>Food</Title>
70
+ </Category>
71
+ <Category id="45">
72
+ <Title>Friends</Title>
73
+ </Category>
74
+ <Category id="56">
75
+ <Title>Funerals</Title>
76
+ </Category>
77
+ <Category id="32">
78
+ <Title>Genealogy</Title>
79
+ </Category>
80
+ <Category id="63">
81
+ <Title>Grandchildren</Title>
82
+ </Category>
83
+ <Category id="11">
84
+ <Title>Groups</Title>
85
+ </Category>
86
+ <Category id="59">
87
+ <Title>History</Title>
88
+ </Category>
89
+ <Category id="12">
90
+ <Title>Hobbies</Title>
91
+ </Category>
92
+ <Category id="31">
93
+ <Title>Holidays</Title>
94
+ </Category>
95
+ <Category id="30">
96
+ <Title>Humor</Title>
97
+ </Category>
98
+ <Category id="53">
99
+ <Title>Jewelry</Title>
100
+ </Category>
101
+ <Category id="62">
102
+ <Title>Journalism</Title>
103
+ </Category>
104
+ <Category id="13">
105
+ <Title>Landscapes</Title>
106
+ </Category>
107
+ <Category id="54">
108
+ <Title>Machines</Title>
109
+ </Category>
110
+ <Category id="67520">
111
+ <Title>Military</Title>
112
+ </Category>
113
+ <Category id="14">
114
+ <Title>Motorcycles</Title>
115
+ </Category>
116
+ <Category id="29">
117
+ <Title>Movies</Title>
118
+ </Category>
119
+ <Category id="34">
120
+ <Title>Music</Title>
121
+ </Category>
122
+ <Category id="15">
123
+ <Title>Nature</Title>
124
+ </Category>
125
+ <Category id="44">
126
+ <Title>Parties</Title>
127
+ </Category>
128
+ <Category id="16">
129
+ <Title>People</Title>
130
+ </Category>
131
+ <Category id="167961">
132
+ <Title>Performance</Title>
133
+ </Category>
134
+ <Category id="37">
135
+ <Title>Pets</Title>
136
+ </Category>
137
+ <Category id="27">
138
+ <Title>Photography</Title>
139
+ </Category>
140
+ <Category id="55">
141
+ <Title>Politics</Title>
142
+ </Category>
143
+ <Category id="28">
144
+ <Title>Portfolio</Title>
145
+ </Category>
146
+ <Category id="52">
147
+ <Title>Portraits</Title>
148
+ </Category>
149
+ <Category id="159296">
150
+ <Title>Pregnancy</Title>
151
+ </Category>
152
+ <Category id="17">
153
+ <Title>Professional</Title>
154
+ </Category>
155
+ <Category id="36">
156
+ <Title>Religion</Title>
157
+ </Category>
158
+ <Category id="46">
159
+ <Title>School</Title>
160
+ </Category>
161
+ <Category id="160095">
162
+ <Title>Shower</Title>
163
+ </Category>
164
+ <Category id="35">
165
+ <Title>Spirituality</Title>
166
+ </Category>
167
+ <Category id="18">
168
+ <Title>Sports</Title>
169
+ </Category>
170
+ <Category id="19">
171
+ <Title>Still Life</Title>
172
+ </Category>
173
+ <Category id="50">
174
+ <Title>Street Scenes</Title>
175
+ </Category>
176
+ <Category id="159294">
177
+ <Title>Swimming</Title>
178
+ </Category>
179
+ <Category id="49">
180
+ <Title>Theater</Title>
181
+ </Category>
182
+ <Category id="42">
183
+ <Title>Trains</Title>
184
+ </Category>
185
+ <Category id="20">
186
+ <Title>Travel</Title>
187
+ </Category>
188
+ <Category id="21">
189
+ <Title>Trucks</Title>
190
+ </Category>
191
+ <Category id="58">
192
+ <Title>Underwater</Title>
193
+ </Category>
194
+ <Category id="33">
195
+ <Title>Vacation</Title>
196
+ </Category>
197
+ <Category id="24">
198
+ <Title>Video Games</Title>
199
+ </Category>
200
+ <Category id="61">
201
+ <Title>Weather</Title>
202
+ </Category>
203
+ <Category id="23">
204
+ <Title>Weddings</Title>
205
+ </Category>
206
+ <Category id="38">
207
+ <Title>Woodworking</Title>
208
+ </Category>
209
+ <Category id="60">
210
+ <Title>Zoos</Title>
211
+ </Category>
212
+ </Categories>
213
+ </rsp>