jamescook-ruby-imagebam 0.2.0

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/README ADDED
@@ -0,0 +1,37 @@
1
+ -- ruby-imagebam
2
+
3
+ ruby-imagebam provides a module to easily upload your images to ImageBam via their API.
4
+
5
+ You need both a user and developer key from http://www.imagebam.com/nav/API after you have created an account.
6
+
7
+ -- Usage
8
+
9
+ # Uploading a single file
10
+ ib = ImageBam::Upload::Image.new(:file_path => '/path/to/my/picture.jpg',
11
+ :content_type => 0, # 0 = safe for work, 1 = not safe for work
12
+ :api_key_dev => 'your-dev-key',
13
+ :api_key_user => 'your-user-key',
14
+ :api_user_secret => 'your-user-secret',
15
+ :api_dev_secret => 'your-dev-secret')
16
+
17
+ response = ib.upload!
18
+ puts response[:url]
19
+
20
+
21
+
22
+ # Uploading multiple files
23
+ ib = ImageBam::Upload::Image.new(:file_path => ['/path/to/my/picture.jpg', '/path/to/my/picture2.jpg'],
24
+ :content_type => 0, # 0 = safe for work, 1 = not safe for work
25
+ :api_key_dev => 'your-dev-key',
26
+ :api_key_user => 'your-user-key',
27
+ :api_user_secret => 'your-user-secret',
28
+ :api_dev_secret => 'your-dev-secret')
29
+
30
+ responses = ib.upload!
31
+ responses.each do |response|
32
+ puts response[:url]
33
+ end
34
+
35
+ -- Notes
36
+ The response is a hash. See lib/response.rb for the keys you have access to.
37
+ You can upload multiple files at once (using threads) if you set allow_concurrent_uploads to true
data/demo.rb ADDED
@@ -0,0 +1,12 @@
1
+ require File.join(File.dirname(__FILE__),"image_bam.rb")
2
+
3
+ ib = ImageBam::Image.new(:file_path => '/Users/jimmy/pics/awesome_screenshot.jpg',
4
+ :content_type => 0,
5
+ :api_key_dev => 'your-dev-key',
6
+ :api_key_user => 'your-user-key',
7
+ :api_user_secret => 'your-user-secret',
8
+ :api_dev_secret => 'your-dev-secret')
9
+
10
+ response = ib.upload!
11
+ puts response[:status]
12
+
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/lib/image_bam.rb'
@@ -0,0 +1,19 @@
1
+ module ImageBam
2
+ module Errors
3
+ # 1: No image uploaded / zero filesize / general upload error (UPLOAD)
4
+ # 2: Developer API-Key has not been found (GENERAL)
5
+ # 3: User API-Key has not been found (GENERAL)
6
+ # 4: Content type has not been set (UPLOAD)
7
+ # 5: Invalid gallery ID (UPLOAD)
8
+ # 6: Secret is invalid (UPLOAD)
9
+ # 7: Image has been banned from Imagebam (UPLOAD)
10
+ # 8: File-extention is not allowed (UPLOAD)
11
+ # 9: File exceeded maximum allowed size (UPLOAD)
12
+ # 666: Developer API Key has been disabled by ImageBAM (GENERAL)
13
+ class InvalidPath < RuntimeError; end
14
+ class InvalidContentType < RuntimeError; end
15
+ class InvalidSalt < RuntimeError; end
16
+ class InvalidFilePattern < RuntimeError; end
17
+ class ResponseError < RuntimeError; end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module ImageBam
2
+ class Gallery
3
+ def initialize(args)
4
+ @api_key_dev = args[:api_key_dev]
5
+ @api_key_user = args[:api_key_user]
6
+ @api_dev_secret = args[:api_dev_secret]
7
+ @api_user_secret = args[:api_user_secret]
8
+ @salt = args[:salt] || Digest::MD5.hexdigest(Time.now.to_i.to_s + 'splahdow!')
9
+ end
10
+
11
+ def generate_secret
12
+ @secret ||= Digest::MD5.hexdigest(@api_dev_secret + @api_user_secret + @salt)
13
+ end
14
+
15
+ def generate!
16
+ query = "API_key_dev=#{@api_key_dev}&API_key_user=#{@api_key_user}&salt=#{@salt}&secret=#{generate_secret}"
17
+ response = Net::HTTP.start(ImageBam::GALLERY_URL.host).post2(ImageBam::GALLERY_URL.path, query, "Content-type" => "application/x-www-form-urlencoded;")
18
+ return ImageBam::Response.new( response.body, 'gallery' ).response
19
+ end
20
+
21
+ end
22
+ end
23
+
@@ -0,0 +1,155 @@
1
+ module ImageBam
2
+ class Image
3
+ include MIME
4
+ attr_accessor :file_path, :content_type, :api_key_dev, :api_key_user, :api_user_secret, :api_dev_secret, :salt, :allow_concurrent_uploads, :file_pattern
5
+
6
+ def initialize(args)
7
+ @api_key_dev = args[:api_key_dev]
8
+ @api_key_user = args[:api_key_user]
9
+ @api_dev_secret = args[:api_dev_secret]
10
+ @api_user_secret = args[:api_user_secret]
11
+ @salt = args[:salt] || Digest::MD5.hexdigest(Time.now.to_i.to_s + 'splahdow!')
12
+ @content_type = args[:content_type] || 0
13
+ @allow_concurrent_uploads = false
14
+ @file_pattern = args[:file_pattern] || /.*/
15
+ @gallery = args[:gallery] || nil
16
+
17
+ # Handle a directory of images
18
+ if(!args[:file_path].is_a?(Array) && File.directory?(args[:file_path]))
19
+ @file_path = filter_images_in_dir(args[:file_path], @file_pattern)
20
+ else
21
+ @file_path = args[:file_path]
22
+ end
23
+
24
+ validate_args
25
+ end
26
+
27
+ def upload!
28
+ boundary = '42424242424242424242424242424242424242424242424242424242'
29
+ if(@file_path.is_a? Array)
30
+ return upload_many(@file_path)
31
+ else
32
+ query = content_headers.map {|p| '--' + boundary + "\r\n" + p}.join('') + "--" + boundary + "--\r\n"
33
+ puts query
34
+ response = Net::HTTP.start(ImageBam::POST_URL.host).post2(ImageBam::POST_URL.path, query, "Content-type" => "multipart/form-data; boundary=" + boundary)
35
+ return ImageBam::Response.new( response.body, 'image' ).response
36
+ end
37
+ end
38
+
39
+
40
+ def gallery_url
41
+ if(@gallery)
42
+ "http://www.imagebam.com/gallery/#{@gallery}"
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def upload_many(files)
49
+ if(@allow_concurrent_uploads == true)
50
+ semaphore = Mutex.new
51
+ semaphore.synchronize {
52
+ responses = []
53
+ @threads = []
54
+ files.each do |file|
55
+ @file_path = file
56
+
57
+ # Only launch 5 threads at at a time, max
58
+ if(@threads.length < 5)
59
+ @threads.push( Thread.new { responses.push(upload!) } )
60
+ files.delete(file)
61
+ end
62
+ sleep(1)
63
+ purge_sleeping_threads
64
+
65
+ # Restart the process if there are more files, or all threads busy
66
+ if(files.any?)
67
+ upload_many(files)
68
+ end
69
+ end
70
+ return responses
71
+ }
72
+ else
73
+ responses = []
74
+ files.each do |file|
75
+ @file_path = file
76
+ responses.push upload!
77
+ end
78
+ return responses
79
+ end
80
+ end
81
+
82
+ # An array of all required parameters that will be POSTed
83
+ # Mostly ripped from http://www.realityforge.org/articles/2006/03/02/upload-a-file-via-post-with-net-http
84
+ def content_headers
85
+ params = [param_for('API_key_dev', @api_key_dev),
86
+ param_for('API_key_user', @api_key_user),
87
+ param_for('secret', generate_secret),
88
+ param_for('salt', @salt),
89
+ param_for('gallery', @gallery),
90
+ param_for('content_type', @content_type),
91
+ "Content-Disposition: form-data; name=photo; filename=\"#{File.basename(@file_path)}\"\r\n" +
92
+ "Content-Transfer-Encoding: binary\r\n" +
93
+ "Content-Type: #{MIME::Types.type_for(@file_path)}\r\n" +
94
+ "\r\n" +
95
+ "#{File.read(@file_path)}\r\n"]
96
+ end
97
+
98
+ # Wrap the text-only data in the appropriate header
99
+ def param_for(key,value)
100
+ "Content-Disposition: form-data; name=\"#{::CGI::escape(key)}\"\r\n" +
101
+ "\r\n" +
102
+ "#{value}\r\n"
103
+ end
104
+
105
+ # From API docs..
106
+ # 32 character secret by building the md5-checksum of the string consisting of your API-Secret,
107
+ # the user's API-Secret and the 32-character salt.
108
+ def generate_secret
109
+ @secret ||= Digest::MD5.hexdigest(@api_dev_secret + @api_user_secret + @salt)
110
+ end
111
+
112
+ def validate_args
113
+
114
+ raise ImageBam::Errors::InvalidFilePattern unless @file_pattern.is_a?(Regexp)
115
+
116
+ if(@file_path.is_a? Array)
117
+ errors = []
118
+ @file_path.each do |file|
119
+ errors.push("#{file} is invalid.") unless File.exists?(file)
120
+ end
121
+ raise ImageBam::Errors::InvalidPath, errors.join("\n") unless errors.empty?
122
+ else
123
+ unless(File.exists?(@file_path) || File.directory?(@file_path))
124
+ raise ImageBam::Errors::InvalidPath, "An invalid file path was specified"
125
+ end
126
+ end
127
+
128
+ unless(@content_type == 0 || @content_type == 1)
129
+ raise ImageBam::Errors::InvalidContentType, "Either 0 (SFW) or 1 (NSFW) must be specified as a content type. You said #{@content_type}"
130
+ end
131
+
132
+ unless(@salt =~ /^[a-zA-Z0-9]+$/)
133
+ raise ImageBam::Errors::InvalidSalt, "Salt is invalid. You must only use alpha-numeric characters (0-9 & A-Z). You provided #{@salt}"
134
+ end
135
+ end
136
+
137
+ # Only find images, and then filter them out by regex
138
+ def filter_images_in_dir(path, regexp)
139
+ old_dir = Dir.pwd
140
+ Dir.chdir(path)
141
+ images = Dir.glob("*.{jpg,jpeg,png,gif,bmp}").map{|f| f="#{File.join(path, f)}"}
142
+ filtered_images = images.reject{|img| !(File.basename(img) =~ regexp)}
143
+ puts filtered_images.join(", ")
144
+ Dir.chdir(old_dir)
145
+ return filtered_images
146
+ end
147
+
148
+ def purge_sleeping_threads
149
+ @threads.each do |thread|
150
+ # TODO should I call thread.join here? does it matter?
151
+ @threads.delete(thread) unless thread.alive?
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,15 @@
1
+ require 'digest/md5'
2
+ require "net/http"
3
+ require 'mime/types'
4
+ require 'cgi'
5
+ require 'thread'
6
+
7
+ require File.join(File.dirname(__FILE__), 'errors.rb')
8
+ require File.join(File.dirname(__FILE__), 'image.rb')
9
+ require File.join(File.dirname(__FILE__), 'response.rb')
10
+ require File.join(File.dirname(__FILE__), 'gallery.rb')
11
+
12
+ module ImageBam
13
+ POST_URL = URI.parse('http://www.imagebam.com/services/upload/')
14
+ GALLERY_URL = URI.parse('http://www.imagebam.com/services/generate_GID/')
15
+ end
@@ -0,0 +1,55 @@
1
+ require 'rexml/document'
2
+
3
+ module ImageBam
4
+ class Response
5
+ include REXML
6
+ attr_accessor :response
7
+ # What the response XML may look like for uploading images..
8
+
9
+ # <?xml version="1.0" encoding="utf-8" ?>
10
+ # <rsp stat="fail">
11
+ # <err code="3" msg="API_key_user has an invalid format." />
12
+ # </rsp>
13
+
14
+ # <?xml version="1.0" encoding="utf-8" ?>
15
+ # <rsp stat="ok">
16
+ # <ID>12404256</ID>
17
+ # <URL>http://www.imagebam.com/image/b932b912404256</URL>
18
+ # <thumbnail>http://thumbnails6.imagebam.com/1241/b932b912404256.gif</thumbnail>
19
+ # <delcode>http://www.imagebam.com/remove/12404256/981f4bc82a730a7005319e8952c17a36</delcode>
20
+ # </rsp>
21
+
22
+ # For generating a gallery ID ..
23
+
24
+ # <?xml version="1.0" encoding="utf-8" ?>
25
+ # <rsp stat="ok">
26
+ # <GID>5ee0886642b07d5ac912bc131e038532</GID>
27
+ # <URL>http://www.imagebam.com/gallery/5ee0886642b07d5ac912bc131e038532/</URL>
28
+ # </rsp>
29
+
30
+
31
+ def initialize(xml,type)
32
+ @doc = Document.new(xml)
33
+ @response = {}
34
+ @response[:status] = @doc.root.attributes['stat']
35
+ if( @response[:status] == 'fail')
36
+
37
+ @response[:error] = {:code => @doc.root.get_elements('err').first.attributes['code'],
38
+ :message => @doc.root.get_elements('err').first.attributes['msg']}
39
+
40
+ raise ImageBam::Errors::ResponseError, @response[:error][:message]
41
+ else
42
+ case type
43
+ when 'image':
44
+ @response[:id] = @doc.root.get_elements('ID').first.get_text
45
+ @response[:url] = @doc.root.get_elements('URL').first.get_text
46
+ @response[:thumbnail] = @doc.root.get_elements('thumbnail').first.get_text
47
+ @response[:delcode] = @doc.root.get_elements('delcode').first.get_text
48
+ when 'gallery':
49
+ @response[:gid] = @doc.root.get_elements('GID').first.get_text
50
+ @response[:url] = @doc.root.get_elements('URL').first.get_text
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
File without changes
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jamescook-ruby-imagebam
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - James Cook
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-09-03 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mime-types
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">"
21
+ - !ruby/object:Gem::Version
22
+ version: 0.0.0
23
+ version:
24
+ description: ruby-imagebam interfaces with the ImageBam API to easily upload your images to ImageBam's image hosting service.
25
+ email: none@google.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - README
34
+ - demo.rb
35
+ - image_bam.rb
36
+ - lib/errors.rb
37
+ - lib/image_bam.rb
38
+ - lib/response.rb
39
+ - lib/image.rb
40
+ - lib/gallery.rb
41
+ has_rdoc: false
42
+ homepage: http://github.com/jamescook/ruby-imagebam/tree/master
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.2.0
64
+ signing_key:
65
+ specification_version: 2
66
+ summary: Simple interface to the ImageBam API
67
+ test_files:
68
+ - test/test.rb