sufia 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +5 -2
  5. data/History.md +6 -0
  6. data/README.md +40 -0
  7. data/Rakefile +4 -0
  8. data/app/assets/javascripts/sufia.js +3 -115
  9. data/app/assets/javascripts/sufia/batch_select_all.js +179 -0
  10. data/app/assets/javascripts/sufia/edit_metadata.js +86 -0
  11. data/app/assets/javascripts/sufia/multiForm.js +57 -0
  12. data/app/assets/javascripts/terms_of_service.js +7 -0
  13. data/app/assets/stylesheets/audio-js.css +3 -0
  14. data/app/assets/stylesheets/dashboard.css.scss +51 -0
  15. data/app/assets/stylesheets/generic_files.css +36 -0
  16. data/app/assets/stylesheets/sufia.css.scss +2 -0
  17. data/app/controllers/batch_controller.rb +11 -0
  18. data/app/controllers/batch_edits_controller.rb +1 -77
  19. data/app/controllers/generic_files_controller.rb +1 -0
  20. data/app/controllers/mailbox_controller.rb +1 -1
  21. data/app/controllers/single_use_link_controller.rb +11 -7
  22. data/app/helpers/generic_file_helper.rb +11 -3
  23. data/app/helpers/sufia_helper.rb +13 -10
  24. data/app/models/batch.rb +1 -1
  25. data/app/models/datastreams/fits_datastream.rb +2 -2
  26. data/app/models/datastreams/generic_file_rdf_datastream.rb +22 -18
  27. data/app/models/datastreams/properties_datastream.rb +2 -2
  28. data/app/views/_user_util_links.html.erb +2 -2
  29. data/app/views/batch/_metadata.html.erb +82 -0
  30. data/app/views/batch/_more_metadata.html.erb +6 -0
  31. data/app/views/batch/edit.html.erb +1 -8
  32. data/app/views/batch_edits/_check_all.html.erb +0 -157
  33. data/app/views/batch_edits/edit.html.erb +0 -29
  34. data/app/views/catalog/_index_partials/_list_files.html.erb +8 -10
  35. data/app/views/catalog/_recent_document.html.erb +9 -18
  36. data/app/views/catalog/_results_pagination.html.erb +1 -1
  37. data/app/views/contact_form/new.html.erb +1 -1
  38. data/app/views/dashboard/_index_partials/_default_group.html.erb +1 -1
  39. data/app/views/dashboard/_index_partials/_list_files.html.erb +12 -14
  40. data/app/views/dashboard/_index_partials/_thumbnail_display.html.erb +9 -19
  41. data/app/views/dashboard/_results_pagination.html.erb +1 -1
  42. data/app/views/dashboard/index.html.erb +6 -82
  43. data/app/views/error/single_use_error.html.erb +35 -0
  44. data/app/views/generic_files/_descriptions.html.erb +2 -2
  45. data/app/views/generic_files/_extra_fields_modal.html.erb +1 -1
  46. data/app/views/generic_files/_field_form.html.erb +2 -5
  47. data/app/views/generic_files/_media_display.html.erb +8 -6
  48. data/app/views/generic_files/_permission.html.erb +2 -2
  49. data/app/views/generic_files/_rights_modal.html.erb +1 -1
  50. data/app/views/generic_files/_show_actions.html.erb +1 -1
  51. data/app/views/generic_files/_show_details.html.erb +11 -6
  52. data/app/views/generic_files/edit.html.erb +0 -8
  53. data/app/views/generic_files/edit_fields/_type.html.erb +1 -1
  54. data/app/views/generic_files/show.html.erb +5 -8
  55. data/app/views/generic_files/show_fields/_based_near.html.erb +4 -1
  56. data/app/views/generic_files/show_fields/_contributor.html.erb +4 -1
  57. data/app/views/generic_files/show_fields/_creator.html.erb +4 -1
  58. data/app/views/generic_files/show_fields/_date_created.html.erb +4 -1
  59. data/app/views/generic_files/show_fields/_description.html.erb +4 -1
  60. data/app/views/generic_files/show_fields/_language.html.erb +1 -1
  61. data/app/views/generic_files/show_fields/_publisher.html.erb +4 -1
  62. data/app/views/generic_files/show_fields/_related_url.html.erb +3 -1
  63. data/app/views/generic_files/show_fields/_resource_type.html.erb +1 -1
  64. data/app/views/generic_files/show_fields/_subject.html.erb +4 -1
  65. data/app/views/generic_files/show_fields/_tag.html.erb +1 -1
  66. data/app/views/generic_files/show_fields/_title.html.erb +4 -1
  67. data/app/views/layouts/error.html.erb +0 -4
  68. data/app/views/layouts/hydra-head.html.erb +2 -6
  69. data/app/views/single_use_link/show.html.erb +1 -1
  70. data/app/views/users/index.html.erb +1 -1
  71. data/app/views/users/show.html.erb +1 -1
  72. data/config/locales/sufia.en.yml +1 -0
  73. data/config/routes.rb +11 -4
  74. data/lib/generators/sufia/sufia_generator.rb +2 -1
  75. data/lib/generators/sufia/templates/catalog_controller.rb +143 -117
  76. data/lib/generators/sufia/templates/config/resque_admin.rb +10 -0
  77. data/lib/generators/sufia/templates/config/sufia.rb +8 -0
  78. data/lib/sufia.rb +4 -14
  79. data/lib/sufia/batch_edits_controller_behavior.rb +89 -0
  80. data/lib/sufia/controller.rb +7 -5
  81. data/lib/sufia/downloads_controller_behavior.rb +14 -19
  82. data/lib/sufia/file_content/extract_metadata.rb +11 -4
  83. data/lib/sufia/files_controller_behavior.rb +63 -44
  84. data/lib/sufia/generic_file.rb +29 -11
  85. data/lib/sufia/generic_file/audit.rb +1 -1
  86. data/lib/sufia/generic_file/thumbnail.rb +51 -26
  87. data/lib/sufia/id_service.rb +28 -11
  88. data/lib/sufia/jobs/batch_update_job.rb +2 -2
  89. data/lib/sufia/jobs/characterize_job.rb +11 -3
  90. data/lib/sufia/jobs/ffmpeg_transcode_job.rb +61 -0
  91. data/lib/sufia/jobs/resolrize_job.rb +1 -1
  92. data/lib/sufia/jobs/transcode_audio_job.rb +40 -0
  93. data/lib/sufia/jobs/transcode_video_job.rb +9 -49
  94. data/lib/sufia/queue/resque.rb +2 -2
  95. data/lib/sufia/single_use_error.rb +4 -0
  96. data/lib/sufia/solr_document_behavior.rb +108 -1
  97. data/lib/sufia/version.rb +1 -1
  98. data/solr_conf/conf/schema.xml +332 -652
  99. data/solr_conf/conf/solrconfig.xml +60 -196
  100. data/spec/controllers/batch_controller_spec.rb +4 -5
  101. data/spec/controllers/catalog_controller_spec.rb +13 -13
  102. data/spec/controllers/dashboard_controller_spec.rb +2 -2
  103. data/spec/controllers/downloads_controller_spec.rb +74 -62
  104. data/spec/controllers/generic_files_controller_spec.rb +10 -8
  105. data/spec/controllers/single_use_link_controller_spec.rb +12 -4
  106. data/spec/fixtures/Example.ogg +0 -0
  107. data/spec/fixtures/piano_note.wav +0 -0
  108. data/spec/fixtures/sufia_generic_stub.descMeta.txt +1 -1
  109. data/spec/helpers/sufia_helper_spec.rb +12 -0
  110. data/spec/models/characterize_job_spec.rb +89 -0
  111. data/spec/models/checksum_audit_log_spec.rb +1 -0
  112. data/spec/models/event_jobs_spec.rb +9 -9
  113. data/spec/models/file_content_datastream_spec.rb +16 -10
  114. data/spec/models/fits_datastream_spec.rb +2 -8
  115. data/spec/models/generic_file_spec.rb +131 -60
  116. data/spec/models/solr_document_spec.rb +21 -0
  117. data/spec/models/transcode_audio_job_spec.rb +81 -0
  118. data/spec/models/transcode_video_job_spec.rb +2 -2
  119. data/spec/models/unzip_job_spec.rb +3 -3
  120. data/spec/spec_helper.rb +21 -0
  121. data/spec/support/Gemfile +7 -3
  122. data/sufia.gemspec +8 -11
  123. data/tasks/cucumber.rake +1 -2
  124. data/tasks/sufia-dev.rake +13 -2
  125. data/tasks/sufia.rake +1 -1
  126. metadata +77 -118
  127. data/app/views/batch_edits/_metadata.html.erb +0 -180
  128. data/lib/generators/sufia/templates/config/hydra_config.rb +0 -32
  129. data/lib/kaminari/helpers/tag.rb +0 -11
