hydra-derivatives 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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