From cc9e0ec80a86ddc539dd2b44d2c8c8a96ab9c8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 17 Jun 2013 01:00:25 +0200 Subject: [PATCH] create thumbnails when needed --- app/models/optimized_image.rb | 32 +++++++++++++++-- app/models/upload.rb | 36 +++++++++++++++++++ .../20130616082327_create_optimized_images.rb | 1 + lib/cooked_post_processor.rb | 27 ++++++++++---- 4 files changed, 87 insertions(+), 9 deletions(-) diff --git a/app/models/optimized_image.rb b/app/models/optimized_image.rb index 7566c09ffa4..05be3317b5f 100644 --- a/app/models/optimized_image.rb +++ b/app/models/optimized_image.rb @@ -1,7 +1,35 @@ class OptimizedImage < ActiveRecord::Base belongs_to :upload - def filename - "#{sha[0..2]}/#{sha[3..5]}/#{sha[6..16]}_#{width}x#{height}#{ext}" + def self.create_for(upload_id, path) + image_info = FastImage.new(path) + OptimizedImage.new({ + upload_id: upload_id, + sha: Digest::SHA1.file(path).hexdigest, + ext: File.extname(path), + width: image_info.size[0], + height: image_info.size[1] + }) end + + def url + "#{Upload.base_url}/#{optimized_path}/#{filename}" + end + + def path + "#{path_root}/#{optimized_path}/#{filename}" + end + + def path_root + @path_root ||= "#{Rails.root}/public" + end + + def optimized_path + "uploads/#{RailsMultisite::ConnectionManagement.current_db}/_optimized/#{sha[0..2]}/#{sha[3..5]}" + end + + def filename + "#{sha[6..16]}_#{width}x#{height}#{ext}" + end + end diff --git a/app/models/upload.rb b/app/models/upload.rb index 953749767a4..fcc78095de3 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -2,6 +2,8 @@ require 'digest/sha1' require 'image_sizer' require 's3' require 'local_store' +require 'tempfile' +require 'pathname' class Upload < ActiveRecord::Base belongs_to :user @@ -14,6 +16,40 @@ class Upload < ActiveRecord::Base validates_presence_of :filesize validates_presence_of :original_filename + def thumbnail + @thumbnail ||= optimized_images.where(width: width, height: height).first + end + + def thumbnail_url + thumbnail.url if has_thumbnail? + end + + def has_thumbnail? + thumbnail.present? + end + + def create_thumbnail! + return unless SiteSetting.create_thumbnails? + return unless width > SiteSetting.auto_link_images_wider_than + return if has_thumbnail? + @image_sorcery_loaded ||= require "image_sorcery" + original_path = "#{Rails.root}/public#{url}" + temp_file = Tempfile.new(["discourse", File.extname(original_path)]) + if ImageSorcery.new(original_path).convert(temp_file.path, resize: "#{width}x#{height}") + thumbnail = OptimizedImage.create_for(id, temp_file.path) + optimized_images << thumbnail + # make sure the directory exists + FileUtils.mkdir_p Pathname.new(thumbnail.path).dirname + # move the temp file to the right location + File.open(thumbnail.path, "wb") do |f| + f.write temp_file.read + end + end + # close && remove temp file if it exists + temp_file.close + temp_file.unlink + end + def self.create_for(user_id, file) # compute the sha sha = Digest::SHA1.file(file.tempfile).hexdigest diff --git a/db/migrate/20130616082327_create_optimized_images.rb b/db/migrate/20130616082327_create_optimized_images.rb index 24d73e26076..a927548a00c 100644 --- a/db/migrate/20130616082327_create_optimized_images.rb +++ b/db/migrate/20130616082327_create_optimized_images.rb @@ -9,6 +9,7 @@ class CreateOptimizedImages < ActiveRecord::Migration end add_index :optimized_images, :upload_id + add_index :optimized_images, [:upload_id, :width, :height], unique: true end def down diff --git a/lib/cooked_post_processor.rb b/lib/cooked_post_processor.rb index f46c49d837f..192502d4ae0 100644 --- a/lib/cooked_post_processor.rb +++ b/lib/cooked_post_processor.rb @@ -2,7 +2,6 @@ # example, inserting the onebox content, or image sizes. require_dependency 'oneboxer' -require_dependency 'image_optimizer' class CookedPostProcessor @@ -34,10 +33,18 @@ class CookedPostProcessor if src.present? # make sure the img has both width and height attributes update_dimensions!(img) - # optimize image - img['src'] = optimize_image(img) - # lightbox treatment - convert_to_link!(img) + # retrieve the associated upload, if any + upload = get_upload_from_url(img['src']) + if upload.present? + # create a thumbnail + upload.create_thumbnail! + # optimize image + img['src'] = optimize_image(img) + # lightbox treatment + convert_to_link!(img, upload.thumbnail_url) + else + convert_to_link!(img) + end # mark the post as dirty whenever the src has changed @dirty |= src != img['src'] end @@ -74,14 +81,19 @@ class CookedPostProcessor end end + def get_upload_from_url(url) + if Upload.has_been_uploaded?(url) && m = Upload.uploaded_regex.match(url) + Upload.where("id = ?", m[:upload_id]).first + end + end + def optimize_image(img) return img["src"] # 1) optimize using image_optim # 2) .png vs. .jpg - # TODO: needs some <3 end - def convert_to_link!(img) + def convert_to_link!(img, thumbnail=nil) src = img["src"] width, height = img["width"].to_i, img["height"].to_i @@ -99,6 +111,7 @@ class CookedPostProcessor end # not a hyperlink so we can apply + img['src'] = thumbnail if thumbnail a = Nokogiri::XML::Node.new "a", @doc img.add_next_sibling(a) a["href"] = src