Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 24 additions & 35 deletions app/services/hyrax/file_set_derivatives_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,49 +67,39 @@ def supported_mime_types
end

def create_pdf_derivatives(filename)
Hydra::Derivatives::PdfDerivatives.create(filename,
outputs: [{
label: :thumbnail,
format: 'jpg',
size: '338x493',
url: derivative_url('thumbnail'),
layer: 0
}])
extract_full_text(filename, derivative_url('extracted_text'))
create_derivatives_for(:pdf, filename, Hydra::Derivatives::PdfDerivatives)
end

def create_office_document_derivatives(filename)
Hydra::Derivatives::DocumentDerivatives.create(filename,
outputs: [{
label: :thumbnail, format: 'jpg',
size: '200x150>',
url: derivative_url('thumbnail'),
layer: 0
}])
extract_full_text(filename, derivative_url('extracted_text'))
create_derivatives_for(:office, filename, Hydra::Derivatives::DocumentDerivatives)
end

def create_audio_derivatives(filename)
Hydra::Derivatives::AudioDerivatives.create(filename,
outputs: [{ label: 'mp3', format: 'mp3', url: derivative_url('mp3'), mime_type: 'audio/mpeg', container: 'service_file' },
{ label: 'ogg', format: 'ogg', url: derivative_url('ogg'), mime_type: 'audio/ogg', container: 'service_file' }])
create_derivatives_for(:audio, filename, Hydra::Derivatives::AudioDerivatives)
end

def create_video_derivatives(filename)
Hydra::Derivatives::VideoDerivatives.create(filename,
outputs: [{ label: :thumbnail, format: 'jpg', url: derivative_url('thumbnail'), mime_type: 'image/jpeg' },
{ label: 'webm', format: 'webm', url: derivative_url('webm'), mime_type: 'video/webm', container: 'service_file' },
{ label: 'mp4', format: 'mp4', url: derivative_url('mp4'), mime_type: 'video/mp4', container: 'service_file' }])
create_derivatives_for(:video, filename, Hydra::Derivatives::VideoDerivatives)
end

def create_image_derivatives(filename)
# We're asking for layer 0, becauase otherwise pyramidal tiffs flatten all the layers together into the thumbnail
Hydra::Derivatives::ImageDerivatives.create(filename,
outputs: [{ label: :thumbnail,
format: 'jpg',
size: '200x150>',
url: derivative_url('thumbnail'),
layer: 0 }])
create_derivatives_for(:image, filename, Hydra::Derivatives::ImageDerivatives)
end

# Routes +extracted_text+ outputs to full text extraction; the rest to +processor+.
def create_derivatives_for(type, filename, processor)
text_outputs, processor_outputs = derivative_outputs(type).partition { |output| output[:container] == 'extracted_text' }
processor.create(filename, outputs: processor_outputs) if processor_outputs.any?
extract_full_text(filename, text_outputs) if text_outputs.any?
end

# Resolves configured specs for +type+: callable values are invoked with the
# file set, and +:url+ destination names become real derivative URLs.
def derivative_outputs(type)
Hyrax.config.derivative_options.fetch(type).map do |output|
resolved = output.transform_values { |value| value.respond_to?(:call) ? value.call(file_set) : value }
resolved.merge(url: derivative_url(resolved[:url]))
end
end

def derivative_path_factory
Expand All @@ -119,12 +109,11 @@ def derivative_path_factory
# Calls the Hydra::Derivates::FulltextExtraction unless the extract_full_text
# configuration option is set to false
# @param [String] filename of the object to be used for full text extraction
# @param [String] uri to the file set (deligated to file_set)
def extract_full_text(filename, uri)
# @param [Array<Hash>] outputs the resolved extracted_text output specs
def extract_full_text(filename, outputs)
return unless Hyrax.config.extract_full_text?

