upflickr 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/CHANGELOG +4 -0
  2. data/README +94 -0
  3. data/lib/multipartpost.rb +55 -0
  4. data/lib/upflickr.rb +295 -0
  5. metadata +83 -0
data/CHANGELOG ADDED
@@ -0,0 +1,4 @@
1
+ v0.1.6 - 06-06-2010
2
+ -new methods: list_photos, add_photo, remove_photo, delete_photo, license_photo, list_sets, create_set, delete_set
3
+ -improved error handling
4
+ -updated README
data/README ADDED
@@ -0,0 +1,94 @@
1
+ #######################
2
+ # upflickr v0.1.6 #
3
+ #######################
4
+ #
5
+ # Upflickr makes it easier to upload files to flickr using the flickr api.
6
+ #
7
+ # you will need the api_key and secret values for your flickr application.
8
+ #
9
+ #######################
10
+ # ATTRIBUTES #
11
+ #######################
12
+
13
+ api_key
14
+
15
+ secret
16
+
17
+ token
18
+
19
+ #######################
20
+ # METHODS #
21
+ #######################
22
+ # (all methods return false on failure)
23
+
24
+ add_photo
25
+ args: photo_id, set_id
26
+ returns: true
27
+
28
+ Add a photo to a set.
29
+
30
+ check_token
31
+ args: none
32
+ returns: true
33
+
34
+ Check if the current authentication token is still valid.
35
+
36
+ create_set
37
+ args: photo_id, title, description (optional)
38
+ returns: set_id
39
+
40
+ Create a new photoset.
41
+
42
+ delete_photo
43
+ args: photo_id
44
+ returns: true
45
+
46
+ Delete a photo.
47
+
48
+ delete_set
49
+ args: set_id
50
+ returns: true
51
+
52
+ Delete a photoset.
53
+
54
+ get_auth_url
55
+ args: none
56
+ returns: authorization_url
57
+
58
+ Gets the unique authentication URL for confirming read/write/delete access. You must load this URL in a web browser prior to attempting get_token.
59
+
60
+ get_token
61
+ args: none
62
+ returns: token
63
+
64
+ Get an authentication token after visiting the authentication URL and clicking confirm. This token may be used with the above 'token' method to set the token in a new instance without re-authenticating.
65
+
66
+ license_photo
67
+ args: photo_id, license_id
68
+ returns: true
69
+
70
+ Specify a license for a photo. License IDs can be found at http://www.flickr.com/services/api/flickr.photos.licenses.getInfo.html
71
+
72
+ list_photos
73
+ args: set_id (optional)
74
+ returns: hash {photo_id => title}
75
+
76
+ Without argument, lists all photos not assigned to a set. With set_id argument, lists all photos in the given set.
77
+
78
+ list_sets
79
+ args: none
80
+ returns: hash {set_id => title}
81
+
82
+ List all the photosets for the current user.
83
+
84
+ remove_photo
85
+ args: photo_id, set_id
86
+ returns: true
87
+
88
+ Remove a photo from a set.
89
+
90
+ upload
91
+ args: hash {arg => value}
92
+ returns: photo_id
93
+
94
+ Upload a new image. Create a hash using any of the arguments from http://www.flickr.com/services/api/upload.api.html as keys. All arguments are optional except 'photo'. Therefore, the minimum required argument is: args{'photo' => "/path/to/image1.jpg"}
@@ -0,0 +1,55 @@
1
+ #--
2
+ # Helper class to prepare an HTTP POST request with a file upload
3
+ # Mostly taken from http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/113774
4
+ # Anything that's broken and wrong probably the fault of Bill Stilwell (bill@marginalia.org)
5
+ #++
6
+
7
+ require 'rubygems'
8
+ require 'mime/types'
9
+ require 'net/http'
10
+ require 'cgi'
11
+
12
+ class Param
13
+ attr_accessor :k, :v
14
+ def initialize( k, v )
15
+ @k = k
16
+ @v = v
17
+ end
18
+
19
+ def to_multipart
20
+ return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"\r\n\r\n#{v}\r\n"
21
+ end
22
+ end
23
+
24
+ class FileParam
25
+ attr_accessor :k, :filename, :content
26
+ def initialize( k, filename, content )
27
+ @k = k
28
+ @filename = filename
29
+ @content = content
30
+ end
31
+
32
+ def to_multipart
33
+ return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"; filename=\"#{filename}\"\r\n" +
34
+ "Content-Transfer-Encoding: binary\r\n" +
35
+ "Content-Type: #{MIME::Types.type_for(@filename)}\r\n\r\n" + content + "\r\n"
36
+ end
37
+ end
38
+
39
+ class MultipartPost
40
+ BOUNDARY = 'flickrrocks-aaaaaabbbb0000'
41
+ HEADER = {"Content-type" => "multipart/form-data, boundary=" + BOUNDARY + " "}
42
+
43
+ def prepare_query ( params )
44
+ fp = []
45
+ params.each do |k,v|
46
+ if v.respond_to?(:read)
47
+ fp.push(FileParam.new(k,v.path,v.read))
48
+ else
49
+ fp.push(Param.new(k,v))
50
+ end
51
+ end
52
+ query = fp.collect {|p| "--" + BOUNDARY + "\r\n" + p.to_multipart }.join("") + "--" + BOUNDARY + "--"
53
+ return query, HEADER
54
+ end
55
+ end
data/lib/upflickr.rb ADDED
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ require 'open-uri'
4
+ require 'digest/md5'
5
+ require 'rexml/document'
6
+ require 'multipartpost'
7
+
8
+ class Upflickr
9
+ API_URL = 'http://api.flickr.com/services/rest/?'
10
+ attr_accessor :api_key
11
+ attr_accessor :secret
12
+ attr_accessor :token
13
+
14
+ def check_attr(token=false)
15
+ if token == false
16
+ if @api_key != nil && @secret != nil
17
+ return true
18
+ else
19
+ puts "UPFLICKR: api_key and/or secret not set"
20
+ return false
21
+ end
22
+ else
23
+ if @api_key != nil && @secret != nil && @token != nil
24
+ return true
25
+ else
26
+ puts "UPFLICKR: api_key, secret, and/or token not set"
27
+ return false
28
+ end
29
+ end
30
+ end
31
+
32
+ def sign(s)
33
+ return Digest::MD5.hexdigest(s)
34
+ end
35
+
36
+ def remove_white(s)
37
+ return s.gsub(" ","+")
38
+ end
39
+
40
+ def get_auth_url
41
+ if self.check_attr == true
42
+ sig = self.sign("#{@secret}api_key#{@api_key}methodflickr.auth.getFrob")
43
+ data = open("#{API_URL}method=flickr.auth.getFrob&api_key=#{@api_key}&api_sig=#{sig}")
44
+ doc = REXML::Document.new(data)
45
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
46
+ @frob = doc.elements["//frob"].text.strip
47
+ sig = self.sign("#{@secret}api_key#{@api_key}frob#{@frob}permsdelete")
48
+ url = "http://flickr.com/services/auth/?api_key=#{@api_key}&frob=#{@frob}&perms=delete&api_sig=#{sig}"
49
+ return url
50
+ else
51
+ return false
52
+ end
53
+ else
54
+ return false
55
+ end
56
+ end
57
+
58
+ def get_token
59
+ if self.check_attr == true
60
+ sig = self.sign("#{@secret}api_key#{@api_key}frob#{@frob}methodflickr.auth.getToken")
61
+ data = open("#{API_URL}method=flickr.auth.getToken&api_key=#{@api_key}&frob=#{@frob}&api_sig=#{sig}")
62
+ doc = REXML::Document.new(data)
63
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
64
+ self.token = doc.elements["//token"].text.strip
65
+ return @token
66
+ else
67
+ return false
68
+ end
69
+ else
70
+ return false
71
+ end
72
+ end
73
+
74
+ def check_token
75
+ if self.check_attr(true) == true
76
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.auth.checkToken")
77
+ data = open("#{API_URL}method=flickr.auth.checkToken&api_key=#{@api_key}&auth_token=#{@token}&api_sig=#{sig}")
78
+ doc = REXML::Document.new(data)
79
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
80
+ perms = doc.elements["//perms"].text.strip
81
+ if perms == "delete"
82
+ return true
83
+ else
84
+ puts "UPFLICKR: Full permissions not enabled."
85
+ return false
86
+ end
87
+ else
88
+ puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
89
+ return false
90
+ end
91
+ else
92
+ return false
93
+ end
94
+ end
95
+
96
+ def list_photos(set_id=false)
97
+ if self.check_attr(true) == true
98
+ if set_id == false
99
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photos.getNotInSet")
100
+ data = open("#{API_URL}method=flickr.photos.getNotInSet&api_key=#{@api_key}&auth_token=#{@token}&api_sig=#{sig}")
101
+ doc = REXML::Document.new(data)
102
+ else
103
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.getPhotosphotoset_id#{set_id}")
104
+ data = open("#{API_URL}method=flickr.photosets.getPhotos&api_key=#{@api_key}&photoset_id=#{set_id}&auth_token=#{@token}&api_sig=#{sig}")
105
+ doc = REXML::Document.new(data)
106
+ end
107
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
108
+ photos = {}
109
+ doc.elements.each("*/*/photo") do |e|
110
+ id = e.attributes['id']
111
+ name = e.attributes['title']
112
+ photos[id] = name
113
+ end
114
+ return photos
115
+ else
116
+ puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
117
+ return false
118
+ end
119
+ else
120
+ return false
121
+ end
122
+ end
123
+
124
+ def add_photo(photo_id, set_id)
125
+ if self.check_attr(true) == true
126
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.addPhotophoto_id#{photo_id}photoset_id#{set_id}")
127
+ data = open("#{API_URL}method=flickr.photosets.addPhoto&api_key=#{@api_key}&photo_id=#{photo_id}&photoset_id=#{set_id}&auth_token=#{@token}&api_sig=#{sig}")
128
+ doc = REXML::Document.new(data)
129
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
130
+ return true
131
+ else
132
+ puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
133
+ return false
134
+ end
135
+ else
136
+ return false
137
+ end
138
+ end
139
+
140
+ def remove_photo(photo_id, set_id)
141
+ if self.check_attr(true) == true
142
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.removePhotophoto_id#{photo_id}photoset_id#{set_id}")
143
+ data = open("#{API_URL}method=flickr.photosets.removePhoto&api_key=#{@api_key}&photo_id=#{photo_id}&photoset_id=#{set_id}&auth_token=#{@token}&api_sig=#{sig}")
144
+ doc = REXML::Document.new(data)
145
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
146
+ return true
147
+ else
148
+ puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
149
+ return false
150
+ end
151
+ else
152
+ return false
153
+ end
154
+ end
155
+
156
+ def delete_photo(photo_id)
157
+ if self.check_attr(true) == true
158
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photos.deletephoto_id#{photo_id}")
159
+ data = open("#{API_URL}method=flickr.photos.delete&api_key=#{@api_key}&photo_id=#{photo_id}&auth_token=#{@token}&api_sig=#{sig}")
160
+ doc = REXML::Document.new(data)
161
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
162
+ return true
163
+ else
164
+ puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
165
+ return false
166
+ end
167
+ else
168
+ return false
169
+ end
170
+ end
171
+
172
+ def license_photo(photo_id,license_id)
173
+ if self.check_attr(true) == true
174
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}license_id#{license_id}methodflickr.photos.licenses.setLicensephoto_id#{photo_id}")
175
+ data = open("#{API_URL}method=flickr.photos.licenses.setLicense&api_key=#{@api_key}&photo_id=#{photo_id}&license_id=#{license_id}&auth_token=#{@token}&api_sig=#{sig}")
176
+ doc = REXML::Document.new(data)
177
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
178
+ return true
179
+ else
180
+ puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
181
+ return false
182
+ end
183
+ end
184
+ end
185
+
186
+ def list_sets
187
+ if self.check_attr(true) == true
188
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.getList")
189
+ data = open("#{API_URL}method=flickr.photosets.getList&api_key=#{@api_key}&auth_token=#{@token}&api_sig=#{sig}")
190
+ doc = REXML::Document.new(data)
191
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
192
+ photosetids = {}
193
+ doc.elements.each("*/photosets/photoset") do |e|
194
+ id = e.attributes['id']
195
+ name = e.elements['title'].text.strip
196
+ photosetids[id] = name
197
+ end
198
+ return photosetids
199
+ else
200
+ puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
201
+ return false
202
+ end
203
+ else
204
+ return false
205
+ end
206
+ end
207
+
208
+ def create_set(photo_id, title, description=false)
209
+ if self.check_attr(true) == true
210
+ if description != false
211
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}description#{description}methodflickr.photosets.createprimary_photo_id#{photo_id}title#{title}")
212
+ title = self.remove_white(title)
213
+ description = self.remove_white(description)
214
+ data = open("#{API_URL}method=flickr.photosets.create&api_key=#{@api_key}&title=#{title}&description=#{description}&primary_photo_id=#{photo_id}&auth_token=#{@token}&api_sig=#{sig}")
215
+ else
216
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.createprimary_photo_id#{photo_id}title#{title}")
217
+ title = self.remove_white(title)
218
+ data = open("#{API_URL}method=flickr.photosets.create&api_key=#{@api_key}&title=#{title}&primary_photo_id=#{photo_id}&auth_token=#{@token}&api_sig=#{sig}")
219
+ end
220
+ doc = REXML::Document.new(data)
221
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
222
+ return doc.elements['//photoset'].attributes['id']
223
+ else
224
+ puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
225
+ return false
226
+ end
227
+ else
228
+ return false
229
+ end
230
+ end
231
+
232
+ def delete_set(set_id)
233
+ if self.check_attr(true) == true
234
+ sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.deletephotoset_id#{set_id}")
235
+ data = open("#{API_URL}method=flickr.photosets.delete&api_key=#{@api_key}&photoset_id=#{set_id}&auth_token=#{@token}&api_sig=#{sig}")
236
+ doc = REXML::Document.new(data)
237
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
238
+ return true
239
+ else
240
+ puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
241
+ return false
242
+ end
243
+ end
244
+ end
245
+
246
+ def post_form(url, query, headers)
247
+ Net::HTTP.start(url.host, url.port) {|con|
248
+ con.read_timeout = 10
249
+ begin
250
+ return con.post(url.path, query, headers)
251
+ rescue => e
252
+ puts "UPFLICKR: POSTING Failed #{e}... #{Time.now}"
253
+ end
254
+ }
255
+ end
256
+
257
+ def upload(params)
258
+ if self.check_attr(true) == true
259
+ params['api_key'] = @api_key
260
+ params['auth_token'] = @token
261
+ p2s = {}
262
+ params.each do |k,v|
263
+ if k == 'photo'
264
+ params['photo'] = File.open(v, 'r')
265
+ else
266
+ p2s[k] = v
267
+ end
268
+ end
269
+ p2s = p2s.sort.to_s
270
+ sig = self.sign("#{@secret}#{p2s}")
271
+ params['api_sig'] = sig
272
+
273
+ mp = MultipartPost.new
274
+
275
+ # Get both the headers and the query ready,
276
+ # given the new MultipartPost and the params
277
+ # Hash
278
+ query, headers = mp.prepare_query(params)
279
+
280
+ # Make sure the URL is useable
281
+ url = URI.parse('http://flickr.com/services/upload/')
282
+
283
+ # Do the actual POST, given the right inputs
284
+ data = self.post_form(url, query, headers)
285
+ doc = REXML::Document.new(data.read_body)
286
+ if doc.elements["//rsp"].attributes['stat'] == "ok"
287
+ return doc.elements["//photoid"].text.strip
288
+ else
289
+ return false
290
+ end
291
+ else
292
+ return false
293
+ end
294
+ end
295
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: upflickr
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 6
10
+ version: 0.1.6
11
+ platform: ruby
12
+ authors:
13
+ - Ben Baker-Smith
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-06-06 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: mime-types
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description:
36
+ email:
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README
43
+ files:
44
+ - lib/multipartpost.rb
45
+ - lib/upflickr.rb
46
+ - README
47
+ - CHANGELOG
48
+ has_rdoc: true
49
+ homepage: http://bitsynthesis.com/upflickr/
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.3.7
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: a simple flickr uploader
82
+ test_files: []
83
+