remove_bg 1.2.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,6 +8,8 @@ module RemoveBg
8
8
  HTTP_BASE_TIMEOUT = 10
9
9
  HTTP_WRITE_TIMEOUT = 120
10
10
 
11
+ # @return [Faraday::Connection]
12
+ #
11
13
  def self.build(api_url = RemoveBg::Api::URL)
12
14
  retry_options = {
13
15
  max: 2,
@@ -0,0 +1,60 @@
1
+ require_relative "error"
2
+
3
+ module RemoveBg
4
+ # Combines alpha.png and color.jpg files to produce a full-sized transparent PNG.
5
+ # An image processing library (ImageMagick, GraphicsMagick, or libvips) must
6
+ # be available on the system.
7
+ # @see RemoveBg::CompositeResult
8
+ #
9
+ class ImageComposer
10
+ DEFAULT_BINARY_DETECTOR = lambda do |binary_name|
11
+ system("which", binary_name, out: File::NULL)
12
+ end
13
+
14
+ def self.detect_image_processor(detector: DEFAULT_BINARY_DETECTOR)
15
+ case
16
+ when detector.call("magick"), detector.call("convert"), detector.call("gm")
17
+ :minimagick
18
+ when detector.call("vips")
19
+ :vips
20
+ end
21
+ end
22
+
23
+ def compose(color_file:, alpha_file:, destination_path:)
24
+ image = case configured_image_processor
25
+ when :vips
26
+ then vips_compose(color_file: color_file, alpha_file: alpha_file)
27
+ when :minimagick
28
+ then minimagick_compose(color_file: color_file, alpha_file: alpha_file)
29
+ when nil
30
+ raise RemoveBg::Error, "Please configure an image processor to use image composition"
31
+ else
32
+ raise RemoveBg::Error, "Unsupported image processor: #{configured_image_processor.inspect}"
33
+ end
34
+
35
+ image.call(destination: destination_path)
36
+ end
37
+
38
+ private
39
+
40
+ def configured_image_processor
41
+ RemoveBg::Configuration.configuration.image_processor
42
+ end
43
+
44
+ def minimagick_compose(color_file:, alpha_file:)
45
+ require "image_processing/mini_magick"
46
+
47
+ ImageProcessing::MiniMagick
48
+ .source(color_file)
49
+ .composite(alpha_file, mode: "copy-opacity")
50
+ end
51
+
52
+ def vips_compose(color_file:, alpha_file:)
53
+ require "image_processing/vips"
54
+
55
+ ImageProcessing::Vips
56
+ .source(color_file)
57
+ .custom { |image| image.bandjoin(Vips::Image.new_from_file(alpha_file.path)) }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,34 @@
1
+ require "time"
2
+
3
+ module RemoveBg
4
+ class RateLimitInfo
5
+ attr_reader :total, :remaining, :retry_after_seconds
6
+
7
+ def initialize(headers)
8
+ @total = headers["X-RateLimit-Limit"]&.to_i
9
+ @remaining = headers["X-RateLimit-Remaining"]&.to_i
10
+ @reset_timestamp = headers["X-RateLimit-Reset"]&.to_i
11
+
12
+ # Only present if rate limit exceeded
13
+ @retry_after_seconds = headers["Retry-After"]&.to_i
14
+ end
15
+
16
+ def reset_at
17
+ return if reset_timestamp.nil?
18
+ Time.at(reset_timestamp).utc
19
+ end
20
+
21
+ def to_s
22
+ "<RateLimit"\
23
+ " reset_at='#{reset_at.iso8601}'"\
24
+ " retry_after_seconds=#{retry_after_seconds}"\
25
+ " total=#{total}"\
26
+ " remaining=#{remaining}"\
27
+ ">"
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :reset_timestamp
33
+ end
34
+ end
@@ -1,7 +1,9 @@
1
- require_relative "error"
1
+ require_relative "base_request_options"
2
2
 
3
3
  module RemoveBg
4
- class RequestOptions
4
+ # Options for image processing requests. Arbitary options are passed directly to the API.
5
+ #
6
+ class RequestOptions < BaseRequestOptions
5
7
  SIZE_REGULAR = "regular"
6
8
  SIZE_MEDIUM = "medium"
7
9
  SIZE_HD = "hd"
@@ -15,32 +17,40 @@ module RemoveBg
15
17
  CHANNELS_RGBA = "rgba"
16
18
  CHANNELS_ALPHA = "alpha"
17
19
 
18
- attr_reader :api_key, :data
20
+ FORMAT_PNG = "png"
21
+ FORMAT_ZIP = "zip"
22
+ FORMAT_JPG = "jpg"
19
23
 
20
24
  def initialize(raw_options = {})
21
25
  options = raw_options.dup
22
26
  options[:size] ||= SIZE_AUTO
23
- @api_key = resolve_api_key(options.delete(:api_key))
24
- @data = options
27
+
28
+ if options.key?(:format)
29
+ options[:format] = optimize_format(options[:format])
30
+ end
31
+
32
+ super(options)
25
33
  end
26
34
 
27
35
  private
28
36
 
29
- def resolve_api_key(request_api_key)
30
- api_key = request_api_key || global_api_key
37
+ # Save bandwidth where possible
38
+ def optimize_format(requested_format)
39
+ requested_png = requested_format.to_s.casecmp?(FORMAT_PNG)
31
40
 
32
- if api_key.nil? || api_key.empty?
33
- raise RemoveBg::Error, <<~MSG
34
- Please configure an API key or specify one per request. API key was:
35
- #{api_key.inspect}
36
- MSG
41
+ if requested_png && optimization_enabled? && can_process_images?
42
+ FORMAT_ZIP
43
+ else
44
+ requested_format
37
45
  end
46
+ end
38
47
 
39
- api_key
48
+ def can_process_images?
49
+ RemoveBg::Configuration.configuration.can_process_images?
40
50
  end
41
51
 
42
- def global_api_key
43
- RemoveBg::Configuration.configuration.api_key
52
+ def optimization_enabled?
53
+ RemoveBg::Configuration.configuration.auto_upgrade_png_to_zip
44
54
  end
45
55
  end
46
56
  end
@@ -1,22 +1,57 @@
1
+ require "fileutils"
2
+ require "forwardable"
3
+ require "zip"
1
4
  require_relative "error"
5
+ require_relative "image_composer"
2
6
 
3
7
  module RemoveBg
8
+ # Provides convenience methods to save the processed image, read the image data,
9
+ # and access metadata such as the image height/width and credits charged.
10
+ #
4
11
  class Result
5
- attr_reader :data, :width, :height, :credits_charged
12
+ extend ::Forwardable
6
13
 
7
- def initialize(data:, width:, height:, credits_charged:)
8
- @data = data
9
- @width = width
10
- @height = height
11
- @credits_charged = credits_charged
14
+ # @return [RemoveBg::ResultMetadata]
15
+ attr_reader :metadata
16
+
17
+ # @return [RemoveBg::RateLimitInfo]
18
+ attr_reader :rate_limit
19
+
20
+ def_delegators :metadata, :type, :width, :height, :credits_charged
21
+
22
+ def initialize(download:, metadata:, rate_limit:)
23
+ @download = download
24
+ @metadata = metadata
25
+ @rate_limit = rate_limit
12
26
  end
13
27
 
28
+ # Saves the processed image to the path specified
29
+ # @param file_path [string]
30
+ # @param overwrite [boolean] Overwrite any existing file at the specified path
31
+ # @return [nil]
32
+ #
14
33
  def save(file_path, overwrite: false)
15
34
  if File.exist?(file_path) && !overwrite
16
35
  raise FileOverwriteError.new(file_path)
17
36
  end
18
37
 
19
- File.write(file_path, data)
38
+ FileUtils.cp(image_file, file_path)
39
+ end
40
+
41
+ # Returns the binary data of the processed image
42
+ # @return [String]
43
+ #
44
+ def data
45
+ image_file.rewind
46
+ image_file.read
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :download
52
+
53
+ def image_file
54
+ download
20
55
  end
21
56
  end
22
57
  end
@@ -0,0 +1,14 @@
1
+ require_relative "api"
2
+
3
+ module RemoveBg
4
+ class ResultMetadata
5
+ attr_reader :type, :width, :height, :credits_charged
6
+
7
+ def initialize(headers)
8
+ @type = headers["X-Type"]
9
+ @width = headers["X-Width"]&.to_i
10
+ @height = headers["X-Height"]&.to_i
11
+ @credits_charged = headers["X-Credits-Charged"]&.to_f
12
+ end
13
+ end
14
+ end
@@ -1,4 +1,4 @@
1
- require "faraday/upload_io"
1
+ require "faraday"
2
2
  require_relative "error"
3
3
 
4
4
  module RemoveBg
@@ -9,7 +9,7 @@ module RemoveBg
9
9
  end
10
10
 
11
11
  content_type = determine_content_type(file_path)
12
- Faraday::UploadIO.new(file_path, content_type)
12
+ FARADAY_FILE.new(file_path, content_type)
13
13
  end
14
14
 
15
15
  def self.determine_content_type(file_path)
@@ -22,5 +22,9 @@ module RemoveBg
22
22
  end
23
23
 
24
24
  private_class_method :determine_content_type
25
+
26
+ # UploadIO for Faraday < 0.16.0
27
+ FARADAY_FILE = defined?(Faraday::FilePart) ? Faraday::FilePart : Faraday::UploadIO
28
+ private_constant :FARADAY_FILE
25
29
  end
26
30
  end
@@ -1,3 +1,3 @@
1
1
  module RemoveBg
2
- VERSION = "1.2.0"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -23,16 +23,21 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.add_dependency "faraday", ">= 0.14", "< 2"
26
+ spec.add_dependency "faraday", ">= 0.15", "< 2"
27
+ spec.add_dependency "image_processing", ">= 1.9", "< 2"
28
+ spec.add_dependency "rubyzip", ">= 2.0", "< 3"
27
29
 
28
- spec.add_development_dependency "appraisal"
29
- spec.add_development_dependency "bundler", "~> 1.17"
30
+ spec.add_development_dependency "bundler", "~> 2.0"
31
+ spec.add_development_dependency "codecov"
30
32
  spec.add_development_dependency "dotenv"
31
33
  spec.add_development_dependency "pry"
32
- spec.add_development_dependency "rake", "~> 12.0"
34
+ spec.add_development_dependency "rake"
33
35
  spec.add_development_dependency "rspec_junit_formatter"
34
36
  spec.add_development_dependency "rspec-with_params"
35
37
  spec.add_development_dependency "rspec", "~> 3.8"
38
+ spec.add_development_dependency "simplecov"
39
+ spec.add_development_dependency "vcr_better_binary"
36
40
  spec.add_development_dependency "vcr"
37
41
  spec.add_development_dependency "webmock"
42
+ spec.add_development_dependency "yard"
38
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remove_bg
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oliver Peate
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-07-12 00:00:00.000000000 Z
11
+ date: 2020-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0.14'
19
+ version: '0.15'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '2'
@@ -26,38 +26,78 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '0.14'
29
+ version: '0.15'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '2'
33
33
  - !ruby/object:Gem::Dependency
34
- name: appraisal
34
+ name: image_processing
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
40
- type: :development
39
+ version: '1.9'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '2'
43
+ type: :runtime
41
44
  prerelease: false
42
45
  version_requirements: !ruby/object:Gem::Requirement
43
46
  requirements:
44
47
  - - ">="
45
48
  - !ruby/object:Gem::Version
46
- version: '0'
49
+ version: '1.9'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '2'
53
+ - !ruby/object:Gem::Dependency
54
+ name: rubyzip
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '2.0'
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '3'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '2.0'
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '3'
47
73
  - !ruby/object:Gem::Dependency
48
74
  name: bundler
49
75
  requirement: !ruby/object:Gem::Requirement
50
76
  requirements:
51
77
  - - "~>"
52
78
  - !ruby/object:Gem::Version
53
- version: '1.17'
79
+ version: '2.0'
54
80
  type: :development
55
81
  prerelease: false
56
82
  version_requirements: !ruby/object:Gem::Requirement
57
83
  requirements:
58
84
  - - "~>"
59
85
  - !ruby/object:Gem::Version
60
- version: '1.17'
86
+ version: '2.0'
87
+ - !ruby/object:Gem::Dependency
88
+ name: codecov
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ type: :development
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
61
101
  - !ruby/object:Gem::Dependency
62
102
  name: dotenv
63
103
  requirement: !ruby/object:Gem::Requirement
@@ -90,16 +130,16 @@ dependencies:
90
130
  name: rake
91
131
  requirement: !ruby/object:Gem::Requirement
92
132
  requirements:
93
- - - "~>"
133
+ - - ">="
94
134
  - !ruby/object:Gem::Version
95
- version: '12.0'
135
+ version: '0'
96
136
  type: :development
97
137
  prerelease: false
98
138
  version_requirements: !ruby/object:Gem::Requirement
99
139
  requirements:
100
- - - "~>"
140
+ - - ">="
101
141
  - !ruby/object:Gem::Version
102
- version: '12.0'
142
+ version: '0'
103
143
  - !ruby/object:Gem::Dependency
104
144
  name: rspec_junit_formatter
105
145
  requirement: !ruby/object:Gem::Requirement
@@ -142,6 +182,34 @@ dependencies:
142
182
  - - "~>"
143
183
  - !ruby/object:Gem::Version
144
184
  version: '3.8'
185
+ - !ruby/object:Gem::Dependency
186
+ name: simplecov
187
+ requirement: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ type: :development
193
+ prerelease: false
194
+ version_requirements: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: '0'
199
+ - !ruby/object:Gem::Dependency
200
+ name: vcr_better_binary
201
+ requirement: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ type: :development
207
+ prerelease: false
208
+ version_requirements: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
145
213
  - !ruby/object:Gem::Dependency
146
214
  name: vcr
147
215
  requirement: !ruby/object:Gem::Requirement
@@ -170,6 +238,20 @@ dependencies:
170
238
  - - ">="
171
239
  - !ruby/object:Gem::Version
172
240
  version: '0'
241
+ - !ruby/object:Gem::Dependency
242
+ name: yard
243
+ requirement: !ruby/object:Gem::Requirement
244
+ requirements:
245
+ - - ">="
246
+ - !ruby/object:Gem::Version
247
+ version: '0'
248
+ type: :development
249
+ prerelease: false
250
+ version_requirements: !ruby/object:Gem::Requirement
251
+ requirements:
252
+ - - ">="
253
+ - !ruby/object:Gem::Version
254
+ version: '0'
173
255
  description:
174
256
  email:
175
257
  - team@remove.bg
@@ -190,17 +272,24 @@ files:
190
272
  - Rakefile
191
273
  - bin/console
192
274
  - bin/setup
193
- - gemfiles/faraday_0_14.gemfile
194
275
  - gemfiles/faraday_0_15.gemfile
195
- - gemfiles/faraday_1rc1.gemfile
276
+ - gemfiles/faraday_0_16.gemfile
277
+ - gemfiles/faraday_0_17.gemfile
278
+ - gemfiles/faraday_1_x.gemfile
196
279
  - lib/remove_bg.rb
280
+ - lib/remove_bg/account_info.rb
197
281
  - lib/remove_bg/api.rb
198
282
  - lib/remove_bg/api_client.rb
283
+ - lib/remove_bg/base_request_options.rb
284
+ - lib/remove_bg/composite_result.rb
199
285
  - lib/remove_bg/configuration.rb
200
286
  - lib/remove_bg/error.rb
201
287
  - lib/remove_bg/http_connection.rb
288
+ - lib/remove_bg/image_composer.rb
289
+ - lib/remove_bg/rate_limit_info.rb
202
290
  - lib/remove_bg/request_options.rb
203
291
  - lib/remove_bg/result.rb
292
+ - lib/remove_bg/result_metadata.rb
204
293
  - lib/remove_bg/upload.rb
205
294
  - lib/remove_bg/url_validator.rb
206
295
  - lib/remove_bg/version.rb
@@ -227,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
316
  - !ruby/object:Gem::Version
228
317
  version: '0'
229
318
  requirements: []
230
- rubygems_version: 3.0.4
319
+ rubygems_version: 3.1.2
231
320
  signing_key:
232
321
  specification_version: 4
233
322
  summary: Remove image background - 100% automatically