jekyll-imgflow 0.3.3 → 0.4.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.
@@ -2,144 +2,98 @@
2
2
 
3
3
  require "net/http"
4
4
  require "uri"
5
- require "tempfile"
6
5
  require_relative "base_provider"
7
6
 
8
7
  module JekyllImgFlow
9
8
  module Providers
10
9
  # Flyimg provider implementation using the standardized tag interface
11
- class Flyimg < BaseProvider
10
+ class Flyimg < HttpBase
12
11
  TIMEOUT = 60
13
12
 
14
- def available?
15
- # Check if flyimg service is running
16
- return false unless @config.respond_to?(:flyimg_url) && @config.flyimg_url
17
-
18
- check_http_service(@config.flyimg_url)
13
+ def service_url
14
+ @config.respond_to?(:flyimg_url) ? @config.flyimg_url : nil
19
15
  end
20
16
 
21
- def execute(input_path, output_path)
22
- return if @operations.empty?
23
-
24
- # Build single Flyimg URL with all operations combined
25
- url = build_combined_flyimg_url(input_path)
26
-
27
- # Fetch the result in one request
28
- fetch_and_save(url, output_path)
29
- output_path
30
- ensure
31
- reset_operations
17
+ def build_combined_flyimg_url(input_path)
18
+ build_combined_url(input_path)
32
19
  end
33
20
 
34
- def build_combined_flyimg_url(input_path)
35
- raise "flyimg_url not set" unless @config.flyimg_url
21
+ def build_combined_url(input_path)
22
+ raise "flyimg_url not set" unless service_url
36
23
 
37
24
  encoded_url = encode_file_url(input_path)
38
25
  options = []
39
26
 
40
- # Add all operations to single options string
41
- @operations.each do |operation|
42
- case operation[:type]
43
- when :resize
44
- # Tags now provide complete calculated values.
45
- # c_1 forces the image to fill the exact width x height area.
46
- # pns_0 allows upscaling when the source is smaller than the target
47
- # (Flyimg's pns/preserve-natural-size defaults to 1, blocking enlarge).
48
- options << "w_#{operation[:width]}"
49
- options << "pns_0"
50
- if operation[:height]
51
- options << "h_#{operation[:height]}"
52
- options << "c_1"
53
- end
54
-
55
- when :crop
56
- opts = operation[:options]
57
- params = operation[:params] || {}
58
-
59
- # Check for keep parameter (smartcrop support)
60
- keep = opts[:keep] || params[:keep] || params[:position]
61
-
62
- if operation[:ratio] && keep && SMARTCROP_POSITIONS.include?(keep.to_s)
63
- # Use smartcrop (Flyimg's smc option picks the best crop area)
64
- crop_width = opts[:calculated_width]
65
- crop_height = opts[:calculated_height]
66
-
67
- options << "w_#{crop_width}"
68
- options << "h_#{crop_height}"
69
- options << "c_1"
70
- options << "smc_1"
71
- else
72
- # Use basic cropping via extract (top-left / bottom-right coordinates)
73
- if operation[:ratio]
74
- crop_x = opts[:calculated_x]
75
- crop_y = opts[:calculated_y]
76
- crop_width = opts[:calculated_width]
77
- crop_height = opts[:calculated_height]
78
- else
79
- crop_x = opts[:x]
80
- crop_y = opts[:y]
81
- crop_width = opts[:width]
82
- crop_height = opts[:height]
83
- end
84
- options << "e_1"
85
- options << "p1x_#{crop_x}"
86
- options << "p1y_#{crop_y}"
87
- options << "p2x_#{crop_x + crop_width}"
88
- options << "p2y_#{crop_y + crop_height}"
89
- end
90
-
91
- when :quality
92
- flyimg_quality = translate_quality_to_flyimg(operation[:quality])
93
- options << "q_#{flyimg_quality}"
94
-
95
- when :format
96
- # Assume validated input from tags
97
- options << "o_#{operation[:format]}"
98
-
99
- when :watermark
100
- watermark_path = operation[:watermark_path]
101
- position = operation[:options][:position]
102
- opacity = operation[:options][:opacity]
103
-
104
- # Flyimg watermark parameters
105
- encoded_watermark = encode_file_url(watermark_path)
106
- position_param = translate_flyimg_position(position)
107
- opacity_param = (opacity * 100).round
108
- options << "wm_#{encoded_watermark}_#{position_param}_#{opacity_param}"
109
-
110
- when :alpha_opacity
111
- opacity = operation[:opacity]
112
- options << "a_#{(opacity * 255).round}"
113
- end
114
- end
27
+ @operations.each { |operation| append_flyimg_operation(operation, options) }
28
+
29
+ preserve_input_format(input_path, options)
30
+ "#{service_url}/upload/#{options.join(',')}/#{encoded_url}"
31
+ end
115
32
 
