mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-24 08:09:13 -05:00
Merge pull request #709 from ZogStriP/automatically-resize-images
automatically resize images
This commit is contained in:
commit
31c5ad2328
8 changed files with 131 additions and 64 deletions
3
Gemfile
3
Gemfile
|
@ -26,6 +26,9 @@ gem 'hiredis'
|
|||
# note: for image_optim to correctly work you need
|
||||
# sudo apt-get install -y advancecomp gifsicle jpegoptim libjpeg-progs optipng pngcrush
|
||||
gem 'image_optim'
|
||||
# note: for image_sorcery to correctly work you need
|
||||
# sudo apt-get install -y imagemagick
|
||||
gem 'image_sorcery'
|
||||
gem 'jquery-rails'
|
||||
gem 'minitest'
|
||||
gem 'multi_json'
|
||||
|
|
|
@ -219,6 +219,7 @@ GEM
|
|||
in_threads (~> 1.1.1)
|
||||
progress (~> 2.4.0)
|
||||
image_size (1.1.1)
|
||||
image_sorcery (1.1.0)
|
||||
in_threads (1.1.1)
|
||||
ipaddress (0.8.0)
|
||||
jasminerice (0.0.10)
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
# example, inserting the onebox content, or image sizes.
|
||||
|
||||
require_dependency 'oneboxer'
|
||||
require_dependency 'image_optimizer'
|
||||
|
||||
class CookedPostProcessor
|
||||
require 'open-uri'
|
||||
|
||||
def initialize(post, opts={})
|
||||
@dirty = false
|
||||
|
@ -34,58 +34,59 @@ class CookedPostProcessor
|
|||
images = @doc.search("img")
|
||||
return unless images.present?
|
||||
|
||||
images.each do |img|
|
||||
src = img['src']
|
||||
src = Discourse.base_url_no_prefix + src if src[0] == "/"
|
||||
|
||||
if src.present?
|
||||
|
||||
if img['width'].blank? || img['height'].blank?
|
||||
w, h = get_size_from_image_sizes(src, @opts[:image_sizes]) || image_dimensions(src)
|
||||
|
||||
if w && h
|
||||
img['width'] = w.to_s
|
||||
img['height'] = h.to_s
|
||||
@dirty = true
|
||||
end
|
||||
end
|
||||
|
||||
if src != img['src']
|
||||
img['src'] = src
|
||||
@dirty = true
|
||||
end
|
||||
|
||||
convert_to_link!(img)
|
||||
img['src'] = optimize_image(img)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# Extract the first image from the first post and use it as the 'topic image'
|
||||
if @post.post_number == 1
|
||||
img = images.first
|
||||
@post.topic.update_column :image_url, img['src'] if img['src'].present?
|
||||
end
|
||||
|
||||
images.each do |img|
|
||||
src = img['src']
|
||||
src = Discourse.base_url_no_prefix + src if src[0] == "/"
|
||||
|
||||
if src.present? && (img['width'].blank? || img['height'].blank?)
|
||||
|
||||
w,h =
|
||||
get_size_from_image_sizes(src, @opts[:image_sizes]) ||
|
||||
image_dimensions(src)
|
||||
|
||||
if w && h
|
||||
img['width'] = w.to_s
|
||||
img['height'] = h.to_s
|
||||
@dirty = true
|
||||
end
|
||||
end
|
||||
|
||||
if src.present?
|
||||
if src != img['src']
|
||||
img['src'] = src
|
||||
@dirty = true
|
||||
end
|
||||
convert_to_link!(img)
|
||||
img.set_attribute('src', optimize_image(src))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def optimize_image(src)
|
||||
# uri = get_image_uri(src)
|
||||
# uri.open(read_timeout: 20) do |f|
|
||||
#
|
||||
# end
|
||||
def optimize_image(img)
|
||||
src = img["src"]
|
||||
|
||||
src
|
||||
# supports only local uploads
|
||||
return src if SiteSetting.enable_imgur? || SiteSetting.enable_s3_uploads?
|
||||
|
||||
width, height = img["width"].to_i, img["height"].to_i
|
||||
|
||||
ImageOptimizer.new(src).optimized_image_url(width, height)
|
||||
end
|
||||
|
||||
def convert_to_link!(img)
|
||||
src = img["src"]
|
||||
width = img["width"].to_i
|
||||
height = img["height"].to_i
|
||||
width, height = img["width"].to_i, img["height"].to_i
|
||||
|
||||
return unless src.present? && width > SiteSetting.auto_link_images_wider_than
|
||||
|
||||
original_width, original_height = get_size(src)
|
||||
original_width, original_height = get_size(src)
|
||||
|
||||
return unless original_width.to_i > width && original_height.to_i > height
|
||||
|
||||
|
|
|
@ -1,35 +1,22 @@
|
|||
#
|
||||
# This class is used to download and optimize images.
|
||||
#
|
||||
# I have not had a chance to implement me, and will not for about 3 weeks.
|
||||
# If you are looking for a small project this simple API would be a good stint.
|
||||
#
|
||||
# Implement the following methods. With tests, the tests are a HUGE PITA cause
|
||||
# network, disk and external dependencies are involved.
|
||||
|
||||
require 'image_sorcery'
|
||||
require 'digest/sha1'
|
||||
require 'open-uri'
|
||||
|
||||
class ImageOptimizer
|
||||
attr_accessor :url, :root_dir
|
||||
attr_accessor :url
|
||||
|
||||
# url is a url of an image ex:
|
||||
# 'http://site.com/image.png'
|
||||
# '/uploads/site/image.png'
|
||||
#
|
||||
# root_dir is the path where we
|
||||
# store optimized images
|
||||
def initialize(opts = {})
|
||||
@url = opts[:url]
|
||||
@root_dir = opts[:root_dir]
|
||||
end
|
||||
|
||||
# attempt to refresh the original image, if refreshed
|
||||
# remove old downsized copies
|
||||
def refresh_local!
|
||||
end
|
||||
|
||||
# clear all local copies of the images
|
||||
def clear_local!
|
||||
end
|
||||
|
||||
# yield a list of relative paths to local images cached
|
||||
def each_local
|
||||
def initialize(url)
|
||||
@url = url
|
||||
# make sure directories exists
|
||||
FileUtils.mkdir_p downloads_dir
|
||||
FileUtils.mkdir_p optimized_dir
|
||||
end
|
||||
|
||||
# return the path of an optimized image,
|
||||
|
@ -42,7 +29,68 @@ class ImageOptimizer
|
|||
# at the basic level it runs through image_optim https://github.com/toy/image_optim
|
||||
# it also has a failsafe that converts jpg to png or the opposite. if jpg size is 1.5*
|
||||
# as efficient as png it flips formats.
|
||||
def optimized_image_path(width=nil, height=nil)
|
||||
def optimized_image_url (width = nil, height = nil)
|
||||
begin
|
||||
unless has_been_uploaded?
|
||||
return @url unless SiteSetting.crawl_images?
|
||||
# download the file if it hasn't been cached yet
|
||||
download! unless File.exists?(cached_path)
|
||||
end
|
||||
|
||||
# resize the image using Image Magick
|
||||
result = ImageSorcery.new(cached_path).convert(optimized_path, resize: "#{width}x#{height}")
|
||||
return optimized_url if result
|
||||
@url
|
||||
rescue
|
||||
@url
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def public_dir
|
||||
@public_dir ||= "#{Rails.root}/public"
|
||||
end
|
||||
|
||||
def downloads_dir
|
||||
@downloads_dir ||= "#{public_dir}/downloads/#{RailsMultisite::ConnectionManagement.current_db}"
|
||||
end
|
||||
|
||||
def optimized_dir
|
||||
@optimized_dir ||= "#{public_dir}/images/#{RailsMultisite::ConnectionManagement.current_db}"
|
||||
end
|
||||
|
||||
def has_been_uploaded?
|
||||
@url.start_with?(Discourse.base_url_no_prefix)
|
||||
end
|
||||
|
||||
def cached_path
|
||||
@cached_path ||= if has_been_uploaded?
|
||||
"#{public_dir}#{@url[Discourse.base_url_no_prefix.length..-1]}"
|
||||
else
|
||||
"#{downloads_dir}/#{file_name(@url)}"
|
||||
end
|
||||
end
|
||||
|
||||
def optimized_path
|
||||
@optimized_path ||= "#{optimized_dir}/#{file_name(cached_path)}"
|
||||
end
|
||||
|
||||
def file_name (uri)
|
||||
image_info = FastImage.new(uri)
|
||||
name = Digest::SHA1.hexdigest(uri)[0,16]
|
||||
name << ".#{image_info.type}"
|
||||
name
|
||||
end
|
||||
|
||||
def download!
|
||||
File.open(cached_path, "wb") do |f|
|
||||
f.write open(@url, "rb", read_timeout: 20).read
|
||||
end
|
||||
end
|
||||
|
||||
def optimized_url
|
||||
@optimized_url ||= Discourse::base_uri + "/images/#{RailsMultisite::ConnectionManagement.current_db}/#{file_name(cached_path)}"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -41,6 +41,7 @@ describe CookedPostProcessor do
|
|||
before do
|
||||
@topic = Fabricate(:topic)
|
||||
@post = Fabricate.build(:post_with_image_url, topic: @topic, user: @topic.user)
|
||||
ImageSorcery.any_instance.stubs(:convert).returns(false)
|
||||
@cpp = CookedPostProcessor.new(@post, image_sizes: {'http://www.forumwarz.com/images/header/logo.png' => {'width' => 111, 'height' => 222}})
|
||||
@cpp.expects(:get_size).returns([111,222])
|
||||
end
|
||||
|
@ -63,6 +64,7 @@ describe CookedPostProcessor do
|
|||
|
||||
before do
|
||||
FastImage.stubs(:size).returns([123, 456])
|
||||
ImageSorcery.any_instance.stubs(:convert).returns(false)
|
||||
CookedPostProcessor.any_instance.expects(:image_dimensions).returns([123, 456])
|
||||
creator = PostCreator.new(user, raw: Fabricate.build(:post_with_images).raw, topic_id: topic.id)
|
||||
@post = creator.create
|
||||
|
@ -70,7 +72,7 @@ describe CookedPostProcessor do
|
|||
|
||||
it "adds a topic image if there's one in the post" do
|
||||
@post.topic.reload
|
||||
@post.topic.image_url.should == "/path/to/img.jpg"
|
||||
@post.topic.image_url.should == "http://test.localhost/path/to/img.jpg"
|
||||
end
|
||||
|
||||
it "adds the height and width to images that don't have them" do
|
||||
|
|
|
@ -3,6 +3,10 @@ require_dependency 'post_destroyer'
|
|||
|
||||
describe PostAction do
|
||||
|
||||
before do
|
||||
ImageSorcery.any_instance.stubs(:convert).returns(false)
|
||||
end
|
||||
|
||||
it { should belong_to :user }
|
||||
it { should belong_to :post }
|
||||
it { should belong_to :post_action_type }
|
||||
|
|
|
@ -3,6 +3,10 @@ require_dependency 'post_destroyer'
|
|||
|
||||
describe PostAlertObserver do
|
||||
|
||||
before do
|
||||
ImageSorcery.any_instance.stubs(:convert).returns(false)
|
||||
end
|
||||
|
||||
let!(:evil_trout) { Fabricate(:evil_trout) }
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
|
|
|
@ -3,6 +3,10 @@ require_dependency 'post_destroyer'
|
|||
|
||||
describe Post do
|
||||
|
||||
before do
|
||||
ImageSorcery.any_instance.stubs(:convert).returns(false)
|
||||
end
|
||||
|
||||
it { should belong_to :user }
|
||||
it { should belong_to :topic }
|
||||
it { should validate_presence_of :raw }
|
||||
|
|
Loading…
Reference in a new issue