Hydra::Derivatives::FullTextExtract.create(filename,
outputs: [{ url: uri, container: "extracted_text" }])
Hydra::Derivatives::FullTextExtract.create(filename, outputs: outputs)
end
end
end
37 changes: 37 additions & 0 deletions lib/hyrax/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,43 @@ def derivative_mime_type_mappings
video: lookup_mimes(:video_mime_types) }
end

attr_writer :derivative_options

##
# The +outputs:+ specs passed to +Hydra::Derivatives+ per derivative type.
# +:url+ is a destination name resolved to a real URL at create time; a
# callable value is invoked with the file set. An +extracted_text+ container
# routes to full text extraction (gated also by {#extract_full_text?}) — its
# presence is the per-type toggle. Tune these instead of decorating
# +Hyrax::FileSetDerivativesService+:
#
# Hyrax.config do |config|
# config.derivative_options[:pdf].first[:size] = '676x986' # bigger thumbnail
# config.derivative_options[:pdf].reject! { |o| o[:container] == 'extracted_text' } # no PDF text
#
# # transcode video to its source dimensions (falls back to 320x240)
# sizer = ->(fs) { (w = Array(fs.width).first) && (h = Array(fs.height).first) ? "#{w}x#{h}" : '320x240' }
# config.derivative_options[:video].each { |o| o[:size] = sizer }
# end
#
# @return [Hash{Symbol => Array<Hash>}]
# @see Hyrax::FileSetDerivativesService
def derivative_options
@derivative_options ||= {
pdf: [{ label: :thumbnail, format: 'jpg', size: '338x493', url: 'thumbnail', layer: 0 },
{ url: 'extracted_text', container: 'extracted_text' }],
office: [{ label: :thumbnail, format: 'jpg', size: '200x150>', url: 'thumbnail', layer: 0 },
{ url: 'extracted_text', container: 'extracted_text' }],
audio: [{ label: 'mp3', format: 'mp3', url: 'mp3', mime_type: 'audio/mpeg', container: 'service_file' },
{ label: 'ogg', format: 'ogg', url: 'ogg', mime_type: 'audio/ogg', container: 'service_file' }],
video: [{ label: :thumbnail, format: 'jpg', url: 'thumbnail', mime_type: 'image/jpeg' },
{ label: 'webm', format: 'webm', url: 'webm', mime_type: 'video/webm', container: 'service_file' },
{ label: 'mp4', format: 'mp4', url: 'mp4', mime_type: 'video/mp4', container: 'service_file' }],
# image uses layer: 0 because otherwise pyramidal tiffs flatten all the layers together into the thumbnail
image: [{ label: :thumbnail, format: 'jpg', size: '200x150>', url: 'thumbnail', layer: 0 }]
}
end

attr_writer :derivative_services
# The registered candidate derivative services. In the array, the first `valid?` candidate will
# handle the derivative generation.
Expand Down
27 changes: 27 additions & 0 deletions spec/lib/hyrax/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
it { is_expected.to respond_to(:default_admin_set_id) }
it { is_expected.to respond_to(:derivative_services) }
it { is_expected.to respond_to(:derivative_services=) }
it { is_expected.to respond_to(:derivative_options) }
it { is_expected.to respond_to(:derivative_options=) }
it { is_expected.to respond_to(:display_media_download_link=) }
it { is_expected.to respond_to(:display_media_download_link?) }
it { is_expected.to respond_to(:display_microdata?) }
Expand Down Expand Up @@ -132,6 +134,31 @@
end
end

describe "#derivative_options" do
it "defaults to output specs for each derivative type" do
expect(described_class.new.derivative_options.keys)
.to contain_exactly(:pdf, :office, :audio, :video, :image)
end

it "resolves url to a destination name rather than a full url" do
expect(described_class.new.derivative_options[:image])
.to contain_exactly(hash_including(label: :thumbnail, url: 'thumbnail', layer: 0))
end

it "is overridable" do
config = described_class.new
config.derivative_options = { image: [{ label: :big, format: 'jpg', size: '999x999', url: 'big' }] }
expect(config.derivative_options[:image].first).to include(size: '999x999')
end