@@ -25,7 +25,7 @@ module Sufia
25
25
  has_metadata :name => "descMetadata", :type => GenericFileRdfDatastream
26
26
  has_metadata :name => "properties", :type => PropertiesDatastream
27
27
  has_file_datastream :name => "content", :type => FileContentDatastream
28
- has_file_datastream :name => "thumbnail", :type => FileContentDatastream
28
+ has_file_datastream :name => "thumbnail"
29
29
 
30
30
  belongs_to :batch, :property => :is_part_of
31
31
 
@@ -36,6 +36,8 @@ module Sufia
36
36
  :publisher, :date_created, :subject,
37
37
  :resource_type, :identifier, :language]
38
38
  around_save :characterize_if_changed, :retry_warming
39
+ before_save :remove_blank_assertions
40
+
39
41
  end
40
42
 
41
43
  def delete
@@ -43,6 +45,12 @@ module Sufia
43
45
  super
44
46
  end
45
47
 
48
+ def remove_blank_assertions
49
+ terms_for_editing.each do |key|
50
+ self[key] = nil if self[key] == ['']
51
+ end
52
+ end
53
+
46
54
 
47
55
  def record_version_committer(user)
48
56
  version = content.latest_version
@@ -55,15 +63,21 @@ module Sufia
55
63
  end