116
- # Preserve format when no explicit format operation is requested.
117
- # Without this, chained HTTP requests (e.g. quality-only steps after a
118
- # prior format conversion) would lose the previously converted format.
119
- unless @operations.any? { |op| op[:type] == :format }
120
- input_ext = File.extname(input_path).delete(".").downcase
121
- options << "o_#{map_format(input_ext)}" unless input_ext.empty?
33
+ private
34
+
35
+ def append_flyimg_operation(operation, options)
36
+ case operation[:type]
37
+ when :resize
38
+ append_flyimg_resize(operation, options)
39
+ when :crop
40
+ append_flyimg_crop(operation, options)
41
+ when :quality
42
+ options << "q_#{operation[:quality]}"
43
+ when :format
44
+ options << "o_#{operation[:format]}"
45
+ when :watermark
46
+ append_flyimg_watermark(operation, options)
47
+ when :alpha_opacity
48
+ options << "a_#{alpha_byte_value(operation[:opacity])}"
122
49
  end
50
+ end
51
+
52
+ def append_flyimg_resize(operation, options)
53
+ # c_1 forces the image to fill the exact width x height area.
54
+ # pns_0 allows upscaling when the source is smaller than the target
55
+ # (Flyimg's pns/preserve-natural-size defaults to 1, blocking enlarge).
56
+ options << "w_#{operation[:width]}"
57
+ options << "pns_0"
58
+ return unless operation[:height]
123
59
 
124
- # Build combined URL: base/upload/options/options,.../encoded_url
125
- options_string = options.join(",")
126
- "#{@config.flyimg_url}/upload/#{options_string}/#{encoded_url}"
60
+ options << "h_#{operation[:height]}"
61
+ options << "c_1"
127
62
  end
128
63
 
129
- private
64
+ def append_flyimg_crop(operation, options)
65
+ geo = crop_geometry(operation)
130
66
 
131
- def translate_flyimg_position(position)
132
- # Translate compass directions to Flyimg position format
133
- case position
134
- when "northwest" then "tl"
135
- when "northeast" then "tr"
136
- when "southwest" then "bl"
137
- when "southeast" then "br"
138
- when "center" then "c"
139
- else position
67
+ if geo[:smartcrop]
68
+ options << "w_#{geo[:width]}"
69
+ options << "h_#{geo[:height]}"
70
+ options << "c_1"
71
+ options << "smc_1"
72
+ else
73
+ # Use basic cropping via extract (top-left / bottom-right coordinates)
74
+ options << "e_1"
75
+ options << "p1x_#{geo[:x]}"
76
+ options << "p1y_#{geo[:y]}"
77
+ options << "p2x_#{geo[:x] + geo[:width]}"
78
+ options << "p2y_#{geo[:y] + geo[:height]}"
140
79
  end
141
80
  end
142
81
 
82
+ def append_flyimg_watermark(operation, options)
83
+ parts = watermark_parts(operation)
84
+ encoded_watermark = encode_file_url(parts[:watermark_path])
85
+ position_param = compass_to_short(parts[:position])
86
+ opacity_param = (parts[:opacity] * 100).round
87
+ options << "wm_#{encoded_watermark}_#{position_param}_#{opacity_param}"
88
+ end
89
+
90
+ def preserve_input_format(input_path, options)
91
+ return if op?(:format)
92
+
93
+ ext = input_format_ext(input_path)
94
+ options << "o_#{map_format(ext)}" unless ext.empty?
95
+ end
96
+
143
97
  def map_format(format)
144
98
  case format&.downcase
145
99
  when "jpeg", "jpg"
@@ -155,33 +109,9 @@ module JekyllImgFlow
155
109
  end
156
110
  end
157
111
 