it "extracts text for pdf and office by default" do
%i[pdf office].each do |type|
expect(described_class.new.derivative_options[type])
.to include(hash_including(container: 'extracted_text'))
end
end
end

describe "#use_valkyrie?" do
before { stub_const("ENV", "HYRAX_SKIP_WINGS" => "true") }
it "returns true if wings is disabled" do
Expand Down
80 changes: 80 additions & 0 deletions spec/services/hyrax/file_set_derivatives_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,66 @@
end
end

context "when an app overrides only the image thumbnail size" do
let(:valid_file_set) do
FactoryBot.valkyrie_create(:hyrax_file_metadata, :image, file_set_id: SecureRandom.uuid)
end
let(:custom_size) { '9999x9999' }

before do
allow(Hyrax.config).to receive(:derivative_options).and_return(
image: [Hyrax::Configuration.new.derivative_options[:image].first.merge(size: custom_size)]
)
end

it "creates the thumbnail at the configured size but keeps the other defaults" do
allow(Hydra::Derivatives::ImageDerivatives).to receive(:create)
described_class.new(valid_file_set).create_derivatives('foo.jpg')
expect(Hydra::Derivatives::ImageDerivatives).to have_received(:create).with(
'foo.jpg',
outputs: contain_exactly(
hash_including(size: custom_size, label: :thumbnail, format: 'jpg', layer: 0)
)
)
end
end

context "when given a pdf file" do
let(:valid_file_set) do
FactoryBot.valkyrie_create(:hyrax_file_metadata, mime_type: 'application/pdf', file_set_id: SecureRandom.uuid)
end

before do
allow(Hydra::Derivatives::PdfDerivatives).to receive(:create)
allow(Hydra::Derivatives::FullTextExtract).to receive(:create)
end

it "creates the thumbnail and extracts full text" do
described_class.new(valid_file_set).create_derivatives('foo.pdf')
expect(Hydra::Derivatives::PdfDerivatives).to have_received(:create).with(
'foo.pdf', outputs: contain_exactly(hash_including(label: :thumbnail))
)
expect(Hydra::Derivatives::FullTextExtract).to have_received(:create).with(
'foo.pdf', outputs: contain_exactly(hash_including(container: 'extracted_text'))
)
end

context "when the extracted_text output is removed from the config" do
before do
pdf_without_text = Hyrax::Configuration.new.derivative_options[:pdf].reject { |o| o[:container] == 'extracted_text' }
allow(Hyrax.config).to receive(:derivative_options).and_return(pdf: pdf_without_text)
end

it "still creates the thumbnail but skips full text extraction" do
described_class.new(valid_file_set).create_derivatives('foo.pdf')
expect(Hydra::Derivatives::PdfDerivatives).to have_received(:create).with(
'foo.pdf', outputs: contain_exactly(hash_including(label: :thumbnail))
)
expect(Hydra::Derivatives::FullTextExtract).not_to have_received(:create)
end
end
end

context "when given a video file" do
let(:valid_file_set) do
FactoryBot.valkyrie_create(:hyrax_file_metadata, :video_file, file_set_id: SecureRandom.uuid)
Expand All @@ -59,6 +119,26 @@
)
)
end

context "when an output size is a callable" do
let(:received_file_set) { [] }

before do
size_from = ->(file_set) { received_file_set << file_set && '1280x720' }
sized = Hyrax::Configuration.new.derivative_options[:video].map { |output| output.merge(size: size_from) }
allow(Hyrax.config).to receive(:derivative_options).and_return(video: sized)
end

it "invokes it with the file set and passes the result through" do
allow(Hydra::Derivatives::VideoDerivatives).to receive(:create)
described_class.new(valid_file_set).create_derivatives('foo.mov')

expect(received_file_set).to all(eq(valid_file_set))
expect(Hydra::Derivatives::VideoDerivatives).to have_received(:create) do |_filename, outputs:|
expect(outputs).to all(include(size: '1280x720'))
end
end
end
end
end
end
Expand Down
Loading