56
64
 
57
65
  def pdf?
58
- ["application/pdf"].include? self.mime_type
66
+ ['application/pdf'].include? self.mime_type
59
67
  end
60
68
 
61
69
  def image?
62
- ["image/png","image/jpeg", 'image/jpg', 'image/bmp', "image/gif"].include? self.mime_type
70
+ ['image/png','image/jpeg', 'image/jpg', 'image/jp2', 'image/bmp', 'image/gif'].include? self.mime_type
63
71
  end
64
72
 
65
73
  def video?
66
- ["video/mpeg", "video/mp4", "video/x-msvideo", "video/avi", "video/quicktime"].include? self.mime_type
74
+ ['video/mpeg', 'video/mp4', 'video/webm', 'video/x-msvideo', 'video/avi', 'video/quicktime', 'application/mxf'].include? self.mime_type
75
+ end
76
+
77
+ def audio?
78
+ # audio/x-wave is the mime type that fits 0.6.0 returns for a wav file.
79
+ # audio/mpeg is the mime type that fits 0.6.0 returns for an mp3 file.
80
+ ['audio/mp3', 'audio/mpeg', 'audio/x-wave', 'audio/x-wav', 'audio/ogg'].include? self.mime_type
67
81
  end
68
82
 
69
83
  def persistent_url
@@ -111,21 +125,25 @@ module Sufia
111
125
  relateds = begin
112
126
  self.batch.generic_files
113
127
  rescue NoMethodError => e
114
- #batch is nil
128
+ #batch is nil - When would this ever happen?
115
129
  batch_id = self.object_relations["isPartOf"].first || self.object_relations[:is_part_of].first
116
130
  return [] if batch_id.nil?
117
- self.class.find(:is_part_of_s => batch_id)
131
+ self.class.find(Solrizer.solr_name('is_part_of', :symbol) => batch_id)
118
132
  end
119
133
  relateds.reject { |gf| gf.pid == self.pid }
120
134
  end
121
135
 
136
+ # Unstemmed, searchable, stored
137
+ def self.noid_indexer
138
+ @noid_indexer ||= Solrizer::Descriptor.new(:text, :indexed, :stored)
139
+ end
122
140
 
123
141
  def to_solr(solr_doc={}, opts={})
124
142
  super(solr_doc, opts)