158
- def fetch_and_save(url, output_path)
159
- response = fetch_with_timeout(url)
160
- raise "Flyimg request failed" if response.empty?
161
-
162
- FileUtils.mkdir_p(File.dirname(output_path))
163
- File.write(output_path, response)
164
- end
165
-
166
- def fetch_with_timeout(url)
167
- uri = URI(url)
168
- Net::HTTP.start(uri.host, uri.port,
169
- use_ssl: uri.scheme == "https",
170
- open_timeout: TIMEOUT,
171
- read_timeout: TIMEOUT) do |http|
172
- request = Net::HTTP::Get.new(uri)
173
- response = http.request(request)
174
-
175
- raise "HTTP #{response.code}: #{response.message}" unless response.is_a?(Net::HTTPSuccess)
176
-
177
- response.body
178
- end
179
- rescue StandardError => e
180
- raise "Flyimg request failed: #{e.message}"
181
- end
182
-
183
- def translate_quality_to_flyimg(quality)
184
- quality
112
+ # Thin wrapper kept for test compatibility.
113
+ def translate_flyimg_position(position)
114
+ compass_to_short(position)
185
115
  end
186
116
  end
187
117
  end
@@ -7,31 +7,22 @@ require_relative "base_provider"
7
7
  module JekyllImgFlow
8
8
  module Providers
9
9
  # ImageMagick provider implementation using the standardized tag interface
10
- class Imagemagick < BaseProvider
10
+ class Imagemagick < CliBase
11
11
  # Cache rsvg delegate check across all instances (checked once per build)
12
12
  @rsvg_available = nil
13
13
  @svg_warning_shown = false
14
14
 
15
15
  def available?
16
- # Check if magick or convert CLI is available
17
- _, _, status1 = Open3.capture3("which", "magick")
18
- _, _, status2 = Open3.capture3("which", "convert")
19
- status1.success? || status2.success?
16
+ cli_available?("magick", "convert")
20
17
  end
21
18
 
22
- def execute(input_path, output_path)
23
- return if @operations.empty?
24
-
19
+ def before_execute(input_path)
25
20
  # Warn once per build about SVG performance with ImageMagick
26
21
  warn_svg_performance if svg?(input_path) && !self.class.instance_variable_get(:@svg_warning_shown)
22
+ end
27
23
 
28
- # Build single ImageMagick command with all operations combined
29
- command = build_combined_imagemagick_command(input_path, output_path)
30
- execute_command(command)
31
-
32
- output_path
33
- ensure
34
- reset_operations
24
+ def build_commands(input_path, output_path)
25
+ build_combined_imagemagick_command(input_path, output_path)
35
26
  end
36
27
 
37
28
  def build_combined_imagemagick_command(input_path, output_path)
@@ -47,85 +38,7 @@ module JekyllImgFlow
47
38
  ["magick", input_path.shellescape]
48
39
  end
49
40
 
