em-flickraw 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006 Mael Clerambault <maelclerambault@yahoo.fr>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,198 @@
1
+ = EM-Flickraw
2
+
3
+ This port of github.com/hanklords awesome flickraw library uses eventmachine for all its HTTP calls.
4
+ Ruby 1.9, eventmachine, em-http-request and em-synchrony required.
5
+
6
+ The source code can be found in the em branch at http://github.com/jkraemer/flickraw
7
+
8
+ As it is now, this gem depends on beta versions of these em libs:
9
+
10
+ * eventmachine - 1.0.0.beta.3
11
+ * em-http-request - 1.0.0.beta.3
12
+ * em-synchrony - 0.3.0.beta.1
13
+
14
+ I'll update these dependencies when final versions are out.
15
+
16
+ == Installation:
17
+
18
+ gem install em-flickraw
19
+
20
+
21
+ Original contents of README below...
22
+
23
+
24
+ = Flickraw
25
+
26
+ Flickraw is a library to access flickr[http://flickr.com] api in a simple way.
27
+ It maps exactly the methods described in {the official api documentation}[http://www.flickr.com/services/api].
28
+ It also tries to present the data returned in a simple and intuitive way.
29
+ The methods are fetched from flickr when loading the library by using introspection capabilities. So it is always up-to-date with regards to new methods added by flickr.
30
+
31
+ The github repository: http://github.com/hanklords/flickraw
32
+
33
+
34
+ = Installation
35
+ Type this in a console (you might need to be superuser)
36
+
37
+ gem install flickraw
38
+
39
+ This will recreate the documentation by fetching the methods descriptions from flickr and then virtually plugging them in standard rdoc documentation.
40
+ rake rdoc
41
+
42
+ = Features
43
+
44
+ * Small single file: flickraw.rb is less than 300 lines
45
+ * Complete support of flickr API. This doesn't require an update of the library
46
+ * Ruby syntax similar to the flickr api
47
+ * Flickr authentication
48
+ * Photo upload
49
+ * Proxy support
50
+ * Delayed library loading (for rails users)
51
+ * Flickr URLs helpers
52
+
53
+ = Usage
54
+
55
+ == Simple
56
+
57
+ require 'flickraw'
58
+
59
+ FlickRaw.api_key="... Your API key ..."
60
+ FlickRaw.shared_secret="... Your shared secret ..."
61
+
62
+ list = flickr.photos.getRecent
63
+
64
+ id = list[0].id
65
+ secret = list[0].secret
66
+ info = flickr.photos.getInfo :photo_id => id, :secret => secret
67
+
68
+ info.title # => "PICT986"
69
+ info.dates.taken # => "2006-07-06 15:16:18"
70
+
71
+
72
+ sizes = flickr.photos.getSizes :photo_id => id
73
+
74
+ original = sizes.find {|s| s.label == 'Original' }
75
+ original.width # => "800"
76
+
77
+ == Authentication
78
+
79
+ require 'flickraw'
80
+
81
+ FlickRaw.api_key="... Your API key ..."
82
+ FlickRaw.shared_secret="... Your shared secret ..."
83
+
84
+ frob = flickr.auth.getFrob
85
+ auth_url = FlickRaw.auth_url :frob => frob, :perms => 'read'
86
+
87
+ puts "Open this url in your process to complete the authication process : #{auth_url}"
88
+ puts "Press Enter when you are finished."
89
+ STDIN.getc
90
+
91
+ begin
92
+ auth = flickr.auth.getToken :frob => frob
93
+ login = flickr.test.login
94
+ puts "You are now authenticated as #{login.username} with token #{auth.token}"
95
+ rescue FlickRaw::FailedResponse => e
96
+ puts "Authentication failed : #{e.msg}"
97
+ end
98
+
99
+ You don't need to do that each time, you can reuse your token:
100
+
101
+ require 'flickraw'
102
+
103
+ FlickRaw.api_key="... Your API key ..."
104
+ FlickRaw.shared_secret="... Your shared secret ..."
105
+
106
+ auth = flickr.auth.checkToken :auth_token => "... Your saved token ..."
107
+
108
+ # From here you are logged:
109
+ puts auth.user.username
110
+
111
+ == Upload
112
+
113
+ require 'flickraw'
114
+
115
+ FlickRaw.api_key="... Your API key ..."
116
+ FlickRaw.shared_secret="... Your shared secret ..."
117
+
118
+ PHOTO_PATH='photo.jpg'
119
+
120
+ # You need to be authentified to do that, see the previous examples.
121
+ flickr.upload_photo PHOTO_PATH, :title => "Title", :description => "This is the description"
122
+
123
+ == Flickraw Options
124
+
125
+ You can pass some options to modify flickraw behavior:
126
+
127
+ FlickRawOptions = {
128
+ "api_key" => "... Your API key ...",
129
+ "shared_secret" => "... Your shared secret ...",
130
+ "auth_token" => "... Your saved token..." # if you set this you will be automatically loggued
131
+ "lazyload" => true, # This delay the loading of the library until the first call
132
+
133
+ # Proxy support. alternatively, you can use the http_proxy environment variable
134
+ "proxy_host" => "proxy_host",
135
+ "proxy_port" => "proxy_port",
136
+ "proxy_user" => "proxy_user",
137
+ "proxy_password" => "proxy_password",
138
+
139
+ "timeout" => 5 # Set the request timeout
140
+ }
141
+
142
+ require 'flickraw'
143
+
144
+ == Flickr URL Helpers
145
+
146
+ There are some helpers to build flickr urls :
147
+
148
+ === url, url_m, url_s, url_t, url_b, url_z, url_o
149
+
150
+ info = flickr.photos.getInfo(:photo_id => "3839885270")
151
+ FlickRaw.url_b(info) # => "http://farm3.static.flickr.com/2485/3839885270_6fb8b54e06_b.jpg"
152
+
153
+ === url_profile
154
+
155
+ info = flickr.photos.getInfo(:photo_id => "3839885270")
156
+ FlickRaw.url_profile(info) # => "http://www.flickr.com/people/41650587@N02/"
157
+
158
+ === url_photopage
159
+
160
+ info = flickr.photos.getInfo(:photo_id => "3839885270")
161
+ FlickRaw.url_photopage(info) # => "http://www.flickr.com/photos/41650587@N02/3839885270"
162
+
163
+ === url_photoset, url_photosets
164
+
165
+ info = flickr.photos.getInfo(:photo_id => "3839885270")
166
+ FlickRaw.url_photosets(info) # => "http://www.flickr.com/photos/41650587@N02/sets/"
167
+
168
+ === url_short, url_short_m, url_short_s, url_short_t
169
+
170
+ info = flickr.photos.getInfo(:photo_id => "3839885270")
171
+ FlickRaw.url_short(info) # => "http://flic.kr/p/6Rjq7s"
172
+
173
+ === url_photostream
174
+
175
+ info = flickr.photos.getInfo(:photo_id => "3839885270")
176
+ FlickRaw.url_photostream(info) # => "http://www.flickr.com/photos/41650587@N02/"
177
+
178
+
179
+ See the _examples_ directory to find more examples.
180
+
181
+ == Cached version
182
+
183
+ You can use
184
+
185
+ require 'flickraw-cached'
186
+
187
+ instead of
188
+
189
+ require 'flickraw'
190
+
191
+ This way it it doesn't fetch available flickr methods each time it is loaded.
192
+ The flickraw-cached gem is on rubygems.org and should be up-to-date with regard to flickr api most of the time.
193
+
194
+ = Notes
195
+ If you want to use the api authenticated with several user at the same time, you must pass the authentication token explicitely each time.
196
+ This is because it is keeping the authentication token internally.
197
+ As an alternative, you can create new Flickr objects besides Kernel.flickr which is created for you. Use Flickr.new to this effect.
198
+
@@ -0,0 +1,25 @@
1
+ require 'flickraw'
2
+
3
+ # This is how to authenticate on flickr website.
4
+ # You need an API key for that, see http://www.flickr.com/services/api/keys/
5
+ API_KEY=''
6
+ SHARED_SECRET=''
7
+
8
+ FlickRaw.api_key=API_KEY
9
+ FlickRaw.shared_secret=SHARED_SECRET
10
+
11
+ frob = flickr.auth.getFrob
12
+ auth_url = FlickRaw.auth_url :frob => frob, :perms => 'read'
13
+
14
+ puts "Open this url in your process to complete the authication process : #{auth_url}"
15
+ puts "Press Enter when you are finished."
16
+ STDIN.getc
17
+
18
+ begin
19
+ flickr.auth.getToken :frob => frob
20
+ login = flickr.test.login
21
+ puts "You are now authenticated as #{login.username}"
22
+ rescue FlickRaw::FailedResponse => e
23
+ puts "Authentication failed : #{e.msg}"
24
+ end
25
+
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/ruby
2
+ # Chooses a photo from the current interesting
3
+ # photo and set it as the background image on
4
+ # your first KDE desktop.
5
+
6
+ require 'flickraw'
7
+ require 'open-uri'
8
+ DESKTOP=1
9
+
10
+ list = flickr.interestingness.getList
11
+ photo = list[rand(100)]
12
+ sizes = flickr.photos.getSizes(:photo_id => photo.id)
13
+ original = sizes.find {|s| s.label == 'Original' }
14
+
15
+ url = original.source
16
+ file = File.basename url
17
+ full_path = File.join(Dir.pwd, file)
18
+
19
+ open url do |remote|
20
+ open(file, 'wb') { |local| local << remote.read }
21
+ end
22
+
23
+ `dcop kdesktop KBackgroundIface setWallpaper #{DESKTOP} #{full_path} 7`
@@ -0,0 +1,6 @@
1
+ require 'flickraw'
2
+
3
+ # Get the list of the 20 most recent 'interesting photos'
4
+
5
+ list = flickr.interestingness.getList :per_page => 20
6
+ list.each {|photo| puts "'#{photo.title}' id=#{photo.id} secret=#{photo.secret}" }
@@ -0,0 +1,22 @@
1
+ require 'flickraw'
2
+
3
+ # This is how to upload photos on flickr.
4
+ # You need to be authentified to do that.
5
+ API_KEY=''
6
+ SHARED_SECRET=''
7
+ PHOTO_PATH='photo.jpg'
8
+
9
+ FlickRaw.api_key=API_KEY
10
+ FlickRaw.shared_secret=SHARED_SECRET
11
+
12
+ frob = flickr.auth.getFrob
13
+ auth_url = FlickRaw.auth_url :frob => frob, :perms => 'write'
14
+
15
+ puts "Open this url in your process to complete the authication process : #{auth_url}"
16
+ puts "Press Enter when you are finished."
17
+ STDIN.getc
18
+
19
+ flickr.auth.getToken :frob => frob
20
+ login = flickr.test.login
21
+
22
+ flickr.upload_photo PHOTO_PATH, :title => 'Title', :description => 'This is the description'
@@ -0,0 +1,133 @@
1
+ require "rdoc"
2
+ require "rdoc/parser/ruby"
3
+ require "cgi"
4
+
5
+ FLICKR_API_URL='http://www.flickr.com/services/api'
6
+ FlickRaw.api_key = 'apikey'
7
+ FlickRaw.shared_secret = 'shared secret'
8
+
9
+ FakedToken = Struct.new :text
10
+
11
+ module RDoc
12
+ class FlickrawParser < Parser::Ruby
13
+ parse_files_matching(/flickraw\.rb$/)
14
+
15
+ def scan
16
+ super
17
+
18
+ fr = @top_level.find_module_named 'FlickRaw'
19
+ k = fr.add_class NormalClass, 'Flickr', 'FlickRaw::Request'
20
+ k.record_location @top_level
21
+ @stats.add_class 'Flickr'
22
+
23
+ add_flickr_methods(FlickRaw::Flickr, k)
24
+ @top_level
25
+ end
26
+
27
+ private
28
+ def add_flickr_methods(obj, doc)
29
+ flickr # Force loading of methods if lazyloaded
30
+ obj.constants.each { |const_name|
31
+ const = obj.const_get const_name
32
+ if const.is_a?(Class) && const < FlickRaw::Request
33
+ name = const.name.sub(/.*::/, '')
34
+ k = doc.add_class NormalClass, name, 'FlickRaw::Request'
35
+ k.record_location @top_level
36
+ @stats.add_class name
37
+
38
+ m = AnyMethod.new nil, name.downcase
39
+ m.comment = "Returns a #{name} object."
40
+ m.params = ''
41
+ m.singleton = false
42
+ doc.add_method m
43
+ @stats.add_method m
44
+
45
+ add_flickr_methods(const, k)
46
+ end
47
+ }
48
+
49
+ obj.flickr_methods.each {|name|
50
+ flickr_method = obj.request_name + '.' + name
51
+ info = flickr.reflection.getMethodInfo :method_name => flickr_method
52
+
53
+ m = AnyMethod.new nil, name
54
+ m.comment = flickr_method_comment(info)
55
+ m.params = flickr_method_args(info)
56
+ m.singleton = false
57
+
58
+ m.start_collecting_tokens
59
+ m.add_token FakedToken.new( %{
60
+ # Generated automatically from flickr api
61
+ def #{name}(*args)
62
+ @flickr.call '#{flickr_method}', *args
63
+ end
64
+ } )
65
+ doc.add_method m
66
+ @stats.add_method m
67
+ }
68
+ end
69
+
70
+ def flickr_method_comment(info)
71
+ description = CGI.unescapeHTML(info.method.description.to_s)
72
+ # description.gsub!( /<\/?(\w+)>/ ) {|b|
73
+ # return b if ['em', 'b', 'tt'].include? $1
74
+ # return ''
75
+ # }
76
+
77
+ if info.respond_to? :arguments
78
+ args = info.arguments.select { |arg| arg.name != 'api_key' }
79
+
80
+ arguments = "<b>Arguments</b>\n"
81
+ if args.size > 0
82
+ args.each {|arg|
83
+ arguments << "[#{arg.name} "
84
+ arguments << "<em>(required)</em> " if arg.optional == '0'
85
+ arguments << "] "
86
+ arguments << "#{CGI.unescapeHTML(arg.to_s)}\n"
87
+ }
88
+ end
89
+ end
90
+
91
+ if info.respond_to? :errors
92
+ errors = "<b>Error codes</b>\n"
93
+ info.errors.each {|e|
94
+ errors << "* #{e.code}: <em>#{e.message}</em>\n\n"
95
+ errors << " #{CGI.unescapeHTML e.to_s}\n"
96
+ }
97
+ end
98
+
99
+ if info.method.respond_to? :response
100
+ response = "<b>Returns</b>\n"
101
+ raw = CGI.unescapeHTML(info.method.response.to_s)
102
+ response << raw.lines.collect { |line| line.insert(0, ' ') }.join
103
+ else
104
+ response = ''
105
+ end
106
+
107
+ str = "{#{info.method.name}}[#{FLICKR_API_URL}/#{info.method.name}.html] request.\n\n"
108
+ str << description << "\n\n"
109
+ str << arguments << "\n\n"
110
+ str << errors << "\n\n"
111
+ str << response << "\n\n"
112
+ end
113
+
114
+ def flickr_method_args(info)
115
+ str = ''
116
+ if info.respond_to? :arguments
117
+ args = info.arguments.select { |arg| arg.name != 'api_key' }
118
+
119
+ if args.size > 0
120
+ str << '('
121
+ args.each {|arg|
122
+ str << ":#{arg.name} => '#{arg.name}'"
123
+ str << ','
124
+ }
125
+ str.chomp! ','
126
+ str << ')'
127
+ end
128
+ end
129
+ str
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,339 @@
1
+ # encoding: ascii-8bit
2
+ # Copyright (c) 2006 Mael Clerambault <maelclerambault@yahoo.fr>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+
24
+ require 'net/http'
25
+ require 'digest/md5'
26
+ require 'json'
27
+ require 'em-synchrony/em-http'
28
+
29
+ FlickRawOptions = {} if not Object.const_defined? :FlickRawOptions # :nodoc:
30
+ if ENV['http_proxy'] and not FlickRawOptions['proxy_host']
31
+ proxy = URI.parse ENV['http_proxy']
32
+ FlickRawOptions.update('proxy_host' => proxy.host, 'proxy_port' => proxy.port, 'proxy_user' => proxy.user, 'proxy_password' => proxy.password)
33
+ end
34
+
35
+ module FlickRaw
36
+ VERSION='0.8.5'
37
+
38
+ FLICKR_HOST='api.flickr.com'.freeze
39
+ REST_PATH='/services/rest/?'.freeze
40
+ UPLOAD_PATH='/services/upload/'.freeze
41
+ REPLACE_PATH='/services/replace/'.freeze
42
+
43
+ AUTH_PATH='http://flickr.com/services/auth/?'.freeze
44
+ PHOTO_SOURCE_URL='http://farm%s.static.flickr.com/%s/%s_%s%s.%s'.freeze
45
+ URL_PROFILE='http://www.flickr.com/people/'.freeze
46
+ URL_PHOTOSTREAM='http://www.flickr.com/photos/'.freeze
47
+ URL_SHORT='http://flic.kr/p/'.freeze
48
+
49
+ class Response
50
+ def self.build(h, type) # :nodoc:
51
+ if h.is_a? Response
52
+ h
53
+ elsif type =~ /s$/ and (a = h[$`]).is_a? Array
54
+ ResponseList.new(h, type, a.collect {|e| Response.build(e, $`)})
55
+ elsif h.keys == ["_content"]
56
+ h["_content"]
57
+ else
58
+ Response.new(h, type)
59
+ end
60
+ end
61
+
62
+ attr_reader :flickr_type
63
+ def initialize(h, type) # :nodoc:
64
+ @flickr_type, @h = type, {}
65
+ methods = "class << self;"
66
+ h.each {|k,v|
67
+ @h[k] = case v
68
+ when Hash then Response.build(v, k)
69
+ when Array then v.collect {|e| Response.build(e, k)}
70
+ else v
71
+ end
72
+ methods << "def #{k}; @h['#{k}'] end;"
73
+ }
74
+ eval methods << "end"
75
+ end
76
+ def [](k); @h[k] end
77
+ def to_s; @h["_content"] || super end
78
+ def inspect; @h.inspect end
79
+ def to_hash; @h end
80
+ def marshal_dump; [@h, @flickr_type] end
81
+ def marshal_load(data); initialize(*data) end
82
+ end
83
+
84
+ class ResponseList < Response
85
+ include Enumerable
86
+ def initialize(h, t, a); super(h, t); @a = a end
87
+ def [](k); k.is_a?(Fixnum) ? @a[k] : super(k) end
88
+ def each; @a.each{|e| yield e} end
89
+ def to_a; @a end
90
+ def inspect; @a.inspect end
91
+ def size; @a.size end
92
+ def marshal_dump; [@h, @flickr_type, @a] end
93
+ end
94
+
95
+ class FailedResponse < StandardError
96
+ attr_reader :code
97
+ alias :msg :message
98
+ def initialize(msg, code, req)
99
+ @code = code
100
+ super("'#{req}' - #{msg}")
101
+ end
102
+ end
103
+
104
+ class Request
105
+ def initialize(flickr = nil) # :nodoc:
106
+ @flickr = flickr
107
+
108
+ self.class.flickr_objects.each {|name|
109
+ klass = self.class.const_get name.capitalize
110
+ instance_variable_set "@#{name}", klass.new(@flickr)
111
+ }
112
+ end
113
+
114
+ def self.build_request(req) # :nodoc:
115
+ method_nesting = req.split '.'
116
+ raise "'#{@name}' : Method name mismatch" if method_nesting.shift != request_name.split('.').last
117
+
118
+ if method_nesting.size > 1
119
+ name = method_nesting.first
120
+ class_name = name.capitalize
121
+ if flickr_objects.include? name
122
+ klass = const_get(class_name)
123
+ else
124
+ klass = Class.new Request
125
+ const_set(class_name, klass)
126
+ attr_reader name
127
+ flickr_objects << name
128
+ end
129
+
130
+ klass.build_request method_nesting.join('.')
131
+ else
132
+ req = method_nesting.first
133
+ module_eval %{
134
+ def #{req}(*args, &block)
135
+ @flickr.call("#{request_name}.#{req}", *args, &block)
136
+ end
137
+ }
138
+ flickr_methods << req
139
+ end
140
+ end
141
+
142
+ # List of the flickr subobjects of this object
143
+ def self.flickr_objects; @flickr_objects ||= [] end
144
+
145
+ # List of the flickr methods of this object
146
+ def self.flickr_methods; @flickr_methods ||= [] end
147
+
148
+ # Returns the prefix of the request corresponding to this class.
149
+ def self.request_name; name.downcase.gsub(/::/, '.').sub(/[^\.]+\./, '') end
150
+ end
151
+
152
+ # Root class of the flickr api hierarchy.
153
+ class Flickr < Request
154
+ def self.build(methods); methods.each { |m| build_request m } end
155
+
156
+ def initialize(token = FlickRawOptions['auth_token']) # :nodoc:
157
+ Flickr.build(call('flickr.reflection.getMethods')) if Flickr.flickr_objects.empty?
158
+ super self
159
+ @token = token
160
+ end
161
+
162
+ # This is the central method. It does the actual request to the flickr server.
163
+ #
164
+ # Raises FailedResponse if the response status is _failed_.
165
+ def call(req, args={}, &block)
166
+ @token = nil if req == "flickr.auth.getFrob"
167
+ request = EM::HttpRequest.new("http://#{FLICKR_HOST}#{REST_PATH}", :proxy => proxy_opts).post :head => {'User-Agent' => "Flickraw/#{VERSION}"},
168
+ :body => build_args(args, req)
169
+ process_response(req, request.response)
170
+ end
171
+
172
+ # Use this to upload the photo in _file_.
173
+ #
174
+ # flickr.upload_photo '/path/to/the/photo', :title => 'Title', :description => 'This is the description'
175
+ #
176
+ # See http://www.flickr.com/services/api/upload.api.html for more information on the arguments.
177
+ def upload_photo(file, args={}); upload_flickr(UPLOAD_PATH, file, args) end
178
+
179
+ # Use this to replace the photo with :photo_id with the photo in _file_.
180
+ #
181
+ # flickr.replace_photo '/path/to/the/photo', :photo_id => id
182
+ #
183
+ # See http://www.flickr.com/services/api/replace.api.html for more information on the arguments.
184
+ def replace_photo(file, args={}); upload_flickr(REPLACE_PATH, file, args) end
185
+
186
+ private
187
+ def build_args(args={}, req = nil)
188
+ full_args = {:api_key => FlickRaw.api_key, :format => 'json', :nojsoncallback => "1"}
189
+ full_args[:method] = req if req
190
+ full_args[:auth_token] = @token if @token
191
+ args.each {|k, v|
192
+ v = v.to_s.encode("utf-8").force_encoding("ascii-8bit") if RUBY_VERSION >= "1.9"
193
+ full_args[k.to_sym] = v.to_s
194
+ }
195
+ full_args[:api_sig] = FlickRaw.api_sig(full_args) if FlickRaw.shared_secret
196
+ full_args
197
+ end
198
+
199
+ def process_response(req, response)
200
+ json = JSON.load(response.empty? ? "{}" : response)
201
+ raise FailedResponse.new(json['message'], json['code'], req) if json.delete('stat') == 'fail'
202
+ type, json = json.to_a.first if json.size == 1 and json.all? {|k,v| v.is_a? Hash}
203
+
204
+ res = Response.build json, type
205
+ @token = res.token if res.respond_to? :flickr_type and res.flickr_type == "auth"
206
+ res
207
+ end
208
+
209
+ def proxy_opts
210
+ if @proxy_opts.nil?
211
+ if FlickRawOptions['proxy_host']
212
+ @proxy_opts = { :host => FlickRawOptions['proxy_host'] }
213
+ @proxy_opts[:port] = FlickRawOptions['proxy_port'] if FlickRawOptions['proxy_port']
214
+ if FlickRawOptions['proxy_user'] && FlickRawOptions['proxy_password']
215
+ @proxy_opts[:authorization] = [ FlickRawOptions['proxy_user'], FlickRawOptions['proxy_password'] ]
216
+ end
217
+ else
218
+ @proxy_opts = false
219
+ end
220
+ end
221
+ @proxy_opts ? @proxy_opts : nil
222
+ end
223
+
224
+ def upload_flickr(method, file, args={})
225
+ photo = open(file, 'rb') { |f| f.read }
226
+ boundary = Digest::MD5.hexdigest(photo)
227
+
228
+ header = {'Content-type' => "multipart/form-data, boundary=#{boundary} ", 'User-Agent' => "Flickraw/#{VERSION}"}
229
+ query = ''
230
+
231
+ file = file.to_s.encode("utf-8").force_encoding("ascii-8bit") if RUBY_VERSION >= "1.9"
232
+ build_args(args).each { |a, v|
233
+ query <<
234
+ "--#{boundary}\r\n" <<
235
+ "Content-Disposition: form-data; name=\"#{a}\"\r\n\r\n" <<
236
+ "#{v}\r\n"
237
+ }
238
+ query <<
239
+ "--#{boundary}\r\n" <<
240
+ "Content-Disposition: form-data; name=\"photo\"; filename=\"#{file}\"\r\n" <<
241
+ "Content-Transfer-Encoding: binary\r\n" <<
242
+ "Content-Type: image/jpeg\r\n\r\n" <<
243
+ photo <<
244
+ "\r\n" <<
245
+ "--#{boundary}--"
246
+
247
+ request = EM::HttpRequest.new("http://#{FLICKR_HOST}#{method}", :proxy => proxy_opts).post :head => header,
248
+ :body => query
249
+ xml = request.response
250
+ if xml[/stat="(\w+)"/, 1] == 'fail'
251
+ msg = xml[/msg="([^"]+)"/, 1]
252
+ code = xml[/code="([^"]+)"/, 1]
253
+ raise FailedResponse.new(msg, code, 'flickr.upload')
254
+ end
255
+ type = xml[/<(\w+)/, 1]
256
+ h = {
257
+ "secret" => xml[/secret="([^"]+)"/, 1],
258
+ "originalsecret" => xml[/originalsecret="([^"]+)"/, 1],
259
+ "_content" => xml[/>([^<]+)<\//, 1]
260
+ }.delete_if {|k,v| v.nil? }
261
+ Response.build(h, type)
262
+ end
263
+ end
264
+
265
+ class << self
266
+ # Your flickr API key, see http://www.flickr.com/services/api/keys for more information
267
+ def api_key; FlickRawOptions['api_key'] end
268
+ def api_key=(key); FlickRawOptions['api_key'] = key end
269
+
270
+ # The shared secret of _api_key_, see http://www.flickr.com/services/api/keys for more information
271
+ def shared_secret; FlickRawOptions['shared_secret'] end
272
+ def shared_secret=(key); FlickRawOptions['shared_secret'] = key end
273
+
274
+ # Returns the flickr auth URL.
275
+ def auth_url(args={})
276
+ full_args = {:api_key => api_key, :perms => 'read'}
277
+ args.each {|k, v| full_args[k.to_sym] = v }
278
+ full_args[:api_sig] = api_sig(full_args) if shared_secret
279
+
280
+ AUTH_PATH + full_args.collect { |a, v| "#{a}=#{v}" }.join('&')
281
+ end
282
+
283
+ # Returns the signature of hsh. This is meant to be passed in the _api_sig_ parameter.
284
+ def api_sig(hsh)
285
+ Digest::MD5.hexdigest(FlickRaw.shared_secret + hsh.sort{|a, b| a[0].to_s <=> b[0].to_s }.flatten.join)
286
+ end
287
+
288
+ BASE58_ALPHABET="123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ".freeze
289
+ def base58(id)
290
+ id = id.to_i
291
+ alphabet = BASE58_ALPHABET.split(//)
292
+ base = alphabet.length
293
+ begin
294
+ id, m = id.divmod(base)
295
+ r = alphabet[m] + (r || '')
296
+ end while id > 0
297
+ r
298
+ end
299
+
300
+ def url(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, "", "jpg"] end
301
+ def url_m(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, "_m", "jpg"] end
302
+ def url_s(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, "_s", "jpg"] end
303
+ def url_t(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, "_t", "jpg"] end
304
+ def url_b(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, "_b", "jpg"] end
305
+ def url_z(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, "_z", "jpg"] end
306
+ def url_o(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.originalsecret, "_o", r.originalformat] end
307
+ def url_profile(r); URL_PROFILE + (r.owner.respond_to?(:nsid) ? r.owner.nsid : r.owner) + "/" end
308
+ def url_photopage(r); url_photostream(r) + r.id end
309
+ def url_photosets(r); url_photostream(r) + "sets/" end
310
+ def url_photoset(r); url_photosets(r) + r.id end
311
+ def url_short(r); URL_SHORT + base58(r.id) end
312
+ def url_short_m(r); URL_SHORT + "img/" + base58(r.id) + "_m.jpg" end
313
+ def url_short_s(r); URL_SHORT + "img/" + base58(r.id) + ".jpg" end
314
+ def url_short_t(r); URL_SHORT + "img/" + base58(r.id) + "_t.jpg" end
315
+ def url_photostream(r)
316
+ URL_PHOTOSTREAM +
317
+ if r.respond_to?(:pathalias) and r.pathalias
318
+ r.pathalias
319
+ elsif r.owner.respond_to?(:nsid)
320
+ r.owner.nsid
321
+ else
322
+ r.owner
323
+ end + "/"
324
+ end
325
+ end
326
+ end
327
+
328
+ # Use this to access the flickr API easily. You can type directly the flickr requests as they are described on the flickr website.
329
+ # require 'flickraw'
330
+ #
331
+ # recent_photos = flickr.photos.getRecent
332
+ # puts recent_photos[0].title
333
+ def flickr; $flickraw ||= FlickRaw::Flickr.new end
334
+
335
+ # Load the methods if the option lazyload is not specified
336
+ begin
337
+ flickr
338
+ rescue
339
+ end if not FlickRawOptions['lazyload']