125
- solr_doc["label_t"] = self.label
126
- solr_doc["noid_s"] = noid
127
- solr_doc["file_format_t"] = file_format
128
- solr_doc["file_format_facet"] = solr_doc["file_format_t"]
143
+ solr_doc[Solrizer.solr_name('label')] = self.label
144
+ solr_doc[Solrizer.solr_name('noid', Sufia::GenericFile.noid_indexer)] = noid
145
+ solr_doc[Solrizer.solr_name('file_format')] = file_format
146
+ solr_doc[Solrizer.solr_name('file_format', :facetable)] = file_format
129
147
  return solr_doc
130
148
  end
131
149
 
@@ -161,7 +179,7 @@ module Sufia
161
179
 
162
180
  def terms_for_editing
163
181
  terms_for_display -
164
- [:part_of, :date_modified, :date_uploaded, :format, :resource_type]
182
+ [:part_of, :date_modified, :date_uploaded, :format] #, :resource_type]
165
183
  end
166
184
 
167
185
  def terms_for_display
@@ -89,7 +89,7 @@ module Sufia
89
89
  end
90
90
 
91
91
  def audit_everything(force = false)
92
- ::GenericFile.find(:all, :rows => ::GenericFile.count).each do |gf|
92
+ ::GenericFile.find_each do |gf|
93
93
  gf.per_version do |ver|
94
94
  ::GenericFile.audit(ver, force)
95
95
  end
@@ -4,29 +4,46 @@ module Sufia
4
4
  # Create thumbnail requires that the characterization has already been run (so mime_type, width and height is available)
5
5
  # and that the object is already has a pid set
6
6
  def create_thumbnail
7
- return if self.content.content.nil?
7
+ return unless self.content.has_content?
8
8
  if pdf?
9
9
  create_pdf_thumbnail
10
10
  elsif image?
11
11
  create_image_thumbnail
12
- # elsif video?
13
- # create_video_thumbnail
12
+ elsif video?
13
+ create_video_thumbnail
14
14
  end
15
15
  end
16
16
 
17
+ protected
18
+ def create_video_thumbnail
19
+ return unless Sufia::Engine.config.enable_ffmpeg
20
+
21
+ output_file = Dir::Tmpname.create(['sufia', ".png"], Sufia::Engine.config.temp_file_base){}
22
+ content.to_tempfile do |f|
23
+ # we could use something like this in order to find a frame in the middle.
24
+ #ffprobe -show_files video.avi 2> /dev/null | grep duration | cut -d= -f2 53.399999
25
+ command = "#{Sufia::Engine.config.ffmpeg_path} -i \"#{f.path}\" -loglevel quiet -vf \"scale=338:-1\" -r 1 -t 1 #{output_file}"
26
+ system(command)
27
+ raise "Unable to execute command \"#{command}\"" unless $?.success?
28
+ end
29
+
30
+ self.thumbnail.content = File.open(output_file, 'rb').read
31
+ self.thumbnail.mimeType = 'image/png'
32
+ self.save
33
+ end
34
+
17
35
  def create_pdf_thumbnail
18
36
  retryCnt = 0
19
37
  stat = false;
20
38
  for retryCnt in 1..3
21
39
  begin
22
- pdf = Magick::ImageList.new
23
- pdf.from_blob(content.content)
40
+ pdf = load_image_transformer
24
41
  first = pdf.to_a[0]
25
42
  first.format = "PNG"
26
43
  thumb = first.scale(338, 493)
27
44
  self.thumbnail.content = thumb.to_blob { self.format = "PNG" }
28
- #logger.debug "Has the content changed before saving? #{self.content.changed?}"
29
- stat = self.save
45
+ self.thumbnail.mimeType = 'image/png'
46
+ self.save
30
47
  break
31
48
  rescue => e
32
49
  logger.warn "Rescued an error #{e.inspect} retry count = #{retryCnt}"
@@ -37,30 +54,38 @@ module Sufia
37
54
  end
38
55
 
39
56
  def create_image_thumbnail
40
- img = Magick::ImageList.new
41
- img.from_blob(content.content)
42
- # horizontal img
57
+ self.thumbnail.content = scale_image.to_blob { self.format = "PNG" }
58
+ self.thumbnail.mimeType = 'image/png'
59
+ #logger.debug "Has the content before saving? #{self.content.changed?}"
60
+ self.save
61
+ end
62
+
63
+ def scale_image
64
+ img = load_image_transformer
43
65
  height = Float(self.height.first.to_i)