50
- # Add all operations
51
- @operations.each do |operation|
52
- case operation[:type]
53
- when :resize
54
- # Tags now provide complete calculated values
55
- command_parts << "-resize" << if operation[:height]
56
- "#{operation[:width]}x#{operation[:height]}!"
57
- else
58
- operation[:width].to_s
59
- end
60
-
61
- when :crop
62
- opts = operation[:options]
63
- params = operation[:params] || {}
64
-
65
- # Check for keep parameter (ImageMagick doesn't support smartcrop, but we handle it gracefully)
66
- keep = opts[:keep] || params[:keep] || params[:position]
67
-
68
- if operation[:ratio] && keep && SMARTCROP_POSITIONS.include?(keep.to_s)
69
- # ImageMagick doesn't support smartcrop, but we handle the keep parameter
70
- # Use center gravity as a reasonable fallback for smartcrop requests
71
- crop_width = opts[:calculated_width]
72
- crop_height = opts[:calculated_height]
73
- crop_x = opts[:calculated_x]
74
- crop_y = opts[:calculated_y]
75
-
76
- # Use gravity center for smartcrop-like behavior
77
- crop_spec = "#{crop_width}x#{crop_height}+#{crop_x}+#{crop_y}"
78
- command_parts << "-gravity" << "center"
79
- else
80
- # Use basic cropping
81
- if operation[:ratio]
82
- crop_x = opts[:calculated_x]
83
- crop_y = opts[:calculated_y]
84
- crop_width = opts[:calculated_width]
85
- crop_height = opts[:calculated_height]
86
- else
87
- crop_x = opts[:x]
88
- crop_y = opts[:y]
89
- crop_width = opts[:width]
90
- crop_height = opts[:height]
91
- end
92
- crop_spec = "#{crop_width}x#{crop_height}+#{crop_x}+#{crop_y}"
93
- end
94
- command_parts << "-crop" << crop_spec
95
-
96
- when :quality
97
- magick_quality = translate_quality_to_imagemagick(operation[:quality])
98
- command_parts << "-quality" << magick_quality.to_s
99
-
100
- when :format
101
- # Assume validated input from tags
102
- # Format is handled by output filename extension
103
- # Quality is set separately if needed
104
- unless @operations.any? { |op| op[:type] == :quality }
105
- default_quality = @config&.quality || raise("No quality configured")
106
- command_parts << "-quality" << default_quality.to_s
107
- end
108
-
109
- when :watermark
110
- watermark_path = operation[:watermark_path]
111
- position = operation[:options][:position]
112
-
113
- # Translate compass directions to ImageMagick gravity format
114
- gravity = translate_position(position)
115
-
116
- # Add watermark as composite operation
117
- command_parts << watermark_path.shellescape
118
- command_parts << "-gravity" << gravity
119
- command_parts << "-composite"
120
-
121
- when :alpha_opacity
122
- opacity = operation[:opacity]
123
- alpha_value = (opacity * 100).round
124
- command_parts << "-alpha" << "set"
125
- command_parts << "-channel" << "A"
126
- command_parts << "-evaluate" << "multiply" << "#{alpha_value}%"
127
- end
128
- end
41
+ @operations.each { |operation| append_imagemagick_operation(operation, command_parts) }
129
42
 
130
43
  # Add output filename
131
44
  command_parts << output_path.shellescape
@@ -146,9 +59,56 @@ module JekyllImgFlow
146
59
 
147
60
  private
148
61
 
149
- def translate_quality_to_imagemagick(quality)
150
- # ImageMagick uses 1-100 directly, no translation needed
151
- quality
62
+ def append_imagemagick_operation(operation, command_parts)
63
+ case operation[:type]
64
+ when :resize
65
+ command_parts << "-resize" << if operation[:height]
66
+ "#{operation[:width]}x#{operation[:height]}!"
67
+ else
68
+ operation[:width].to_s
69
+ end
70
+ when :crop
71
+ append_imagemagick_crop(operation, command_parts)
72
+ when :quality
73
+ command_parts << "-quality" << operation[:quality].to_s
74
+ when :format
75
+ # Format is handled by output filename extension.
76
+ # Quality is set separately if needed.
77
+ unless op?(:quality)
78
+ default_quality = @config&.quality || raise("No quality configured")
79
+ command_parts << "-quality" << default_quality.to_s
80
+ end
81
+ when :watermark
82
+ append_imagemagick_watermark(operation, command_parts)
83
+ when :alpha_opacity
84
+ alpha_value = (operation[:opacity] * 100).round
85
+ command_parts << "-alpha" << "set"
86
+ command_parts << "-channel" << "A"
87
+ command_parts << "-evaluate" << "multiply" << "#{alpha_value}%"
88
+ end
89
+ end
90
+
91
+ def append_imagemagick_crop(operation, command_parts)
92
+ geo = crop_geometry(operation)
93
+
94
+ if geo[:smartcrop]
95
+ # ImageMagick doesn't support smartcrop, but we handle the keep parameter
96
+ # Use center gravity as a reasonable fallback for smartcrop requests
97
+ command_parts << "-gravity" << "center"
98
+ end
99
+
100
+ crop_spec = "#{geo[:width]}x#{geo[:height]}+#{geo[:x]}+#{geo[:y]}"
101
+ command_parts << "-crop" << crop_spec
102
+ end
103
+
104
+ def append_imagemagick_watermark(operation, command_parts)
105
+ parts = watermark_parts(operation)
106
+ gravity = translate_position(parts[:position])
107
+
108
+ # Add watermark as composite operation
109
+ command_parts << parts[:watermark_path].shellescape
110
+ command_parts << "-gravity" << gravity
111
+ command_parts << "-composite"
152
112
  end
153
113
 
154
114
  # Choose a rasterization density (DPI) for SVG input.
