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 +37 -0
- data/demo.rb +12 -0
- data/image_bam.rb +1 -0
- data/lib/errors.rb +19 -0
- data/lib/gallery.rb +23 -0
- data/lib/image.rb +155 -0
- data/lib/image_bam.rb +15 -0
- data/lib/response.rb +55 -0
- data/test/test.rb +0 -0
- metadata +68 -0
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
|
+
|
data/image_bam.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/lib/image_bam.rb'
|
data/lib/errors.rb
ADDED
@@ -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
|
data/lib/gallery.rb
ADDED
@@ -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
|
+
|
data/lib/image.rb
ADDED
@@ -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
|
data/lib/image_bam.rb
ADDED
@@ -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
|
data/lib/response.rb
ADDED
@@ -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
|
data/test/test.rb
ADDED
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
|