44
66
  width = Float(self.width.first.to_i)
45
- scale = height / width
46
- if width > height
47
- if width > 150 and height > 105
48
- thumb = img.scale(150, height/scale)
49
- else
50
- thumb = img.scale(width, height)
51
- end
52
- # vertical img
67
+ if width > height && width > 150 && height > 105
68
+ # horizontal img
69
+ scale = 150 / width
70
+ img.scale(150, height * scale)
71
+ elsif height >= width && width > 150 && height > 200
72
+ # vertical or square
73
+ scale = 200 / height
74
+ img.scale(width*scale, 200)
53
75
  else
54
- if width > 150 and height > 200
55
- thumb = img.scale(150*scale, 200)
56
- else
57
- thumb = img.scale(width, height)
58
- end
76
+ # Too small to worry about resizing
77
+ img
59
78
  end
60
- self.thumbnail.content = thumb.to_blob
61
- #logger.debug "Has the content before saving? #{self.content.changed?}"
62
- self.save
63
79
  end
80
+
81
+ # Override this method if you want a different transformer, or need to load the
82
+ # raw image from a different source (e.g. external datastream)
83
+ def load_image_transformer
84
+ xformer = Magick::ImageList.new
85
+ xformer.from_blob(content.content)
86
+ xformer
87
+ end
88
+
64
89
  end
65
90
  end
66
91
  end
@@ -15,26 +15,43 @@
15
15
  require 'noid'
16
16
 
17
17
  module Sufia
18
- class IdService
19
- @@minter = ::Noid::Minter.new(:template => '.reeddeeddk')
20
- @@namespace = Sufia::Engine.config.id_namespace
18
+ module IdService
19
+ @minter = ::Noid::Minter.new(:template => '.reeddeeddk')
20
+ @pid = $$
21
+ @namespace = Sufia::Engine.config.id_namespace
22
+ @semaphore = Mutex.new
21
23
  def self.valid?(identifier)
22
24
  # remove the fedora namespace since it's not part of the noid
23
25
  noid = identifier.split(":").last
24
- return @@minter.valid? noid
26
+ return @minter.valid? noid
25
27
  end
26
28
  def self.mint
27
- while true
28
- pid = self.next_id
29
- break unless ActiveFedora::Base.exists?(pid)
29
+ @semaphore.synchronize do
30
+ while true
31
+ pid = self.next_id
32
+ return pid unless ActiveFedora::Base.exists?(pid)
33
+ end
30
34
  end
31
- return pid
32
35
  end
36
+
33
37
  protected
38
+
34
39
  def self.next_id
35
- # seed with process id so that if two processes are running they do not come up with the same id.
36
- @@minter.seed($$)
37
- return "#{@@namespace}:#{@@minter.mint}"
40
+ pid = ''
41
+ File.open("tmp/minter-state", File::RDWR|File::CREAT, 0644) {|f|
42
+ f.flock(File::LOCK_EX)
43
+ yaml = YAML::load(f.read)
44
+ yaml = {:template => '.reeddeeddk'} unless yaml
45
+ minter = ::Noid::Minter.new(yaml)
46
+ pid = "#{@namespace}:#{minter.mint}"
47
+ f.rewind
48
+ yaml = YAML::dump(minter.dump)
49
+ f.write yaml
50
+ f.flush
51
+ f.truncate(f.pos)
52
+ }
53
+ return pid
38
54
  end
55
+
39
56
  end
40
57
  end
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  class BatchUpdateJob
16
- include Hydra::AccessControlsEnforcement
16
+ include Hydra::PermissionsQuery
17
17
  include GenericFileHelper
18
18
  include Rails.application.routes.url_helpers
19
19
 
@@ -55,7 +55,7 @@ class BatchUpdateJob
55
55
 
56
56
  def update_file(gf, user)
57
57
  unless user.can? :edit, gf
58
- logger.error "User #{user.user_key} DEEEENIED access to #{gf.pid}!"
58
+ logger.error "User #{user.user_key} DENIED access to #{gf.pid}!"
59
59
  @denied << gf
60
60
  return
61
61
  end