@@ -2,162 +2,89 @@
2
2
 
3
3
  require "uri"
4
4
  require "net/http"
5
- require "tempfile"
6
5
  require_relative "base_provider"
7
6
 
8
7
  module JekyllImgFlow
9
8
  module Providers
10
9
  # Imgproxy provider implementation using the standardized tag interface
11
- class Imgproxy < BaseProvider
10
+ class Imgproxy < HttpBase
12
11
  TIMEOUT = 10 # seconds
13
12
 
14
- def available?
15
- # Check if imgproxy service is running
16
- return false unless @config.respond_to?(:imgproxy_url) && @config.imgproxy_url
17
-
18
- check_http_service(@config.imgproxy_url)
13
+ def service_url
14
+ @config.respond_to?(:imgproxy_url) ? @config.imgproxy_url : nil
19
15
  end
20
16
 
21
- def execute(input_path, output_path)
22
- return if @operations.empty?
23
-
24
- # Build single Imgproxy URL with all operations combined
25
- url = build_combined_imgproxy_url(input_path)
26
-
27
- # Fetch the result in one request
28
- fetch_and_save(url, output_path)
29
- output_path
30
- ensure
31
- reset_operations
17
+ def build_combined_imgproxy_url(input_path)
18
+ build_combined_url(input_path)
32
19
  end
33
20
 
34
- def build_combined_imgproxy_url(input_path)
35
- raise "imgproxy_url not set" unless @config.imgproxy_url
21
+ def build_combined_url(input_path)
22
+ raise "imgproxy_url not set" unless service_url
36
23
 
37
24
  encoded_url = encode_file_url(input_path)
38
25
  operations = []
39
26
 
40
- # Add all operations to single URL path
41
- @operations.each do |operation|
42
- case operation[:type]
43
- when :resize
44
- # Tags now provide complete calculated values
45
- operations << if operation[:height]
46
- "rs:fill:#{operation[:width]}:#{operation[:height]}:1"
47
- else
48
- "rs:fit:#{operation[:width]}::1"
49
- end
50
-
51
- when :crop
52
- opts = operation[:options]
53
- params = operation[:params] || {}
54
-
55
- # Check for keep parameter (smartcrop support)
56
- keep = opts[:keep] || params[:keep] || params[:position]
57
-
58
- if operation[:ratio] && keep && SMARTCROP_POSITIONS.include?(keep.to_s)
59
- # Use smartcrop with gravity:sm (libvips smart crop)
60
- crop_width = opts[:calculated_width]
61
- crop_height = opts[:calculated_height]
62
-
63
- operations << "g:sm"
64
- else
65
- # Use basic cropping with explicit coordinates
66
- if operation[:ratio]
67
- crop_x = opts[:calculated_x]
68
- crop_y = opts[:calculated_y]
69
- crop_width = opts[:calculated_width]
70
- crop_height = opts[:calculated_height]
71
- else
72
- crop_x = opts[:x]
73
- crop_y = opts[:y]
74
- crop_width = opts[:width]
75
- crop_height = opts[:height]
76
- end
77
- # imgproxy crop format: c:width:height:gravity
78
- # Use nowe (north-west) gravity with x/y offsets for pixel-precise cropping
79
- operations << "g:nowe:#{crop_x}:#{crop_y}"
80
- end
81
- operations << "c:#{crop_width}:#{crop_height}"
82
-
83
- when :quality
84
- imgproxy_quality = translate_quality_to_imgproxy(operation[:quality])
85
- operations << "q:#{imgproxy_quality}"
86
-
87
- when :format
88
- # Assume validated input from tags
89
- operations << "f:#{operation[:format]}"
90
-
91
- when :watermark
92
- watermark_path = operation[:watermark_path]
93
- position = operation[:options][:position]
94
- operation[:options][:opacity]
95
-
96
- # Imgproxy watermark parameters
97
- encoded_watermark = encode_file_url(watermark_path)
98
- position_param = translate_imgproxy_position(position)
99
- operations << "wm:#{encoded_watermark}:#{position_param}:10:10"
100
-
101
- when :alpha_opacity
102
- opacity = operation[:opacity]
103
- operations << "a:#{(opacity * 255).round}"
104
- end
105
- end
27
+ @operations.each { |operation| append_imgproxy_operation(operation, operations) }
106
28
 
