hydra-derivatives 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2760dd208eaff0215533b8885e35b115a78f1f4f
4
+ data.tar.gz: cdeab3f70511982e5440ad348856c0f51f7bc626
5
+ SHA512:
6
+ metadata.gz: d8f7ec3e514d6d8b2e9aca347e1b2cff3e669e2d3de44bcdf24afd12790262f886d0b5ffb206c8ecea72b72d6151d850b0e7d65a2e5a092154c05b1eeff522f3
7
+ data.tar.gz: 0d15fefd69e56e2afc29806d3201e1787f9e00d41ef973bce43b57ca23965247f379346138caf6570d797573a1521db656f2b1d38ef3c5f358726e0e0d88f5c5
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ tmp/
3
+ jetty/
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ notifications:
6
+ email:
7
+ recipients:
8
+ - "ul-dlt-applications@lists.psu.edu"
9
+ - "michael@psu.edu"
10
+ on_success: "change"
11
+ on_failure: "always"
12
+ irc:
13
+ channels:
14
+ - "irc.freenode.org#scholarsphere"
15
+ - "irc.freenode.org#projecthydra"
16
+ template:
17
+ - "%{repository}//%{branch}@%{commit} by %{author}: %{message} - %{build_url}"
@@ -0,0 +1,111 @@
1
+ # How to Contribute
2
+
3
+ We want your help to make Project Hydra great.
4
+ There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things.
5
+
6
+ ## Hydra Project Intellectual Property Licensing and Ownership
7
+
8
+ All code contributors must have an Individual Contributor License Agreement (iCLA) on file with the Hydra Project Steering Group.
9
+ If the contributor works for an institution, the institution must have a Corporate Contributor License Agreement (cCLA) on file.
10
+
11
+ https://wiki.duraspace.org/display/hydra/Hydra+Project+Intellectual+Property+Licensing+and+Ownership
12
+
13
+ You should also add yourself to the `CONTRIBUTORS.md` file in the root of the project.
14
+
15
+ ## Contribution Tasks
16
+
17
+ * Reporting Issues
18
+ * Making Changes
19
+ * Submitting Changes
20
+ * Merging Changes
21
+
22
+ ### Reporting Issues
23
+
24
+ * Make sure you have a [GitHub account](https://github.com/signup/free)
25
+ * Submit a [Github issue](./issues) by:
26
+ * Clearly describing the issue
27
+ * Provide a descriptive summary
28
+ * Explain the expected behavior
29
+ * Explain the actual behavior
30
+ * Provide steps to reproduce the actual behavior
31
+
32
+ ### Making Changes
33
+
34
+ * Fork the repository on GitHub
35
+ * Create a topic branch from where you want to base your work.
36
+ * This is usually the master branch.
37
+ * To quickly create a topic branch based on master; `git branch fix/master/my_contribution master`
38
+ * Then checkout the new branch with `git checkout fix/master/my_contribution`.
39
+ * Please avoid working directly on the `master` branch.
40
+ * You may find the [hub suite of commands](https://github.com/defunkt/hub) helpful
41
+ * Make commits of logical units.
42
+ * Your commit should include a high level description of your work in HISTORY.textile
43
+ * Check for unnecessary whitespace with `git diff --check` before committing.
44
+ * Make sure your commit messages are [well formed](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
45
+ * If you created an issue, you can close it by including "Closes #issue" in your commit message. See [Github's blog post for more details](https://github.com/blog/1386-closing-issues-via-commit-messages)
46
+
47
+ ```
48
+ Present tense short summary (50 characters or less)
49
+
50
+ More detailed description, if necessary. It should be wrapped to 72
51
+ characters. Try to be as descriptive as you can, even if you think that
52
+ the commit content is obvious, it may not be obvious to others. You
53
+ should add such description also if it's already present in bug tracker,
54
+ it should not be necessary to visit a webpage to check the history.
55
+
56
+ Include Closes #<issue-number> when relavent.
57
+
58
+ Description can have multiple paragraphs and you can use code examples
59
+ inside, just indent it with 4 spaces:
60
+
61
+ class PostsController
62
+ def index
63
+ respond_with Post.limit(10)
64
+ end
65
+ end
66
+
67
+ You can also add bullet points:
68
+
69
+ - you can use dashes or asterisks
70
+
71
+ - also, try to indent next line of a point for readability, if it's too
72
+ long to fit in 72 characters
73
+ ```
74
+
75
+ * Make sure you have added the necessary tests for your changes.
76
+ * Run _all_ the tests to assure nothing else was accidentally broken.
77
+ * When you are ready to submit a pull request
78
+
79
+ ### Submitting Changes
80
+
81
+ * Read the article ["Using Pull Requests"](https://help.github.com/articles/using-pull-requests) on GitHub.
82
+ * Make sure your branch is up to date with its parent branch (i.e. master)
83
+ * `git checkout master`
84
+ * `git pull --rebase`
85
+ * `git checkout <your-branch>`
86
+ * `git rebase master`
87
+ * It is likely a good idea to run your tests again.
88
+ * Squash the commits for your branch into one commit
89
+ * `git rebase --interactive HEAD~<number-of-commits>` ([See Github help](https://help.github.com/articles/interactive-rebase))
90
+ * To determine the number of commits on your branch: `git log master..<your-branch> --oneline | wc -l`
91
+ * Squashing your branch's changes into one commit is "good form" and helps the person merging your request to see everything that is going on.
92
+ * Push your changes to a topic branch in your fork of the repository.
93
+ * Submit a pull request from your fork to the project.
94
+
95
+ ### Merging Changes
96
+
97
+ * It is considered "poor from" to merge your own request.
98
+ * Please take the time to review the changes and get a sense of what is being changed. Things to consider:
99
+ * Does the commit message explain what is going on?
100
+ * Does the code changes have tests? _Not all changes need new tests, some changes are refactorings_
101
+ * Does the commit contain more than it should? Are two separate concerns being addressed in one commit?
102
+ * Did the Travis tests complete successfully?
103
+ * If you are uncertain, bring other contributors into the conversation by creating a comment that includes their @username.
104
+ * If you like the pull request, but want others to chime in, create a +1 comment and tag a user.
105
+
106
+ # Additional Resources
107
+
108
+ * [General GitHub documentation](http://help.github.com/)
109
+ * [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
110
+ * [Pro Git](http://git-scm.com/book) is both a free and excellent book about Git.
111
+ * [A Git Config for Contributing](http://ndlib.github.io/practices/my-typical-per-project-git-config/)
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Please see hydra-derivatives.gemspec for dependency information.
4
+ gemspec
5
+
@@ -0,0 +1,15 @@
1
+ ##########################################################################
2
+ # Copyright 2013 Notre Dame, Data Curation Experts, WGBH
3
+ # Additional copyright may be held by others, as reflected in the commit log
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
@@ -0,0 +1,4 @@
1
+ hydra-derivatives
2
+ =================
3
+
4
+ Derivative generation for hydra
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ APP_ROOT="." # for jettywrapper
6
+ require 'jettywrapper'
7
+ # Dir.glob('tasks/*.rake').each { |r| import r }
8
+
9
+ require 'rspec/core/rake_task'
10
+
11
+ RSpec::Core::RakeTask.new(:spec)
12
+
13
+
14
+ desc 'Spin up hydra-jetty and run specs'
15
+ task :ci => ['jetty:unzip'] do
16
+ puts 'running continuous integration'
17
+ jetty_params = Jettywrapper.load_config
18
+ error = Jettywrapper.wrap(jetty_params) do
19
+ Rake::Task['spec'].invoke
20
+ end
21
+ raise "test failures: #{error}" if error
22
+ end
23
+
24
+ task :default => :ci
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ Are small images upconverted?
2
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,6 @@
1
+ default:
2
+ jetty_port: <%= ENV['TEST_JETTY_PORT'] || 8983 %>
3
+ startup_wait: 60
4
+ java_opts:
5
+ - "-Xmx256m"
6
+ - "-XX:MaxPermSize=128m"
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ version = File.read(File.expand_path("../VERSION", __FILE__)).strip
3
+
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "hydra-derivatives"
7
+ spec.version = version
8
+ spec.authors = ["Justin Coyne"]
9
+ spec.email = ["justin@curationexperts.com"]
10
+ spec.description = %q{Derivative generation plugin for hydra}
11
+ spec.summary = %q{Derivative generation plugin for hydra}
12
+ spec.license = "APACHE2"
13
+ spec.homepage = "https://github.com/projecthydra/hydra-derivatives"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "jettywrapper"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.add_dependency 'active-fedora'
26
+ spec.add_dependency 'rmagick'
27
+ spec.add_dependency 'activesupport', '>= 3.2.13', '< 5.0'
28
+ end
29
+
@@ -0,0 +1,113 @@
1
+ require 'active_fedora'
2
+ require 'RMagick'
3
+ module Hydra
4
+ module Derivatives
5
+ extend ActiveSupport::Concern
6
+ extend ActiveSupport::Autoload
7
+
8
+ autoload :Processor
9
+ autoload :Image
10
+ autoload :Ffmpeg
11
+ autoload :Video
12
+ autoload :Audio
13
+ autoload :ExtractMetadata
14
+
15
+ def self.ffmpeg_path=(val)
16
+ @ffmpeg_path = val
17
+ end
18
+ def self.ffmpeg_path
19
+ #Sufia.config.ffmpeg_path
20
+ @ffmpeg_path ||= 'ffmpeg'
21
+ end
22
+
23
+ def self.temp_file_base=(val)
24
+ @temp_file_base = val
25
+ end
26
+ def self.temp_file_base
27
+ #Sufia.config.temp_file_base
28
+ @temp_file_base ||= '/tmp'
29
+ end
30
+
31
+ def self.fits_path=(val)
32
+ @fits_path = val
33
+ end
34
+ def self.fits_path
35
+ #Sufia.config.fits_path
36
+ @fits_path ||= 'fits.sh'
37
+ end
38
+
39
+ def self.enable_ffmpeg=(val)
40
+ @enable_ffmpeg = val
41
+ end
42
+ def self.enable_ffmpeg
43
+ @enable_ffmpeg ||= true
44
+ end
45
+
46
+ included do
47
+ class_attribute :transformation_scheme
48
+ end
49
+
50
+
51
+ # Runs all of the transformations immediately.
52
+ # You may want to run this job in the background as it may take a long time.
53
+ def create_derivatives
54
+ if transformation_scheme.present?
55
+ transformation_scheme.each do |datastream, value|
56
+ transform_datastream(datastream, value) if self.datastreams[datastream.to_s].has_content?
57
+ end
58
+ else
59
+ logger.warn "`create_derivatives' was called on an instance of #{self.class}, but no derivatives have been requested"
60
+ end
61
+ end
62
+
63
+ # Transform a single datastream
64
+ def transform_datastream(datastream, directive_list)
65
+ directive_list.each do |directive|
66
+ if directive.applies?(self)
67
+ processor = directive.processors ? Array(directive.processors).first : :image
68
+ "Hydra::Derivatives::#{processor.to_s.classify}".constantize.new(self, datastream, directive.derivatives).process
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ class TransformationDirective
75
+ attr_accessor :differentiator, :selector, :derivatives, :processors
76
+ # @param [Hash] args the options
77
+ # @option args [Symbol] :based_on the method that holds the differentiator column
78
+ # @option args [String, Array] :when activates this set of derivatives when the the differentiator column is includes one of these.
79
+ # @option args [Hash] :derivatives the derivatives to be produced
80
+ # @option args [Symbol, Array] :processors the processors to run to produce the derivatives
81
+ def initialize(args)
82
+ self.differentiator = args[:based_on]
83
+ self.selector = args[:when]
84
+ self.derivatives = args[:derivatives]
85
+ self.processors = args[:processors]
86
+ end
87
+
88
+ def applies?(object)
89
+ selector.include?(object.send(differentiator))
90
+ end
91
+ end
92
+
93
+ module ClassMethods
94
+ # @param [Symbol, String] datastream the datastream to operate on
95
+ # @param [Hash] args the options
96
+ # @option args [Symbol] :based_on the method that holds the differentiator column
97
+ # @option args [String, Array] :when activates this set of derivatives when the the differentiator column is includes one of these.
98
+ # @option args [Hash] :derivatives the derivatives to be produced
99
+ # @option args [Symbol, Array] :processors the processors to run to produce the derivatives
100
+ # @example
101
+ # makes_derivatives_of :content, based_on: :mime_type, when: 'text/pdf',
102
+ # derivatives: { :text => { :quality => :better }, processors: [:ocr]}
103
+ #
104
+ # makes_derivatives_of :content, based_on: :mime_type, when: ['image/png', 'image/jpg'],
105
+ # derivatives: { :medium => "300x300>", :thumb => "100x100>" }
106
+ def makes_derivatives_of(datastream, args = {})
107
+ self.transformation_scheme ||= {}
108
+ self.transformation_scheme[datastream.to_sym] ||= []
109
+ self.transformation_scheme[datastream.to_sym] << TransformationDirective.new(args)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,22 @@
1
+ module Hydra
2
+ module Derivatives
3
+ class Audio < Processor
4
+ include Ffmpeg
5
+
6
+ protected
7
+
8
+ def options_for(format)
9
+ end
10
+
11
+ def new_mime_type(format)
12
+ case format
13
+ when 'mp3'
14
+ "audio/mpeg"
15
+ else
16
+ "audio/#{format}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,55 @@
1
+ require 'open3'
2
+ module Hydra
3
+ module Derivatives
4
+ module ExtractMetadata
5
+ include Open3
6
+
7
+ def extract_metadata
8
+ out = nil
9
+ to_tempfile do |f|
10
+ out = run_fits!(f.path)
11
+ end
12
+ out
13
+ end
14
+
15
+ def to_tempfile &block
16
+ return unless has_content?
17
+ tmp_base = Hydra::Derivatives.temp_file_base
18
+ f = Tempfile.new("#{pid}-#{dsVersionID}")
19
+ f.binmode
20
+ if content.respond_to? :read
21
+ f.write(content.read)
22
+ else
23
+ f.write(content)
24
+ end
25
+ f.close
26
+ content.rewind if content.respond_to? :rewind
27
+ yield(f)
28
+ f.unlink
29
+ end
30
+
31
+ private
32
+
33
+
34
+ def run_fits!(file_path)
35
+ command = "#{fits_path} -i \"#{file_path}\""
36
+ stdin, stdout, stderr, wait_thr = popen3(command)
37
+ stdin.close
38
+ out = stdout.read
39
+ stdout.close
40
+ err = stderr.read
41
+ stderr.close
42
+ exit_status = wait_thr.value
43
+ raise "Unable to execute command \"#{command}\"\n#{err}" unless exit_status.success?
44
+ out
45
+ end
46
+
47
+
48
+ def fits_path
49
+ Hydra::Derivatives.fits_path
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,51 @@
1
+ # An abstract class for asyncronous jobs that transcode files using FFMpeg
2
+
3
+ require 'tmpdir'
4
+ require 'open3'
5
+
6
+ module Hydra
7
+ module Derivatives
8
+ module Ffmpeg
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ extend Open3
13
+ end
14
+
15
+ def process
16
+ directives.each do |name, args|
17
+ raise ArgumentError, "You must provide the :format you want to transcode into. You provided #{args}" unless args[:format]
18
+ # TODO if the source is in the correct format, we could just copy it and skip transcoding.
19
+ encode_datastream(output_datastream_id(name), args[:format], new_mime_type(args[:format]), options_for(args[:format]))
20
+ end
21
+ end
22
+
23
+ def encode_datastream(dest_dsid, file_suffix, mime_type, options = '')
24
+ out_file = nil
25
+ output_file = Dir::Tmpname.create(['sufia', ".#{file_suffix}"], Hydra::Derivatives.temp_file_base){}
26
+ source_datastream.to_tempfile do |f|
27
+ self.class.encode(f.path, options, output_file)
28
+ end
29
+ out_file = File.open(output_file, "rb")
30
+ object.add_file_datastream(out_file.read, :dsid=>dest_dsid, :mimeType=>mime_type)
31
+ File.unlink(output_file)
32
+ end
33
+
34
+ module ClassMethods
35
+
36
+ def encode(path, options, output_file)
37
+ command = "#{Hydra::Derivatives.ffmpeg_path} -y -i \"#{path}\" #{options} #{output_file}"
38
+ stdin, stdout, stderr, wait_thr = popen3(command)
39
+ stdin.close
40
+ out = stdout.read
41
+ stdout.close
42
+ err = stderr.read
43
+ stderr.close
44
+ raise "Unable to execute command \"#{command}\"\n#{err}" unless wait_thr.value.success?
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,53 @@
1
+ module Hydra
2
+ module Derivatives
3
+ class Image < Processor
4
+ def process
5
+ directives.each do |name, args|
6
+ size = args
7
+ output_datastream_name = output_datastream_id(name)
8
+ if args.kind_of? Hash
9
+ size = args[:size]
10
+ output_datastream_name = args[:datastream] if args[:datastream]
11
+ end
12
+ create_resized_image(output_datastream_name, size, new_mime_type)
13
+ end
14
+ end
15
+
16
+
17
+ protected
18
+
19
+ def new_mime_type
20
+ 'image/png'
21
+ end
22
+
23
+
24
+ def create_resized_image(output_dsid, size, mime_type, quality=nil)
25
+ create_image(output_dsid, mime_type, quality) do |xfrm|
26
+ xfrm.change_geometry!(size) do |cols, rows, img|
27
+ img.resize!(cols, rows)
28
+ end
29
+ end
30
+ end
31
+
32
+ def create_image(dsid, mime_type, quality=nil)
33
+ xfrm = load_image_transformer
34
+ yield(xfrm) if block_given?
35
+ #out = output_datastream
36
+ output_datastream(dsid).content = if quality
37
+ xfrm.to_blob { self.quality = quality }
38
+ else
39
+ xfrm.to_blob
40
+ end
41
+ output_datastream(dsid).mimeType = mime_type
42
+ end
43
+
44
+ # Override this method if you want a different transformer, or need to load the
45
+ # raw image from a different source (e.g. external datastream)
46
+ def load_image_transformer
47
+ Magick::ImageList.new.tap do |xformer|
48
+ xformer.from_blob(source_datastream.content)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,38 @@
1
+ module Hydra
2
+ module Derivatives
3
+ class Processor
4
+ attr_accessor :object, :source_name, :directives
5
+
6
+ def initialize(obj, source_name, directives)
7
+ self.object = obj
8
+ self.source_name = source_name
9
+ self.directives = directives
10
+ end
11
+
12
+ def process
13
+ raise "Processor is an abstract class. Implement `process' on #{self.class.name}"
14
+ end
15
+
16
+ def output_datastream_id(name)
17
+ [source_name, name].join('_')
18
+ end
19
+
20
+ def output_datastream(dsid)
21
+ # first, check for a defined datastream
22
+ output_datastream = if object.datastreams[dsid]
23
+ object.datastreams[dsid]
24
+ else
25
+ ds = ActiveFedora::Datastream.new(object.inner_object, dsid)
26
+ object.add_datastream(ds)
27
+ ds
28
+ end
29
+ end
30
+
31
+ def source_datastream
32
+ object.datastreams[source_name.to_s]
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,46 @@
1
+ module Hydra
2
+ module Derivatives
3
+ class Video < Processor
4
+ include Ffmpeg
5
+
6
+ protected
7
+
8
+ def options_for(format)
9
+ "-s #{size_attributes} #{video_attributes} #{codecs(format)} #{audio_attributes}"
10
+ end
11
+
12
+ def video_bitrate
13
+ '345k'
14
+ end
15
+
16
+ def video_attributes
17
+ "-g 30 -b:v #{video_bitrate}"
18
+ end
19
+
20
+ def size_attributes
21
+ "320x240"
22
+ end
23
+
24
+ def audio_attributes
25
+ "-ac 2 -ab 96k -ar 44100"
26
+ end
27
+
28
+ def codecs(format)
29
+ case format
30
+ when 'mp4'
31
+ "-vcodec libx264 -acodec libfaac"
32
+ when 'webm'
33
+ "-acodec libvorbis"
34
+ else
35
+ raise ArgumentError, "Unknown format `#{format}'"
36
+ end
37
+ end
38
+
39
+ def new_mime_type(format)
40
+ "video/#{format}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ require 'rspec/autorun'
2
+ ENV['environment'] ||= 'test'
3
+ # - RSpec adds ./lib to the $LOAD_PATH
4
+ require 'hydra/derivatives'
5
+ #Resque.inline = Rails.env.test?
6
+
7
+ RSpec.configure do |config|
8
+ end
9
+
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hydra::Derivatives::Image do
4
+ describe "when arguments are passed as a string" do
5
+ let(:directives) {{ :thumb => "100x100>" } }
6
+ subject { Hydra::Derivatives::Image.new(double(:obj), 'content', directives)}
7
+
8
+ it "should use the string as the size and the name is autogenerated" do
9
+ subject.should_receive(:create_resized_image).with("content_thumb", "100x100>", 'image/png')
10
+ subject.process
11
+ end
12
+ end
13
+ describe "when arguments are passed as a hash" do
14
+ let(:directives) {{ :thumb => {size: "200x300>", datastream: 'thumbnail'} }}
15
+ subject { Hydra::Derivatives::Image.new(double(:obj), 'content', directives)}
16
+ it "should use the specified size and name" do
17
+ subject.should_receive(:create_resized_image).with("thumbnail", "200x300>", 'image/png')
18
+ subject.process
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Transcoder" do
4
+ before do
5
+ class ContentDatastream < ActiveFedora::Datastream
6
+ include Hydra::Derivatives::ExtractMetadata
7
+ end
8
+ class GenericFile < ActiveFedora::Base
9
+ include Hydra::Derivatives
10
+ has_metadata 'characterization', type: ActiveFedora::SimpleDatastream do |m|
11
+ m.field "mime_type", :string
12
+ end
13
+
14
+ delegate :mime_type, :to => :characterization, :unique => true
15
+ has_file_datastream 'content', type: ContentDatastream
16
+
17
+ makes_derivatives_of :content, based_on: :mime_type, when: 'application/pdf',
18
+ derivatives: { :thumb => "100x100>" }
19
+
20
+ makes_derivatives_of :content, based_on: :mime_type, when: 'audio/wav',
21
+ derivatives: { :mp3 => {format: 'mp3'}, :ogg => {format: 'ogg'} }, processors: :audio
22
+
23
+ # -g 30 enforces keyframe generation every second (30fps)
24
+ # -b:v is the video bitrate
25
+ # -acodec is the audio codec
26
+ size_attributes = "-s 320x240"
27
+ audio_attributes = "-ac 2 -ab 96k -ar 44100"
28
+ makes_derivatives_of :content, based_on: :mime_type, when: 'video/avi',
29
+ derivatives: { :mp4 => {format: 'mp4'}, :webm => {format: 'webm'} }, processors: :video
30
+
31
+ makes_derivatives_of :content, based_on: :mime_type, when: ['image/png', 'image/jpg'],
32
+ derivatives: { :medium => "300x300>", :thumb => "100x100>" }
33
+
34
+ end
35
+ end
36
+ describe "with an attached image" do
37
+ let(:attachment) { File.open(File.expand_path('../../fixtures/world.png', __FILE__))}
38
+ let(:file) { GenericFile.new(mime_type: 'image/png').tap { |t| t.content.content = attachment; t.save } }
39
+
40
+ it "should transcode" do
41
+ file.datastreams.key?('content_medium').should be_false
42
+ file.create_derivatives
43
+ file.datastreams['content_medium'].should have_content
44
+ file.datastreams['content_medium'].mimeType.should == 'image/png'
45
+ file.datastreams['content_thumb'].should have_content
46
+ file.datastreams['content_thumb'].mimeType.should == 'image/png'
47
+ file.datastreams.key?('content_text').should be_false
48
+ end
49
+ end
50
+
51
+ describe "with an attached pdf" do
52
+ let(:attachment) { File.open(File.expand_path('../../fixtures/test.pdf', __FILE__))}
53
+ let(:file) { GenericFile.new(mime_type: 'application/pdf').tap { |t| t.content.content = attachment; t.save } }
54
+
55
+ it "should transcode" do
56
+ file.datastreams.key?('content_thumb').should be_false
57
+ file.create_derivatives
58
+ file.datastreams['content_thumb'].should have_content
59
+ file.datastreams['content_thumb'].mimeType.should == 'image/png'
60
+ end
61
+ end
62
+
63
+ describe "with an attached audio", unless: ENV['TRAVIS'] == 'true' do
64
+ let(:attachment) { File.open(File.expand_path('../../fixtures/piano_note.wav', __FILE__))}
65
+ let(:file) { GenericFile.new(mime_type: 'audio/wav').tap { |t| t.content.content = attachment; t.save } }
66
+
67
+ it "should transcode" do
68
+ file.create_derivatives
69
+ file.datastreams['content_mp3'].should have_content
70
+ file.datastreams['content_mp3'].mimeType.should == 'audio/mpeg'
71
+ file.datastreams['content_ogg'].should have_content
72
+ file.datastreams['content_ogg'].mimeType.should == 'audio/ogg'
73
+ end
74
+ end
75
+
76
+ describe "with an attached video", unless: ENV['TRAVIS'] == 'true' do
77
+ let(:attachment) { File.open(File.expand_path('../../fixtures/countdown.avi', __FILE__))}
78
+ let(:file) { GenericFile.new(mime_type: 'video/avi').tap { |t| t.content.content = attachment; t.save } }
79
+
80
+ it "should transcode" do
81
+ file.create_derivatives
82
+ file.datastreams['content_mp4'].should have_content
83
+ file.datastreams['content_mp4'].mimeType.should == 'video/mp4'
84
+ file.datastreams['content_webm'].should have_content
85
+ file.datastreams['content_webm'].mimeType.should == 'video/webm'
86
+ end
87
+ end
88
+ end
metadata ADDED
@@ -0,0 +1,180 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hydra-derivatives
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Justin Coyne
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: jettywrapper
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: active-fedora
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rmagick
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activesupport
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: 3.2.13
104
+ - - <
105
+ - !ruby/object:Gem::Version
106
+ version: '5.0'
107
+ type: :runtime
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - '>='
112
+ - !ruby/object:Gem::Version
113
+ version: 3.2.13
114
+ - - <
115
+ - !ruby/object:Gem::Version
116
+ version: '5.0'
117
+ description: Derivative generation plugin for hydra
118
+ email:
119
+ - justin@curationexperts.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - .gitignore
125
+ - .travis.yml
126
+ - CONTRIBUTING.md
127
+ - Gemfile
128
+ - LICENSE.txt
129
+ - README.md
130
+ - Rakefile
131
+ - TODO
132
+ - VERSION
133
+ - config/jetty.yml
134
+ - hydra-derivatives.gemspec
135
+ - lib/hydra/derivatives.rb
136
+ - lib/hydra/derivatives/audio.rb
137
+ - lib/hydra/derivatives/extract_metadata.rb
138
+ - lib/hydra/derivatives/ffmpeg.rb
139
+ - lib/hydra/derivatives/image.rb
140
+ - lib/hydra/derivatives/processor.rb
141
+ - lib/hydra/derivatives/video.rb
142
+ - spec/fixtures/countdown.avi
143
+ - spec/fixtures/piano_note.wav
144
+ - spec/fixtures/test.pdf
145
+ - spec/fixtures/world.png
146
+ - spec/spec_helper.rb
147
+ - spec/units/image_spec.rb
148
+ - spec/units/transcoding_spec.rb
149
+ homepage: https://github.com/projecthydra/hydra-derivatives
150
+ licenses:
151
+ - APACHE2
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.0.5
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Derivative generation plugin for hydra
173
+ test_files:
174
+ - spec/fixtures/countdown.avi
175
+ - spec/fixtures/piano_note.wav
176
+ - spec/fixtures/test.pdf
177
+ - spec/fixtures/world.png
178
+ - spec/spec_helper.rb
179
+ - spec/units/image_spec.rb
180
+ - spec/units/transcoding_spec.rb