@@ -18,18 +18,26 @@ class CharacterizeJob
18
18
  :characterize
19
19
  end
20
20
 
21
- attr_accessor :generic_file_id
21
+ attr_accessor :generic_file_id, :generic_file
22
22
 
23
23
  def initialize(generic_file_id)
24
24
  self.generic_file_id = generic_file_id
25
25
  end
26
26
 
27
27
  def run
28
- generic_file = GenericFile.find(generic_file_id)
28
+ self.generic_file = GenericFile.find(generic_file_id)
29
29
  generic_file.characterize
30
- generic_file.create_thumbnail
30
+ after_characterize
31
+ end
32
+
33
+ def after_characterize
34
+ if generic_file.pdf? || generic_file.image? || generic_file.video?
35
+ generic_file.create_thumbnail
36
+ end
31
37
  if generic_file.video?
32
38
  Sufia.queue.push(TranscodeVideoJob.new(generic_file_id, 'content'))
39
+ elsif generic_file.audio?
40
+ Sufia.queue.push(TranscodeAudioJob.new(generic_file_id, 'content'))
33
41
  end
34
42
  end
35
43
  end
@@ -0,0 +1,61 @@
1
+ # Created by: Justin Coyne
2
+ # 7 Feb 2013
3
+ # An abstract class for asyncronous jobs that transcode files using FFMpeg
4
+
5
+ require 'tmpdir'
6
+
7
+ class FfmpegTranscodeJob
8
+ extend Open3
9
+
10
+ attr_accessor :generic_file_id, :datastream_in, :datastream, :generic_file
11
+
12
+ def initialize(generic_file_id, datastream_in)
13
+ self.generic_file_id = generic_file_id
14
+ self.datastream_in = datastream_in
15
+ end
16
+
17
+ def process
18
+ raise "You attempted to call process() on an abstract class. Implement process() on the concrete class"
19
+ end
20
+
21
+ def run
22
+ return unless Sufia::Engine.config.enable_ffmpeg
23
+ self.generic_file = GenericFile.find(generic_file_id)
24
+ self.datastream = generic_file.datastreams[datastream_in]
25
+ if datastream
26
+ process
27
+ generic_file.save!
28
+ else
29
+ logger.warn "No datastream for transcoding!!!!! pid: #{generic_file_id} dsid: #{datastream_in}"
30
+ end
31
+ end
32
+
33
+ def encode_datastream(dest_dsid, mime_type, options)
34
+ file_suffix = dest_dsid
35
+ out_file = nil
36
+ output_file = Dir::Tmpname.create(['sufia', ".#{file_suffix}"], Sufia::Engine.config.temp_file_base){}
37
+ datastream.to_tempfile do |f|
38
+ self.class.encode(f.path, options, output_file)
39
+ end
40
+ out_file = File.open(output_file, "rb")
41
+ generic_file.add_file_datastream(out_file.read, :dsid=>dest_dsid, :mimeType=>mime_type)
42
+ File.unlink(output_file)
43
+ end
44
+
45
+ def self.encode(path, options, output_file)
46
+ command = "#{ffmpeg_path} -y -i \"#{path}\" #{options} #{output_file}"
47
+ stdin, stdout, stderr, wait_thr = popen3(command)
48
+ stdin.close
49
+ out = stdout.read
50
+ stdout.close
51
+ err = stderr.read
52
+ stderr.close
53
+ raise "Unable to execute command \"#{command}\"\n#{err}" unless wait_thr.value.success?
54
+ end
55
+
56
+ def self.ffmpeg_path
57
+ Sufia::Engine.config.ffmpeg_path
58
+ end
59
+ end
60
+
61
+
@@ -18,6 +18,6 @@ class ResolrizeJob
18
18
  end
19
19
 
20
20
  def run
21
- Solrizer::Fedora::Solrizer.new.solrize_objects(:suppress_errors => false)
21
+ ActiveFedora::Base.reindex_everything
22
22
  end
23
23
  end
