active_encode 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ If you would like to report an issue, first search [the list of issues](https://github.com/samvera-labs/active_encode/issues/) to see if someone else has already reported it, and then feel free to [create a new issue](https://github.com/samvera-labs/active_encode/issues/new).
2
+
3
+ If you have questions or need help, please email [the Samvera community tech list](https://groups.google.com/forum/#!forum/samvera-tech) or stop by the #dev channel in [the Samvera community Slack team](https://wiki.duraspace.org/pages/viewpage.action?pageId=87460391#Getintouch!-Slack).
4
+
5
+ You can learn more about the various Samvera communication channels on the [Get in touch!](https://wiki.duraspace.org/pages/viewpage.action?pageId=87460391) wiki page.
@@ -21,10 +21,11 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.add_dependency "rails"
23
23
 
24
+ spec.add_development_dependency "aws-sdk"
24
25
  spec.add_development_dependency "bundler"
25
26
  spec.add_development_dependency "coveralls"
26
27
  spec.add_development_dependency "database_cleaner"
27
- spec.add_development_dependency "engine_cart"
28
+ spec.add_development_dependency "engine_cart", "~> 2.2"
28
29
  spec.add_development_dependency "rake"
29
30
  spec.add_development_dependency "rspec"
30
31
  spec.add_development_dependency "rspec-its"
@@ -0,0 +1,12 @@
1
+ module ActiveEncode
2
+ class EncodeRecordController < ActionController::Base
3
+ rescue_from ActiveRecord::RecordNotFound do |e|
4
+ render json: { message: e.message }, status: :not_found
5
+ end
6
+
7
+ def show
8
+ @encode_record = ActiveEncode::EncodeRecord.find(params[:id])
9
+ render json: @encode_record.raw_object, status: :ok
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ ActiveEncode::Engine.routes.draw do
3
+ resources :encode_record, only: [:show]
4
+ end
@@ -1,12 +1,27 @@
1
+ require 'addressable/uri'
2
+ require 'aws-sdk'
3
+ require 'file_locator'
4
+
1
5
  module ActiveEncode
2
6
  module EngineAdapters
3
7
  class ElasticTranscoderAdapter
4
- # TODO: add a stub for an input helper (supplied by an initializer) that transforms encode.input into a zencoder accepted url
8
+
9
+ JOB_STATES = {
10
+ "Submitted" => :running, "Progressing" => :running, "Canceled" => :cancelled,
11
+ "Error" => :failed, "Complete" => :completed
12
+ }
13
+
14
+ # Require options to include :pipeline_id, :masterfile_bucket and :outputs
15
+ # Example :outputs value:
16
+ # [{ key: "quality-low/hls/fireworks", preset_id: '1494429796844-aza6zh', segment_duration: '2' },
17
+ # { key: "quality-medium/hls/fireworks", preset_id: '1494429797061-kvg9ki', segment_duration: '2' },
18
+ # { key: "quality-high/hls/fireworks", preset_id: '1494429797265-9xi831', segment_duration: '2' }]
5
19
  def create(input_url, options = {})
20
+ s3_key = copy_to_input_bucket input_url, options[:masterfile_bucket]
6
21
  job = client.create_job(
7
- input: { key: input_url },
22
+ input: { key: s3_key },
8
23
  pipeline_id: options[:pipeline_id],
9
- output_key_prefix: options[:output_key_prefix],
24
+ output_key_prefix: options[:output_key_prefix] || "#{SecureRandom.uuid}/",
10
25
  outputs: options[:outputs],
11
26
  user_metadata: options[:user_metadata]
12
27
  ).job
@@ -31,28 +46,33 @@ module ActiveEncode
31
46
  @client ||= Aws::ElasticTranscoder::Client.new
32
47
  end
33
48
 
49
+ def s3client
50
+ Aws::S3::Client.new
51
+ end
52
+
34
53
  def get_job_details(job_id)
35
- client.read_job(id: job_id).job
54
+ client.read_job(id: job_id)&.job
36
55
  end
37
56
 
38
57
  def build_encode(job)
39
58
  return nil if job.nil?
40
- encode = ActiveEncode::Base.new(convert_input(job), convert_options(job))
59
+ encode = ActiveEncode::Base.new(convert_input(job), {})
41
60
  encode.id = job.id
42
- encode.state = convert_state(job)
43
- encode.current_operations = convert_current_operations(job)
44
- encode.percent_complete = convert_percent_complete(job)
45
- encode.created_at = convert_time(job.timing["submit_time_millis"])
46
- encode.updated_at = convert_time(job.timing["finish_time_millis"] || job.timing["start_time_millis"]) || encode.created_at
47
- encode.output = convert_output(job)
48
- encode.errors = convert_errors(job)
49
-
50
- encode.input.id = job.input.key
61
+ encode.state = JOB_STATES[job.status]
62
+ encode.current_operations = []
63
+ encode.percent_complete = convert_percent_complete(job)
64
+ encode.created_at = convert_time(job.timing["submit_time_millis"])
65
+ encode.updated_at = convert_time(job.timing["finish_time_millis"]) || convert_time(job.timing["start_time_millis"]) || encode.created_at
66
+
67
+ encode.output = convert_output(job)
68
+ encode.errors = job.outputs.select { |o| o.status == "Error" }.collect(&:status_detail).compact
69
+
51
70
  tech_md = convert_tech_metadata(job.input.detected_properties)
52
- [:width, :height, :frame_rate, :duration, :checksum, :audio_codec, :video_codec,
53
- :audio_bitrate, :video_bitrate, :file_size].each do |field|
71
+ [:width, :height, :frame_rate, :duration, :file_size].each do |field|
54
72
  encode.input.send("#{field}=", tech_md[field])
55
73
  end
74
+
75
+ encode.input.id = job.id
56
76
  encode.input.state = encode.state
57
77
  encode.input.created_at = encode.created_at
58
78
  encode.input.updated_at = encode.updated_at
@@ -65,6 +85,11 @@ module ActiveEncode
65
85
  Time.at(time_millis / 1000)
66
86
  end
67
87
 
88
+ def convert_bitrate(rate)
89
+ return nil if rate.nil?
90
+ (rate.to_f * 1024).to_s
91
+ end
92
+
68
93
  def convert_state(job)
69
94
  case job.status
70
95
  when "Submitted", "Progressing" # Should there be a queued state?
@@ -78,13 +103,12 @@ module ActiveEncode
78
103
  end
79
104
  end
80
105
 
81
- def convert_current_operations(_job)
82
- current_ops = []
83
- current_ops
106
+ def convert_percent_complete(job)
107
+ job.outputs.inject(0) { |sum, output| sum + output_percentage(output) } / job.outputs.length
84
108
  end
85
109
 
86
- def convert_percent_complete(job)
87
- case job.status
110
+ def output_percentage(output)
111
+ case output.status
88
112
  when "Submitted"
89
113
  10
90
114
  when "Progressing", "Canceled", "Error"
@@ -100,29 +124,67 @@ module ActiveEncode
100
124
  job.input
101
125
  end
102
126
 
103
- def convert_options(_job_details)
104
- {}
127
+ def copy_to_input_bucket input_url, bucket
128
+ case Addressable::URI.parse(input_url).scheme
129
+ when nil,'file'
130
+ upload_to_s3 input_url, bucket
131
+ when 's3'
132
+ check_s3_bucket input_url, bucket
133
+ end
134
+ end
135
+
136
+ def check_s3_bucket input_url, source_bucket
137
+ # logger.info("Checking `#{input_url}'")
138
+ s3_object = FileLocator::S3File.new(input_url).object
139
+ if s3_object.bucket_name == source_bucket
140
+ # logger.info("Already in bucket `#{source_bucket}'")
141
+ s3_object.key
142
+ else
143
+ s3_key = File.join(SecureRandom.uuid,s3_object.key)
144
+ # logger.info("Copying to `#{source_bucket}/#{input_url}'")
145
+ target = Aws::S3::Object.new(bucket_name: source_bucket, key: input_url)
146
+ target.copy_from(s3_object, multipart_copy: s3_object.size > 15728640) # 15.megabytes
147
+ s3_key
148
+ end
149
+ end
150
+
151
+ def upload_to_s3 input_url, source_bucket
152
+ original_input = input_url
153
+ bucket = Aws::S3::Resource.new(client: s3client).bucket(source_bucket)
154
+ filename = FileLocator.new(input_url).location
155
+ s3_key = File.join(SecureRandom.uuid,File.basename(filename))
156
+ # logger.info("Copying `#{original_input}' to `#{source_bucket}/#{input_url}'")
157
+ obj = bucket.object(s3_key)
158
+ obj.upload_file filename
159
+
160
+ s3_key
161
+ end
162
+
163
+ def read_preset(id)
164
+ client.read_preset(id: id).preset
105
165
  end
106
166
 
107
167
  def convert_output(job)
108
- job.outputs.collect do |o|
109
- # It is assumed that the first part of the output key can be used to label the output
110
- # e.g. "quality-medium/somepath/filename.flv"
168
+ pipeline = client.read_pipeline(id: job.pipeline_id).pipeline
169
+ job.outputs.collect do |joutput|
170
+ preset = read_preset(joutput.preset_id)
171
+ extension = preset.container == 'ts' ? '.m3u8' : ''
172
+ tech_md = convert_tech_metadata(joutput, preset).merge({
173
+ managed: false,
174
+ id: joutput.id,
175
+ label: joutput.key.split("/", 2).first,
176
+ url: "s3://#{pipeline.output_bucket}/#{job.output_key_prefix}#{joutput.key}#{extension}"
177
+ })
178
+
111
179
  output = ActiveEncode::Output.new
112
- output.id = o.id
113
- output.label = o.key.split("/", 2).first
114
- output.url = job.output_key_prefix + o.key
115
- # TODO: If HLS is considered distinct from this output then it should be a different output
116
- # TODO: If HLS is not considered distinct from this output then this should be handled by a method on a ActiveEncode::Base subclass or consuming client
117
- # extras[:hls_url] = url + ".m3u8" if url.include?("/hls/") # TODO: find a better way to signal hls
118
- tech_md = convert_tech_metadata(o)
180
+ output.state = convert_state(joutput)
181
+ output.created_at = convert_time(job.timing["submit_time_millis"])
182
+ output.updated_at = convert_time(job.timing["finish_time_millis"] || job.timing["start_time_millis"]) || output.created_at
183
+
119
184
  [:width, :height, :frame_rate, :duration, :checksum, :audio_codec, :video_codec,
120
- :audio_bitrate, :video_bitrate, :file_size].each do |field|
185
+ :audio_bitrate, :video_bitrate, :file_size, :label, :url, :id].each do |field|
121
186
  output.send("#{field}=", tech_md[field])
122
187
  end
123
- output.state = convert_state(o)
124
- output.created_at = convert_time(job.timing["submit_time_millis"])
125
- output.updated_at = convert_time(job.timing["finish_time_millis"] || job.timing["start_time_millis"]) || output.created_at
126
188
 
127
189
  output
128
190
  end
@@ -132,12 +194,13 @@ module ActiveEncode
132
194
  job.outputs.select { |o| o.status == "Error" }.collect(&:status_detail).compact
133
195
  end
134
196
 
135
- def convert_tech_metadata(props)
136
- return {} if props.blank?
197
+ def convert_tech_metadata(props, preset=nil)
198
+ return {} if props.nil? || props.empty?
137
199
  metadata_fields = {
138
200
  file_size: { key: :file_size, method: :itself },
139
201
  duration_millis: { key: :duration, method: :to_i },
140
202
  frame_rate: { key: :frame_rate, method: :to_i },
203
+ segment_duration: { key: :segment_duration, method: :itself },
141
204
  width: { key: :width, method: :itself },
142
205
  height: { key: :height, method: :itself }
143
206
  }
@@ -149,6 +212,19 @@ module ActiveEncode
149
212
  next if conversion.nil?
150
213
  metadata[conversion[:key]] = value.send(conversion[:method])
151
214
  end
215
+
216
+ unless preset.nil?
217
+ audio = preset.audio
218
+ video = preset.video
219
+ metadata.merge!({
220
+ audio_codec: audio&.codec,
221
+ audio_channels: audio&.channels,
222
+ audio_bitrate: convert_bitrate(audio&.bit_rate),
223
+ video_codec: video&.codec,
224
+ video_bitrate: convert_bitrate(video&.bit_rate)
225
+ })
226
+ end
227
+
152
228
  metadata
153
229
  end
154
230
  end
@@ -191,7 +191,8 @@ private
191
191
  if data.blank?
192
192
  1
193
193
  else
194
- (progress_value("out_time_ms=", data).to_i * 0.0001 / encode.input.duration).round
194
+ progress_in_milliseconds = progress_value("out_time_ms=", data).to_i / 1000.0
195
+ (progress_in_milliseconds / encode.input.duration * 100).round
195
196
  end
196
197
  end
197
198
 
@@ -219,11 +220,13 @@ private
219
220
  def get_tech_metadata file_path
220
221
  doc = Nokogiri::XML File.read(file_path)
221
222
  doc.remove_namespaces!
223
+ duration = get_xpath_text(doc, '//Duration/text()', :to_f)
224
+ duration = duration * 1000 unless duration.nil? # Convert to milliseconds
222
225
  { url: get_xpath_text(doc, '//media/@ref', :to_s),
223
226
  width: get_xpath_text(doc, '//Width/text()', :to_f),
224
227
  height: get_xpath_text(doc, '//Height/text()', :to_f),
225
228
  frame_rate: get_xpath_text(doc, '//FrameRate/text()', :to_f),
226
- duration: get_xpath_text(doc, '//Duration/text()', :to_f),
229
+ duration: duration,
227
230
  file_size: get_xpath_text(doc, '//FileSize/text()', :to_i),
228
231
  audio_codec: get_xpath_text(doc, '//track[@type="Audio"]/CodecID/text()', :to_s),
229
232
  audio_bitrate: get_xpath_text(doc, '//track[@type="Audio"]/BitRate/text()', :to_i),
@@ -1,3 +1,3 @@
1
1
  module ActiveEncode
2
- VERSION = '0.4.1'.freeze
2
+ VERSION = '0.5.0'.freeze
3
3
  end
@@ -0,0 +1,94 @@
1
+ require 'addressable/uri'
2
+ require 'aws-sdk'
3
+
4
+ class FileLocator
5
+ attr_reader :source
6
+
7
+ class S3File
8
+ attr_reader :bucket, :key
9
+
10
+ def initialize(uri)
11
+ uri = Addressable::URI.parse(uri)
12
+ @bucket = URI.decode(uri.host)
13
+ @key = URI.decode(uri.path).sub(%r(^/*(.+)/*$),'\1')
14
+ end
15
+
16
+ def object
17
+ @object ||= Aws::S3::Object.new(bucket_name: bucket, key: key)
18
+ end
19
+ end
20
+
21
+ def initialize(source)
22
+ @source = source
23
+ end
24
+
25
+ def uri
26
+ if @uri.nil?
27
+ if source.is_a? File
28
+ @uri = Addressable::URI.parse("file://#{URI.encode(File.expand_path(source))}")
29
+ else
30
+ encoded_source = source
31
+ begin
32
+ @uri = Addressable::URI.parse(encoded_source)
33
+ rescue URI::InvalidURIError
34
+ if encoded_source == source
35
+ encoded_source = URI.encode(encoded_source)
36
+ retry
37
+ else
38
+ raise
39
+ end
40
+ end
41
+
42
+ if @uri.scheme.nil?
43
+ @uri = Addressable::URI.parse("file://#{URI.encode(File.expand_path(source))}")
44
+ end
45
+ end
46
+ end
47
+ @uri
48
+ end
49
+
50
+ def location
51
+ case uri.scheme
52
+ when 's3'
53
+ S3File.new(uri).object.presigned_url(:get)
54
+ when 'file'
55
+ URI.decode(uri.path)
56
+ else
57
+ @uri.to_s
58
+ end
59
+ end
60
+
61
+ def exist?
62
+ case uri.scheme
63
+ when 's3'
64
+ S3File.new(uri).object.exists?
65
+ when 'file'
66
+ File.exist?(location)
67
+ else
68
+ false
69
+ end
70
+ end
71
+ alias_method :exists?, :exist?
72
+
73
+ def reader
74
+ case uri.scheme
75
+ when 's3'
76
+ S3File.new(uri).object.get.body
77
+ when 'file'
78
+ File.open(location,'r')
79
+ else
80
+ Kernel::open(uri.to_s, 'r')
81
+ end
82
+ end
83
+
84
+ def attachment
85
+ case uri.scheme
86
+ when 's3'
87
+ uri
88
+ when 'file'
89
+ File.open(location,'r')
90
+ else
91
+ location
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,52 @@
1
+ require 'rails_helper'
2
+
3
+ describe ActiveEncode::EncodeRecordController, type: :controller, db_clean: true do
4
+ routes { ActiveEncode::Engine.routes }
5
+
6
+ let(:encode_record) { ActiveEncode::EncodeRecord.create(id: 1, global_id: "app://ActiveEncode/Encode/1", state: "running", adapter: "ffmpeg", title: "Test", raw_object: raw_object)}
7
+ let(:raw_object) do
8
+ "{\"input\":{\"url\":\"file:///Users/cjcolvar/Documents/Code/samvera/active_encode/spec/fixtures/fireworks.mp4\",\"width\":960.0,\"height\":540.0,\"frame_rate\":29.671,\"duration\":6024,\"file_size\":1629578,\"audio_codec\":\"mp4a-40-2\",\"video_codec\":\"avc1\",\"audio_bitrate\":69737,\"video_bitrate\":2092780,\"created_at\":\"2018-12-17T16:54:50.401-05:00\",\"updated_at\":\"2018-12-17T16:54:50.401-05:00\",\"id\":\"8156\"},\"options\":{},\"id\":\"35efa965-ec51-409d-9495-2ae9669adbcc\",\"output\":[{\"url\":\"file:///Users/cjcolvar/Documents/Code/samvera/active_encode/.internal_test_app/encodes/35efa965-ec51-409d-9495-2ae9669adbcc/outputs/fireworks-low.mp4\",\"label\":\"low\",\"id\":\"8156-low\",\"created_at\":\"2018-12-17T16:54:50.401-05:00\",\"updated_at\":\"2018-12-17T16:54:59.169-05:00\",\"width\":640.0,\"height\":480.0,\"frame_rate\":29.671,\"duration\":6038,\"file_size\":905987,\"audio_codec\":\"mp4a-40-2\",\"video_codec\":\"avc1\",\"audio_bitrate\":72000,\"video_bitrate\":1126859},{\"url\":\"file:///Users/cjcolvar/Documents/Code/samvera/active_encode/.internal_test_app/encodes/35efa965-ec51-409d-9495-2ae9669adbcc/outputs/fireworks-high.mp4\",\"label\":\"high\",\"id\":\"8156-high\",\"created_at\":\"2018-12-17T16:54:50.401-05:00\",\"updated_at\":\"2018-12-17T16:54:59.169-05:00\",\"width\":1280.0,\"height\":720.0,\"frame_rate\":29.671,\"duration\":6038,\"file_size\":2102027,\"audio_codec\":\"mp4a-40-2\",\"video_codec\":\"avc1\",\"audio_bitrate\":72000,\"video_bitrate\":2721866}],\"state\":\"completed\",\"errors\":[],\"created_at\":\"2018-12-17T16:54:50.401-05:00\",\"updated_at\":\"2018-12-17T16:54:59.169-05:00\",\"current_operations\":[],\"percent_complete\":100,\"global_id\":{\"uri\":\"gid://ActiveEncode/Encode/35efa965-ec51-409d-9495-2ae9669adbcc\"}}"
9
+ end
10
+
11
+ before do
12
+ encode_record
13
+ end
14
+
15
+ describe 'GET show' do
16
+ before do
17
+ get :show, params: { id: record_id }
18
+ end
19
+
20
+ context 'when record exists' do
21
+ let(:record_id) { 1 }
22
+
23
+ it "responds with a 200 status code" do
24
+ expect(response.status).to eq 200
25
+ end
26
+
27
+ it "responds with JSON" do
28
+ expect(response.content_type).to eq "application/json"
29
+ end
30
+
31
+ it "returns the encode record's raw json object" do
32
+ expect(response.body).to eq raw_object
33
+ end
34
+ end
35
+
36
+ context 'when record does not exist' do
37
+ let(:record_id) { "non-existant" }
38
+
39
+ it "responds with a 404 status code" do
40
+ expect(response.status).to eq 404
41
+ end
42
+
43
+ it "responds with JSON" do
44
+ expect(response.content_type).to eq "application/json"
45
+ end
46
+
47
+ it "returns the encode record's raw json object" do
48
+ expect(response.body).to eq "{\"message\":\"Couldn't find ActiveEncode::EncodeRecord with 'id'=#{record_id}\"}"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -12,9 +12,11 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
12
12
  end
13
13
 
14
14
  let(:client) { Aws::ElasticTranscoder::Client.new(stub_responses: true) }
15
+ let(:s3client) { Aws::S3::Client.new(stub_responses: true) }
15
16
 
16
17
  before do
17
18
  allow(Aws::ElasticTranscoder::Client).to receive(:new).and_return(client)
19
+ allow(Aws::S3::Client).to receive(:new).and_return(s3client)
18
20
  end
19
21
 
20
22
  let(:created_job) do
@@ -25,14 +27,15 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
25
27
  client.stub_responses(:create_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
26
28
 
27
29
  ActiveEncode::Base.create(
28
- "somefile.mp4",
30
+ "spec/fixtures/fireworks.mp4",
29
31
  pipeline_id: "1471963629141-kmcocm",
30
- output_key_prefix: "elastic-transcoder-samples/output/hls/",
31
- outputs: [{
32
- key: 'hls0400k/' + "e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a",
33
- preset_id: "1351620000001-200050",
34
- segment_duration: "2"
35
- }])
32
+ masterfile_bucket: "BucketName",
33
+ output_key_prefix: "elastic-transcoder-samples/output/hls/",
34
+ outputs: [{
35
+ key: 'hls0400k/' + "e8fe80f5bsomefilesource_bucket7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a",
36
+ preset_id: "1351620000001-200050",
37
+ segment_duration: "2",
38
+ }])
36
39
  end
37
40
 
38
41
  let(:running_job) do
@@ -90,14 +93,14 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
90
93
  ActiveEncode::Base.find('failed-id')
91
94
  end
92
95
 
93
- let(:completed_output) { [{ id: "2", url: "elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k", :width=>400, :height=>224, :frame_rate=>25, :file_size=>6901104, :duration=>117353 }] }
96
+ let(:completed_output) { [{ id: "2", url: "s3://BucketName/elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k", :width=>400, :height=>224, :frame_rate=>25, :file_size=>6901104, :duration=>117353 }] }
94
97
  let(:completed_tech_metadata) { { :width=>1280, :height=>720, :frame_rate=>25, :file_size=>21069678, :duration=>117312 } }
95
98
  let(:failed_tech_metadata) { {} }
96
99
 
97
100
  it_behaves_like "an ActiveEncode::EngineAdapter"
98
101
 
99
102
  describe "#create" do
100
- let(:create_output) { [{ id: "2", url: "elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
103
+ let(:create_output) { [{ id: "2", url: "s3://BucketName/elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
101
104
 
102
105
  subject { created_job }
103
106
 
@@ -114,7 +117,7 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
114
117
 
115
118
  describe "#find" do
116
119
  context "a running encode" do
117
- let(:running_output) { [{ id: "2", url: "elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
120
+ let(:running_output) { [{ id: "2", url: "s3://BucketName/elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
118
121
  let(:running_tech_metadata) { {:width=>1280, :height=>720, :frame_rate=>25, :file_size=>21069678, :duration=>117312} }
119
122
 
120
123
  subject { running_job }
@@ -133,4 +136,35 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
133
136
  end
134
137
  end
135
138
  end
139
+
140
+ describe "#check_s3_bucket" do
141
+ context "when file exists in masterfile_bucket" do
142
+ let(:input_url) { "s3://bucket1/file.mp4" }
143
+ let(:source_bucket) { "bucket1" }
144
+
145
+ it "just returns the key" do
146
+ # TODO: move these bucket helpers out to a service class so we don't have to test private methods
147
+ expect(described_class.new.send(:check_s3_bucket, input_url, source_bucket)).to eq "file.mp4"
148
+ end
149
+ end
150
+
151
+ context "when file is in another bucket" do
152
+ let(:input_url) { "s3://bucket1/file.mp4" }
153
+ let(:source_bucket) { "bucket2" }
154
+
155
+ it "copies to masterfile_bucket" do
156
+ # TODO: move these bucket helpers out to a service class so we don't have to test private methods
157
+ allow(SecureRandom).to receive(:uuid).and_return("randomstring")
158
+ expect(described_class.new.send(:check_s3_bucket, input_url, source_bucket)).to eq "randomstring/file.mp4"
159
+ end
160
+ end
161
+ end
162
+
163
+ describe "#output_percentage" do
164
+ let(:output) { double(ActiveEncode::Output, status: "Random status") }
165
+
166
+ it "returns 0 for any other status" do
167
+ expect(described_class.new.send(:output_percentage, output)).to eq 0
168
+ end
169
+ end
136
170
  end