107
- # Preserve format when no explicit format operation is requested.
108
- # Without this, chained HTTP requests (e.g. quality-only steps after a
109
- # prior format conversion) would lose the previously converted format.
110
- unless @operations.any? { |op| op[:type] == :format }
111
- input_ext = File.extname(input_path).delete(".").downcase
112
- operations << "f:#{input_ext}" unless input_ext.empty?
113
- end
114
-
115
- # Build combined URL: base/insecure/operations/operations/.../plain/encoded_url
29
+ preserve_input_format(input_path, operations)
116
30
  operations_path = operations.join("/")
117
- "#{@config.imgproxy_url}/insecure/#{operations_path}/plain/#{encoded_url}"
31
+ "#{service_url}/insecure/#{operations_path}/plain/#{encoded_url}"
118
32
  end
119
33
 
120
34
  private
121
35
 
122
- def translate_imgproxy_position(position)
123
- # Translate compass directions to Imgproxy position format
124
- case position
125
- when "northwest" then "tl"
126
- when "northeast" then "tr"
127
- when "southwest" then "bl"
128
- when "southeast" then "br"
129
- when "center" then "c"
130
- else position
36
+ def append_imgproxy_operation(operation, operations)
37
+ case operation[:type]
38
+ when :resize
39
+ operations << if operation[:height]
40
+ "rs:fill:#{operation[:width]}:#{operation[:height]}:1"
41
+ else
42
+ "rs:fit:#{operation[:width]}::1"
43
+ end
44
+ when :crop
45
+ append_imgproxy_crop(operation, operations)
46
+ when :quality
47
+ operations << "q:#{operation[:quality]}"
48
+ when :format
49
+ operations << "f:#{operation[:format]}"
50
+ when :watermark
51
+ append_imgproxy_watermark(operation, operations)
52
+ when :alpha_opacity
53
+ operations << "a:#{alpha_byte_value(operation[:opacity])}"
131
54
  end
132
55
  end
133
56
 
134
- def fetch_and_save(url, output_path)
135
- response = fetch_with_timeout(url)
136
- raise "Imgproxy request failed" if response.empty?
137
-
138
- FileUtils.mkdir_p(File.dirname(output_path))
139
- File.write(output_path, response)
57
+ def append_imgproxy_crop(operation, operations)
58
+ geo = crop_geometry(operation)
59
+
60
+ operations << if geo[:smartcrop]
61
+ # Use smartcrop with gravity:sm (libvips smart crop)
62
+ "g:sm"
63
+ else
64
+ # imgproxy crop format: c:width:height:gravity
65
+ # Use nowe (north-west) gravity with x/y offsets for pixel-precise cropping
66
+ "g:nowe:#{geo[:x]}:#{geo[:y]}"
67
+ end
68
+ operations << "c:#{geo[:width]}:#{geo[:height]}"
140
69
  end
141
70
 
142
- def fetch_with_timeout(url)
143
- uri = URI(url)
144
- Net::HTTP.start(uri.host, uri.port,
145
- use_ssl: uri.scheme == "https",
146
- open_timeout: TIMEOUT,
147
- read_timeout: TIMEOUT) do |http|
148
- request = Net::HTTP::Get.new(uri)
149
- response = http.request(request)
71
+ def append_imgproxy_watermark(operation, operations)
72
+ parts = watermark_parts(operation)
73
+ encoded_watermark = encode_file_url(parts[:watermark_path])
74
+ position_param = compass_to_short(parts[:position])
75
+ operations << "wm:#{encoded_watermark}:#{position_param}:10:10"
76
+ end
150
77
 
151
- raise "HTTP #{response.code}: #{response.message}" unless response.is_a?(Net::HTTPSuccess)
78
+ def preserve_input_format(input_path, operations)
79
+ return if op?(:format)
152
80
 
153
- response.body
154
- end
155
- rescue StandardError => e
156
- raise "Imgproxy request failed: #{e.message}"
81
+ ext = input_format_ext(input_path)
82
+ operations << "f:#{ext}" unless ext.empty?
157
83
  end
158
84
 
159
- def translate_quality_to_imgproxy(quality)
160
- quality
85
+ # Thin wrapper kept for test compatibility.
86
+ def translate_imgproxy_position(position)
87
+ compass_to_short(position)
161
88
  end
162
89
  end
163
90
  end