From 520413140d4f869cd3baf0495a59e2dc36860c2a Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Sat, 21 Jan 2023 09:39:31 -0600 Subject: [PATCH] switch to webp, use png for screenshot returns and be done with the accursed rmagick forever --- Gemfile | 2 +- Gemfile.lock | 6 ++- models/site.rb | 12 +++--- tests/files/img/screenshot.png | Bin 0 -> 63228 bytes tests/site_file_tests.rb | 2 +- tests/workers/screenshot_worker_tests.rb | 6 +-- workers/screenshot_worker.rb | 35 +++++------------ workers/thumbnail_worker.rb | 46 +++++++++-------------- 8 files changed, 40 insertions(+), 69 deletions(-) create mode 100644 tests/files/img/screenshot.png diff --git a/Gemfile b/Gemfile index 999df40c..3f23fef6 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,6 @@ gem 'bcrypt' gem 'sinatra-flash', require: 'sinatra/flash' gem 'sinatra-xsendfile', require: 'sinatra/xsendfile' gem 'puma', '5.6.5', require: nil -gem 'rmagick', require: nil gem 'sidekiq', '~> 5.2.0' gem 'mail' gem 'net-smtp' @@ -56,6 +55,7 @@ gem 'maxmind-db' gem 'json', '>= 2.3.0' gem 'nokogiri' gem 'rss' +gem 'webp-ffi' gem 'rszr' group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index d8710dd1..10fdb0d0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -225,7 +225,6 @@ GEM netrc (~> 0.8) rexml (3.2.5) rinku (2.0.6) - rmagick (5.0.0) rss (0.2.9) rexml rszr (1.3.0) @@ -295,6 +294,9 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) + webp-ffi (0.3.1) + ffi (>= 1.9.0) + ffi-compiler (>= 0.1.2) webrick (1.7.0) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) @@ -361,7 +363,6 @@ DEPENDENCIES redis-namespace rest-client rinku - rmagick rss rszr sanitize @@ -382,6 +383,7 @@ DEPENDENCIES tilt timecop webmock + webp-ffi will_paginate xmlrpc zipruby diff --git a/models/site.rb b/models/site.rb index ae408fcc..855dc622 100644 --- a/models/site.rb +++ b/models/site.rb @@ -1554,7 +1554,7 @@ class Site < Sequel::Model end def screenshot_path(path, resolution) - File.join base_screenshots_path, "#{path}.#{resolution}.jpg" + File.join base_screenshots_path, "#{path}.#{resolution}.webp" end def base_screenshots_path(name=username) @@ -1568,14 +1568,14 @@ class Site < Sequel::Model end def screenshot_exists?(path, resolution) - File.exist? File.join(base_screenshots_path, "#{path}.#{resolution}.jpg") + File.exist? File.join(base_screenshots_path, "#{path}.#{resolution}.webp") end def screenshot_url(path, resolution) path[0] = '' if path[0] == '/' out = '' out = 'https://neocities.org' if ENV['RACK_ENV'] == 'development' - out+"#{base_screenshots_url}/#{path}.#{resolution}.jpg" + out+"#{base_screenshots_url}/#{path}.#{resolution}.webp" end def base_thumbnails_path(name=username) @@ -1584,8 +1584,7 @@ class Site < Sequel::Model end def thumbnail_path(path, resolution) - ext = File.extname(path).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' - File.join base_thumbnails_path, "#{path}.#{resolution}.#{ext}" + File.join base_thumbnails_path, "#{path}.#{resolution}.webp" end def thumbnail_exists?(path, resolution) @@ -1598,8 +1597,7 @@ class Site < Sequel::Model def thumbnail_url(path, resolution) path[0] = '' if path[0] == '/' - ext = File.extname(path).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' - "#{THUMBNAILS_URL_ROOT}/#{sharding_dir}/#{values[:username]}/#{path}.#{resolution}.#{ext}" + "#{THUMBNAILS_URL_ROOT}/#{sharding_dir}/#{values[:username]}/#{path}.#{resolution}.webp" end def to_rss diff --git a/tests/files/img/screenshot.png b/tests/files/img/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..ba79519a89de0e6e25dc1c2d7be922cb1c954fc1 GIT binary patch literal 63228 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 N?k-+?E58r>`G4|54x#`6 literal 0 HcmV?d00001 diff --git a/tests/site_file_tests.rb b/tests/site_file_tests.rb index a52ab44b..1c9b7547 100644 --- a/tests/site_file_tests.rb +++ b/tests/site_file_tests.rb @@ -446,7 +446,7 @@ describe 'site_files' do Site::THUMBNAIL_RESOLUTIONS.each do |resolution| _(File.exists?(@site.thumbnail_path('derpie/derptest/test.jpg', resolution))).must_equal true _(@site.thumbnail_url('derpie/derptest/test.jpg', resolution)).must_equal( - File.join "#{Site::THUMBNAILS_URL_ROOT}", Site.sharding_dir(@site.username), @site.username, "/derpie/derptest/test.jpg.#{resolution}.jpg" + File.join "#{Site::THUMBNAILS_URL_ROOT}", Site.sharding_dir(@site.username), @site.username, "/derpie/derptest/test.jpg.#{resolution}.webp" ) end end diff --git a/tests/workers/screenshot_worker_tests.rb b/tests/workers/screenshot_worker_tests.rb index b0259070..713cc717 100644 --- a/tests/workers/screenshot_worker_tests.rb +++ b/tests/workers/screenshot_worker_tests.rb @@ -14,14 +14,14 @@ describe ScreenshotWorker do stub_request(:get, "#{base_host}/?url=#{site.uri}/#{path}&wait_time=#{ScreenshotWorker::PAGE_WAIT_TIME}"). with(basic_auth: [uri.user, uri.password]). - to_return(status: 200, headers: {}, body: File.read('tests/files/img/test.jpg')) + to_return(status: 200, headers: {}, body: File.read('tests/files/img/screenshot.png')) ScreenshotWorker.new.perform site.username, path Site::SCREENSHOT_RESOLUTIONS.each do |r| - _(File.exists?(File.join(Site::SCREENSHOTS_ROOT, Site.sharding_dir(site.username), site.username, "#{path}.#{r}.jpg"))).must_equal true + _(File.exists?(File.join(Site::SCREENSHOTS_ROOT, Site.sharding_dir(site.username), site.username, "#{path}.#{r}.webp"))).must_equal true _(site.screenshot_url(path, r)).must_equal( - File.join(Site::SCREENSHOTS_URL_ROOT, Site.sharding_dir(site.username), site.username, "#{path}.#{r}.jpg") + File.join(Site::SCREENSHOTS_URL_ROOT, Site.sharding_dir(site.username), site.username, "#{path}.#{r}.webp") ) end end diff --git a/workers/screenshot_worker.rb b/workers/screenshot_worker.rb index d2433c27..880d3aef 100644 --- a/workers/screenshot_worker.rb +++ b/workers/screenshot_worker.rb @@ -1,4 +1,3 @@ -require 'rmagick' require 'securerandom' require 'open3' @@ -10,7 +9,6 @@ class ScreenshotWorker sidekiq_options queue: :screenshots, retry: 2, backtrace: true def perform(username, path) - site = Site[username: username] return if site.nil? || site.is_deleted @@ -37,14 +35,6 @@ class ScreenshotWorker path = "/#{path}" unless path[0] == '/' path_for_screenshot = path - - if path.match(Site::HTML_REGEX) - path = path.match(/(.+)#{Site::HTML_REGEX}/).captures.first - end - - if path.match(/(.+)index?/) - path = path.match(/(.+)index?/).captures.first - end uri = Addressable::URI.parse $config['screenshot_urls'].sample api_user, api_password = uri.user, uri.password @@ -54,37 +44,30 @@ class ScreenshotWorker ) begin - base_image_tmpfile_path = "/tmp/#{SecureRandom.uuid}.jpg" + base_image_tmpfile_path = "/tmp/#{SecureRandom.uuid}.png" File.write base_image_tmpfile_path, HTTP.basic_auth(user: api_user, pass: api_password).get(uri).to_s - image = Rszr::Image.load base_image_tmpfile_path user_screenshots_path = File.join SCREENSHOTS_PATH, Site.sharding_dir(username), username screenshot_path = File.join user_screenshots_path, File.dirname(path_for_screenshot) - FileUtils.mkdir_p screenshot_path unless Dir.exist?(screenshot_path) Site::SCREENSHOT_RESOLUTIONS.each do |res| width, height = res.split('x').collect {|r| r.to_i} + full_screenshot_path = File.join(user_screenshots_path, "#{path_for_screenshot}.#{res}.webp") + + opts = {quality: 90, resize_w: width, resize_h: height} + if width == height - new_img = image.resize(width, height, crop: :n) - else - new_img = image.resize width, height + opts.merge! crop_x: 160, crop_y: 0, crop_w: 960, crop_h: 960 end - full_screenshot_path = File.join(user_screenshots_path, "#{path_for_screenshot}.#{res}.jpg") - tmpfile_path = "/tmp/#{SecureRandom.uuid}.jpg" - - begin - new_img.save tmpfile_path, quality: 92 - $image_optim.optimize_image! tmpfile_path - File.open(full_screenshot_path, 'wb') {|file| file.write File.read(tmpfile_path)} - ensure - FileUtils.rm tmpfile_path - end + WebP.encode base_image_tmpfile_path, full_screenshot_path, opts end true + rescue => e + raise e ensure FileUtils.rm base_image_tmpfile_path end diff --git a/workers/thumbnail_worker.rb b/workers/thumbnail_worker.rb index 1aa311a1..1436a4c9 100644 --- a/workers/thumbnail_worker.rb +++ b/workers/thumbnail_worker.rb @@ -1,8 +1,6 @@ -require 'rmagick' - class ThumbnailWorker THUMBNAILS_PATH = Site::THUMBNAILS_ROOT - MAXIMUM_IMAGE_SIZE = 2_000_000 # 2MB + MAXIMUM_IMAGE_SIZE = 10_000_000 # 10MB include Sidekiq::Worker sidekiq_options queue: :thumbnails, retry: 3, backtrace: true @@ -14,43 +12,33 @@ class ThumbnailWorker site_file_path = site.files_path(path) return unless File.exist?(site_file_path) - # Large images jam up ImageMagick and eat a ton of memory, so we skip for now. + # Large images can eat a ton of memory, so we skip for now. return if File.size(site_file_path) > MAXIMUM_IMAGE_SIZE - img_list = Magick::ImageList.new - - begin - img_list.from_blob File.read(site_file_path) - rescue Errno::ENOENT => e # Not found, skip - return - rescue Magick::ImageMagickError => e - GC.start full_mark: true, immediate_sweep: true - puts "thumbnail fail: #{site_file_path} #{e.inspect}" - return - end - - img = img_list.first - user_thumbnails_path = site.base_thumbnails_path FileUtils.mkdir_p user_thumbnails_path FileUtils.mkdir_p File.join(user_thumbnails_path, File.dirname(path)) Site::THUMBNAIL_RESOLUTIONS.each do |res| - resimg = img.resize_to_fit(*res.split('x').collect {|r| r.to_i}) + width, height = res.split('x').collect {|r| r.to_i} format = File.extname(path).gsub('.', '') + full_thumbnail_path = File.join(user_thumbnails_path, "#{path}.#{res}.webp") - save_ext = format.match(Site::LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' + image = Rszr::Image.load site_file_path + if image.width > image.height + image.resize! width, :auto + else + image.resize! :auto, height + end - full_thumbnail_path = File.join(user_thumbnails_path, "#{path}.#{res}.#{save_ext}") + begin + tmpfile = "/tmp/#{SecureRandom.uuid}.png" + image.save(tmpfile) - resimg.write(full_thumbnail_path) { |i| - i.quality = 75 - } - resimg.destroy! - #$image_optim.optimize_image! full_thumbnail_path + WebP.encode tmpfile, full_thumbnail_path, quality: 70 + ensure + FileUtils.rm tmpfile + end end - - img.destroy! - GC.start full_mark: true, immediate_sweep: true end end