@@ -0,0 +1,40 @@
1
+ # Created by: Justin Coyne
2
+ # 7 Feb 2013
3
+ # An asyncronous job for transcoding audio files using FFMpeg
4
+
5
+ class TranscodeAudioJob < FfmpegTranscodeJob
6
+ def queue_name
7
+ :audio
8
+ end
9
+
10
+ def process
11
+ encode_mp3()
12
+ encode_ogg()
13
+ end
14
+
15
+ private
16
+ def encode_ogg
17
+ opts = ""
18
+ if generic_file.mime_type == 'audio/ogg'
19
+ # Don't re-encode, just copy
20
+ generic_file.add_file_datastream(generic_file.content.read, :dsid=>'ogg', :mimeType=>'audio/ogg')
21
+ #generic_file.content.rewind
22
+ else
23
+ encode_datastream('ogg', 'audio/ogg', opts)
24
+ end
25
+ end
26
+
27
+ def encode_mp3
28
+ opts = ""
29
+ if generic_file.mime_type == 'audio/mpeg'
30
+ # Don't re-encode, just copy
31
+ generic_file.add_file_datastream(generic_file.content.read, :dsid=>'mp3', :mimeType=>'audio/mp3')
32
+ #generic_file.content.rewind
33
+ else
34
+ encode_datastream('mp3', 'audio/mp3', opts)
35
+ end
36
+ end
37
+
38
+
39
+ end
40
+
@@ -2,35 +2,23 @@
2
2
  # 13 Dec 2012
3
3
  # An asyncronous job for transcoding video files using FFMpeg
4
4
 
5
- require 'tmpdir'
6
-
7
- class TranscodeVideoJob
8
- extend Open3
5
+ class TranscodeVideoJob < FfmpegTranscodeJob
9
6
  def queue_name
10
7
  :video
11
8
  end
12
9
 
13
- attr_accessor :generic_file_id, :datastream_in
14
-
15
- def initialize(generic_file_id, datastream_in)
16
- self.generic_file_id = generic_file_id
17
- self.datastream_in = datastream_in
10
+ def process
11
+ encode_mp4()
12
+ encode_webm()
18
13
  end
19
14
 
20
- def run
21
- @generic_file = GenericFile.find(generic_file_id)
22
- @datastream = @generic_file.datastreams[datastream_in]
23
- if @datastream
24
- encode_mp4()
25
- encode_webm()
26
- @generic_file.save!
27
- else
28
- logger.warn "No datastream for transcoding!!!!! pid: #{generic_file_id} dsid: #{datastream_in}"
29
- end
30
- end
15
+ private
31
16
 
32
17
  def encode_webm
33
- opts = "#{size_attributes} -b:v 345k -acodec libvorbis #{audio_attributes}"
18
+ # -g 30 enforces keyframe generation every second (30fps)
19
+ # -b:v is the video bitrate
20
+ # -acodec is the audio codec
21
+ opts = "#{size_attributes} -g 30 -b:v 345k -acodec libvorbis #{audio_attributes}"
34
22
  encode_datastream('webm', 'video/webm', opts)
35
23
  end
36
24
 
@@ -47,33 +35,5 @@ class TranscodeVideoJob
47
35
  def audio_attributes
48
36
  "-ac 2 -ab 96k -ar 44100"
49
37
  end
50
-
51
- def encode_datastream(dest_dsid, mime_type, options)
52
- file_suffix = dest_dsid
53
- out_file = nil
54
- output_file = Dir::Tmpname.create('sufia'){} + ".#{file_suffix}"
55
- @datastream.to_tempfile do |f|
56
- self.class.encode(f.path, options, output_file)
57
- end
58
- out_file = File.open(output_file, "rb")
59
- @generic_file.add_file_datastream(out_file.read, :dsid=>dest_dsid, :mimeType=>mime_type)
60
- File.unlink(output_file)
61
- end
62
-
63
- # TODO tmp file for output
64
- def self.encode(path, options, output_file)
65
- command = "#{ffmpeg_path} -y -i #{path} #{options} #{output_file}"
66
- stdin, stdout, stderr, wait_thr = popen3(command)
67
- stdin.close
68
- out = stdout.read
69
- stdout.close
70
- err = stderr.read
71
- stderr.close
72
- raise "Unable to execute command \"#{command}\"\n#{err}" unless wait_thr.value.success?
73
- end
74
-
75
- def self.ffmpeg_path
76
- Sufia::Engine.config.ffmpeg_path
77
- end
78
38
  end
79
39