-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #146 from robotmay/auto-rotate
Improvements to image processing.
- Loading branch information
Showing
8 changed files
with
233 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,84 @@ | ||
class PhotoExpansionWorker | ||
class MetadataRecordMissing < StandardError; end | ||
|
||
include Sidekiq::Worker | ||
include Timeout | ||
sidekiq_options queue: :photos | ||
|
||
def perform(photograph_id) | ||
timeout(120) do | ||
photo = Photograph.find(photograph_id) | ||
photo.standard_image = photo.image.thumb("3000x3000>") | ||
photo.save! | ||
|
||
if photo.standard_image.width > photo.standard_image.height | ||
ImageWorker.perform_async(photo.id, :standard_image, :homepage_image, "2000x", "-quality 80") | ||
ImageWorker.perform_async(photo.id, :standard_image, :large_image, "1500x", "-quality 80") | ||
elsif photo.standard_image.height > photo.standard_image.width | ||
ImageWorker.perform_async(photo.id, :standard_image, :homepage_image, "2000x1400#", "-quality 80") | ||
ImageWorker.perform_async(photo.id, :standard_image, :large_image, "x1000", "-quality 80") | ||
else | ||
ImageWorker.perform_async(photo.id, :standard_image, :homepage_image, "2000x1400#", "-quality 80") | ||
ImageWorker.perform_async(photo.id, :standard_image, :large_image, "1500x1500>", "-quality 80") | ||
timeout(300) do | ||
@photo = Photograph.find(photograph_id) | ||
|
||
# Extract the metadata and save it | ||
extract_metadata | ||
|
||
# Generate all the other image sizes | ||
generate_images | ||
|
||
@photo.save! | ||
end | ||
end | ||
|
||
private | ||
|
||
def extract_metadata | ||
Benchmark.measure "Extracting metadata" do | ||
metadata = @photo.metadata | ||
|
||
# It's possible that Sidekiq could hit this before Postgres catches up | ||
raise MetadataRecordMissing if metadata.nil? | ||
|
||
# Extract the metadata first to allow us to work off that data | ||
metadata.extract_from_photograph | ||
metadata.save! | ||
end | ||
end | ||
|
||
def generate_standard_image | ||
Benchmark.measure "Generating standard image" do | ||
# Create a standard image for generating the smaller sizes | ||
standard_image = @photo.image.thumb("3000x3000>") | ||
|
||
# Rotate the image if the metadata says so | ||
if @photo.metadata.rotate? | ||
standard_image = standard_image.process(:rotate, @photo.metadata.rotate_by) | ||
end | ||
|
||
ImageWorker.perform_async(photo.id, :standard_image, :thumbnail_image, "500x500>", "-quality 70") | ||
# Set the standard image and save | ||
@photo.standard_image = standard_image | ||
@photo.save! | ||
end | ||
end | ||
|
||
def generate_images | ||
# Generate a sensible base size first | ||
generate_standard_image | ||
|
||
# Generate other sizes based on dimensions | ||
case | ||
when @photo.landscape? | ||
ImageWorker.perform_async(@photo.id, :standard_image, :homepage_image, "2000x", "-quality 80") | ||
ImageWorker.perform_async(@photo.id, :standard_image, :large_image, "1500x", "-quality 80") | ||
when @photo.portrait? | ||
ImageWorker.perform_async(@photo.id, :standard_image, :homepage_image, "2000x1400#", "-quality 80") | ||
ImageWorker.perform_async(@photo.id, :standard_image, :large_image, "x1000", "-quality 80") | ||
else | ||
ImageWorker.perform_async(@photo.id, :standard_image, :homepage_image, "2000x1400#", "-quality 80") | ||
ImageWorker.perform_async(@photo.id, :standard_image, :large_image, "1500x1500>", "-quality 80") | ||
end | ||
|
||
ImageWorker.perform_async(@photo.id, :standard_image, :thumbnail_image, "500x500>", "-quality 70") | ||
end | ||
|
||
def generate_image(source, target, size, encode_opts) | ||
Benchmark.measure "Generating #{target} image from #{source}" do | ||
source = @photo.send(source) | ||
if source.present? | ||
image = source.thumb(size).encode(:jpg, encode_opts) | ||
@photo.send("#{target}=", image) | ||
else | ||
raise "Source doesn't exist yet" | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
module Dragonfly | ||
module DataStorage | ||
class CachingS3DataStore < S3DataStore | ||
def retrieve(uid) | ||
cache.fetch(cache_key_for(uid), expires_in: 5.minutes) do | ||
super(uid) | ||
end | ||
end | ||
|
||
private | ||
|
||
def cache | ||
@cache ||= Dalli::Client.new | ||
end | ||
|
||
def cache_key_for(uid) | ||
"dragonfly-#{uid}" | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
require 'spec_helper' | ||
|
||
describe PhotoExpansionWorker do | ||
subject { PhotoExpansionWorker.new } | ||
let(:photograph) { Photograph.make(metadata: metadata) } | ||
let(:metadata) { Metadata.make } | ||
|
||
before { Photograph.stub(:find) { photograph } } | ||
before { Metadata.stub(:find) { metadata } } | ||
before { photograph.stub(:save!) { true } } | ||
|
||
describe "metadata" do | ||
before { metadata.stub(:extract_from_photograph) { true } } | ||
before { metadata.stub(:save!) { true } } | ||
after { subject.perform(1) } | ||
|
||
it "calls extract_metadata" do | ||
subject.should_receive(:extract_metadata) | ||
end | ||
|
||
it "calls extract_from_photograph on the metadata" do | ||
metadata.should_receive(:extract_from_photograph) | ||
end | ||
|
||
it "saves the metadata" do | ||
metadata.should_receive(:save!) | ||
end | ||
end | ||
|
||
describe "image generation" do | ||
before { subject.instance_variable_set('@photo', photograph) } | ||
|
||
describe "#generate_standard_image" do | ||
let(:image) { double('image').as_null_object } | ||
before { photograph.stub_chain(:image, :thumb) { image } } | ||
after { subject.send(:generate_standard_image) } | ||
|
||
context "metadata.rotate? is true" do | ||
before { metadata.stub(:rotate?) { true } } | ||
before { metadata.stub(:rotate_by) { 90 } } | ||
|
||
it "rotates the image" do | ||
image.should_receive(:process).with(:rotate, 90) | ||
end | ||
end | ||
|
||
context "metadata.rotate? is false" do | ||
before { metadata.stub(:rotate?) { false } } | ||
|
||
it "doesn't rotate the image" do | ||
image.should_not_receive(:process) | ||
end | ||
end | ||
|
||
it "sets the standard image" do | ||
photograph.should_receive(:standard_image=) | ||
end | ||
end | ||
|
||
describe "#generate_images" do | ||
before { subject.stub(:generate_image) { true } } | ||
after { subject.send(:generate_images) } | ||
|
||
it "generates the standard image" do | ||
subject.should_receive(:generate_standard_image) | ||
end | ||
|
||
it "generates 3 smaller images" do | ||
|
||
end | ||
end | ||
end | ||
end |