shrine-blurhash 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d996837d159a23efc83e220f7f937ad440bf3acfe98093f7483b4e08de221091
4
+ data.tar.gz: d956efac6992fff592f1b37c46fbbb9bdfe2b09ef5dfb3414feee57eee9adc5b
5
+ SHA512:
6
+ metadata.gz: 32e9956e147d75ab3bb218c58e14dd46eeb4878a9d91812e064d17cb23d7210aebce352280f64b775849231a0d40a032b3f82418dd16d6f83be7c68da71976bc
7
+ data.tar.gz: 27c0e803e7f930c70629ec1a70a6aa256c05eea6f6fbf65cccc9e348f78d7027555d85d5e72d03d5409c2f23f7826fc3f2fd0891cc1a48f419948a6f4b2171ef
@@ -0,0 +1,128 @@
1
+ [![Gem Version](https://badge.fury.io/rb/shrine-blurhash.svg)](https://badge.fury.io/rb/shrine-blurhash)
2
+
3
+ # Shrine::Blurhash
4
+
5
+ Provides [Blurhash] computing for [Shrine].
6
+
7
+ Main features:
8
+ - Multiple pixel extractors support. Current implementations:
9
+ - `ruby-vips`
10
+ - Resize the image before computing blurhash (to 100 pixels by default) for a faster computation
11
+ - Supports custom blurhash components parameter
12
+
13
+ ## Installation
14
+
15
+ Simply add the gem to your `Gemfile`:
16
+
17
+ ```ruby
18
+ gem "shrine-blurhash"
19
+ ```
20
+
21
+ Then run `bundler`
22
+
23
+ ## Usage
24
+
25
+ You first need to load the plugin in your Uploader;
26
+
27
+ ```ruby
28
+ class PictureUploader < Shrine
29
+ plugin :blurhash
30
+ end
31
+ ```
32
+
33
+ Blurhash will be now be computed when you upload a file and stored in a `blurhash` metadata, with a `.blurhash` method for quick access.
34
+
35
+ ## Options
36
+
37
+ You can pass options to the plugin to customize its behavior.
38
+
39
+ ```ruby
40
+ class PictureUploader < Shrine
41
+ plugin :blurhash, resize_to: nil, components: [2, 2]
42
+ end
43
+ ```
44
+
45
+ ### ```components```
46
+
47
+ Type: `[components_x, components_y]`
48
+ Default: `[4, 3]`
49
+
50
+ ```ruby
51
+ plugin :blurhash, components: [2, 2]
52
+ ```
53
+
54
+ This allows you to customize the number of components on each axis for the Blurhash algorithm. The visual result will look like a grid of `X * Y` blurred colors.
55
+
56
+ ### `resize_to`
57
+
58
+ Type: `Integer` or `nil`
59
+ Default: `100`
60
+
61
+ ```ruby
62
+ plugin :blurhash, resize_to: 100
63
+ plugin :blurhash, resize_to: nil
64
+ ```
65
+
66
+ Before computing the Blurhash for an image, this plugin needs to extract every pixel and store them in an array. This can result in a lot of memory allocations. To avoid using too much memory, this plugin resizes the image before extracting the pixels. This should not affect the visual result.
67
+
68
+ You can either specify the target size or `nil` to disable image resizing completely.
69
+
70
+ ### `extractor`
71
+
72
+ Type: `symbol` or `lambda`
73
+
74
+ ```ruby
75
+ plugin :blurhash, extractor: :ruby-vips
76
+ ```
77
+
78
+ Supported extractors:
79
+
80
+ | Name | Description |
81
+ | :----------- | :----------- |
82
+ | `:ruby_vips` | Uses the [ruby-vips] gem to extract pixels from File objects. If non-file IO object is given it will be temporarily downloaded to disk. |
83
+
84
+ You can also create your own custom pixel extractor, where you can reuse
85
+ any of the built-in analyzers. The analyzer is a lambda that accepts an IO
86
+ object and returns a `[width, height, pixels]` array containing image width, image height and all the pixels in the images in an array, or `nil` if pixels could not be extracted.
87
+
88
+ ### `auto_extraction`
89
+
90
+ Type: `boolean`
91
+
92
+ - Multiple pixel extractors support, even if only VIPS is implemented right now
93
+ - Allows to resize the image before computing blurhash (to 100 pixels by default) for a faster computation
94
+ - Supports custom blurhash components parameter
95
+
96
+ ## Errors
97
+
98
+ By default, any exceptions that the plugin raises while computing blurhash
99
+ will be caught and a warning will be printed out. This allows you to have the
100
+ plugin loaded even for files that are not images.
101
+
102
+ However, you can choose different strategies for handling these exceptions:
103
+
104
+ ```rb
105
+ plugin :blurhash, on_error: :warn # prints a warning (default)
106
+ plugin :blurhash, on_error: :fail # raises the exception
107
+ plugin :blurhash, on_error: :ignore # ignores exceptions
108
+ plugin :blurhash, on_error: -> (error) { # custom handler
109
+ # report the exception to your exception handler
110
+ }
111
+ ```
112
+
113
+ ## Contributing
114
+
115
+ ### Running tests
116
+
117
+ After setting up your bucket, run the tests:
118
+
119
+ ```sh
120
+ $ bundle exec rake test
121
+ ```
122
+
123
+ ## License
124
+
125
+ [MIT](http://opensource.org/licenses/MIT)
126
+
127
+ [Shrine]: https://github.com/shrinerb/shrine
128
+ [Blurhash]: https://blurha.sh
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "blurhash"
4
+
5
+ class Shrine
6
+ module Plugins
7
+ module Blurhash
8
+ LOG_SUBSCRIBER = lambda do |event|
9
+ Shrine.logger.info "Blurhash (#{event.duration}ms) – #{{
10
+ io: event[:io].class,
11
+ uploader: event[:uploader],
12
+ }.inspect}"
13
+ end
14
+
15
+ def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
16
+ uploader.opts[:blurhash] ||= {
17
+ extractor: :ruby_vips,
18
+ on_error: :warn,
19
+ auto_extraction: true,
20
+ resize_to: 100,
21
+ components: [4, 3],
22
+ }
23
+ uploader.opts[:blurhash].merge!(opts)
24
+
25
+ # resolve error strategy
26
+ uploader.opts[:blurhash][:on_error] =
27
+ case uploader.opts[:blurhash][:on_error]
28
+ when :fail then ->(error) { raise error }
29
+ when :warn then ->(error) { Shrine.warn "Error occurred when attempting to extract blurhash: #{error.inspect}" }
30
+ when :ignore then ->(error) {}
31
+ else
32
+ uploader.opts[:blurhash][:on_error]
33
+ end
34
+
35
+ uploader.subscribe(:blurhash, &log_subscriber) if uploader.respond_to?(:subscribe)
36
+ end
37
+
38
+ module ClassMethods
39
+ def compute_blurhash(io)
40
+ extractor = opts[:blurhash][:extractor]
41
+ extractor = pixels_extractor(extractor) if extractor.is_a?(Symbol)
42
+
43
+ resize_to = opts[:blurhash][:resize_to]
44
+ args = [io, resize_to, pixels_extractors].take(extractor.arity.abs)
45
+
46
+ blurhash = instrument_blurhash(io) do
47
+ pixels = extractor.call(*args)
48
+
49
+ components = opts[:blurhash][:components]
50
+ ::Blurhash.encode(pixels[:width], pixels[:height], pixels[:pixels], x_comp: components[0], y_comp: components[1])
51
+ end
52
+
53
+ io.rewind
54
+
55
+ blurhash
56
+ end
57
+
58
+ # Returns a hash of built-in pixels extractors, where keys are
59
+ # extractors names and values are `#call`-able objects which accepts the
60
+ # IO object.
61
+ def pixels_extractors
62
+ @pixels_extractors ||= PixelsExtractor::SUPPORTED_EXTRACTORS.inject({}) do |hash, tool|
63
+ hash.merge!(tool => pixels_extractor(tool))
64
+ end
65
+ end
66
+
67
+ # Returns callable pixels extractor object.
68
+ def pixels_extractor(name)
69
+ on_error = opts[:blurhash][:on_error]
70
+
71
+ PixelsExtractor.new(name, on_error: on_error).method(:call)
72
+ end
73
+
74
+ private
75
+
76
+ # Sends a `blurhash.shrine` event for instrumentation plugin.
77
+ def instrument_blurhash(io, &block)
78
+ return yield unless respond_to?(:instrument)
79
+
80
+ instrument(:blurhash, io: io, &block)
81
+ end
82
+ end
83
+
84
+ module InstanceMethods
85
+ def extract_metadata(io, **options)
86
+ return super unless self.class.opts[:blurhash][:auto_extraction]
87
+
88
+ blurhash = self.class.compute_blurhash(io)
89
+ super.merge!("blurhash" => blurhash)
90
+ end
91
+ end
92
+
93
+ module FileMethods
94
+ def blurhash
95
+ metadata["blurhash"]
96
+ end
97
+ end
98
+
99
+ class PixelsExtractor
100
+ SUPPORTED_EXTRACTORS = [:ruby_vips].freeze
101
+
102
+ def initialize(tool, on_error: method(:fail))
103
+ unless SUPPORTED_EXTRACTORS.include?(tool)
104
+ raise Error, "unknown pixel extractor #{tool.inspect}, supported extractors are: #{SUPPORTED_EXTRACTORS.join(',')}"
105
+ end
106
+
107
+ @tool = tool
108
+ @on_error = on_error
109
+ end
110
+
111
+ def call(io, resize_to)
112
+ dimensions = send(:"extract_with_#{@tool}", io, resize_to)
113
+ io.rewind
114
+ dimensions
115
+ end
116
+
117
+ private
118
+
119
+ def extract_with_ruby_vips(io, resize_to)
120
+ require "vips"
121
+
122
+ begin
123
+ Shrine.with_file(io) do |file|
124
+ image = Vips::Image.new_from_file(file.path, access: :sequential)
125
+ image = image.resize(resize_to.fdiv(image.width), vscale: resize_to.fdiv(image.height)) if resize_to
126
+
127
+ {
128
+ width: image.width,
129
+ height: image.height,
130
+ pixels: image.to_a.flatten,
131
+ }
132
+ end
133
+ rescue Vips::Error => e
134
+ on_error(e)
135
+ end
136
+ end
137
+
138
+ def on_error(error)
139
+ @on_error.call(error)
140
+ nil
141
+ end
142
+ end
143
+ end
144
+
145
+ register_plugin(:blurhash, Blurhash)
146
+ end
147
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "shrine-blurhash"
5
+ gem.version = "0.0.1"
6
+
7
+ gem.required_ruby_version = ">= 2.5"
8
+
9
+ gem.summary = "Shrine plugin to compute Blurhash on image attachments"
10
+ gem.homepage = "https://github.com/renchap/shrine-blurhash"
11
+ gem.authors = ["Renaud Chaput"]
12
+ gem.email = ["renchap@gmail.com"]
13
+ gem.license = "MIT"
14
+
15
+ gem.files = Dir["README.md", "LICENSE.txt", "lib/**/*.rb", "shrine-blurhash.gemspec"]
16
+ gem.require_path = "lib"
17
+
18
+ gem.add_dependency "blurhash", "~> 0.1.4"
19
+ gem.add_dependency "shrine", "~> 3.0"
20
+
21
+ gem.add_development_dependency "minitest"
22
+ gem.add_development_dependency "rake"
23
+ gem.add_development_dependency "ruby-vips"
24
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shrine-blurhash
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Renaud Chaput
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: blurhash
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: shrine
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby-vips
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - renchap@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - README.md
91
+ - lib/shrine/plugins/blurhash.rb
92
+ - shrine-blurhash.gemspec
93
+ homepage: https://github.com/renchap/shrine-blurhash
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '2.5'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubygems_version: 3.0.3
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Shrine plugin to compute Blurhash on image attachments
116
+ test_files: []