upflickr 0.1.6

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 (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
+