em-flickraw 0.8.5

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/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']