active_encode 0.4.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +80 -0
- data/.rubocop.yml +9 -70
- data/.rubocop_todo.yml +68 -0
- data/CODE_OF_CONDUCT.md +36 -0
- data/CONTRIBUTING.md +23 -21
- data/Gemfile +5 -4
- data/LICENSE +11 -199
- data/README.md +135 -24
- data/SUPPORT.md +5 -0
- data/active_encode.gemspec +13 -3
- data/app/controllers/active_encode/encode_record_controller.rb +13 -0
- data/app/jobs/active_encode/polling_job.rb +1 -1
- data/app/models/active_encode/encode_record.rb +1 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20180822021048_create_active_encode_encode_records.rb +1 -0
- data/db/migrate/20190702153755_add_create_options_to_active_encode_encode_records.rb +6 -0
- data/db/migrate/20190712174821_add_progress_to_active_encode_encode_records.rb +6 -0
- data/lib/active_encode.rb +1 -0
- data/lib/active_encode/base.rb +2 -2
- data/lib/active_encode/callbacks.rb +1 -0
- data/lib/active_encode/core.rb +4 -3
- data/lib/active_encode/engine.rb +1 -0
- data/lib/active_encode/engine_adapter.rb +1 -0
- data/lib/active_encode/engine_adapters.rb +4 -1
- data/lib/active_encode/engine_adapters/elastic_transcoder_adapter.rb +116 -38
- data/lib/active_encode/engine_adapters/ffmpeg_adapter.rb +141 -87
- data/lib/active_encode/engine_adapters/matterhorn_adapter.rb +5 -4
- data/lib/active_encode/engine_adapters/media_convert_adapter.rb +372 -0
- data/lib/active_encode/engine_adapters/media_convert_output.rb +104 -0
- data/lib/active_encode/engine_adapters/pass_through_adapter.rb +239 -0
- data/lib/active_encode/engine_adapters/test_adapter.rb +5 -4
- data/lib/active_encode/engine_adapters/zencoder_adapter.rb +3 -2
- data/lib/active_encode/errors.rb +6 -0
- data/lib/active_encode/global_id.rb +2 -1
- data/lib/active_encode/input.rb +3 -2
- data/lib/active_encode/output.rb +3 -2
- data/lib/active_encode/persistence.rb +11 -5
- data/lib/active_encode/polling.rb +3 -2
- data/lib/active_encode/spec/shared_specs.rb +2 -0
- data/{spec/shared_specs/engine_adapter_specs.rb → lib/active_encode/spec/shared_specs/engine_adapter.rb} +37 -38
- data/lib/active_encode/status.rb +1 -0
- data/lib/active_encode/technical_metadata.rb +3 -2
- data/lib/active_encode/version.rb +2 -1
- data/lib/file_locator.rb +93 -0
- data/spec/controllers/encode_record_controller_spec.rb +53 -0
- data/spec/fixtures/ffmpeg/cancelled-id/cancelled +0 -0
- data/spec/fixtures/file with space.low.mp4 +0 -0
- data/spec/fixtures/file with space.mp4 +0 -0
- data/spec/fixtures/fireworks.low.mp4 +0 -0
- data/spec/fixtures/media_convert/endpoints.json +1 -0
- data/spec/fixtures/media_convert/job_canceled.json +412 -0
- data/spec/fixtures/media_convert/job_canceling.json +1 -0
- data/spec/fixtures/media_convert/job_completed.json +359 -0
- data/spec/fixtures/media_convert/job_completed_detail.json +1 -0
- data/spec/fixtures/media_convert/job_completed_detail_query.json +1 -0
- data/spec/fixtures/media_convert/job_created.json +408 -0
- data/spec/fixtures/media_convert/job_failed.json +406 -0
- data/spec/fixtures/media_convert/job_progressing.json +414 -0
- data/spec/fixtures/pass_through/cancelled-id/cancelled +0 -0
- data/spec/fixtures/pass_through/cancelled-id/input_metadata +90 -0
- data/spec/fixtures/pass_through/completed-id/completed +0 -0
- data/spec/fixtures/pass_through/completed-id/input_metadata +102 -0
- data/spec/fixtures/pass_through/completed-id/output_metadata-high +90 -0
- data/spec/fixtures/pass_through/completed-id/output_metadata-low +90 -0
- data/spec/fixtures/pass_through/completed-id/video-high.mp4 +0 -0
- data/spec/fixtures/pass_through/completed-id/video-low.mp4 +0 -0
- data/spec/fixtures/pass_through/failed-id/error.log +1 -0
- data/spec/fixtures/pass_through/failed-id/input_metadata +90 -0
- data/spec/fixtures/pass_through/running-id/input_metadata +90 -0
- data/spec/integration/elastic_transcoder_adapter_spec.rb +63 -29
- data/spec/integration/ffmpeg_adapter_spec.rb +96 -24
- data/spec/integration/matterhorn_adapter_spec.rb +45 -44
- data/spec/integration/media_convert_adapter_spec.rb +126 -0
- data/spec/integration/pass_through_adapter_spec.rb +151 -0
- data/spec/integration/zencoder_adapter_spec.rb +210 -209
- data/spec/rails_helper.rb +1 -0
- data/spec/routing/encode_record_controller_routing_spec.rb +10 -0
- data/spec/spec_helper.rb +2 -2
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +13 -12
- data/spec/units/callbacks_spec.rb +3 -2
- data/spec/units/core_spec.rb +26 -25
- data/spec/units/engine_adapter_spec.rb +1 -0
- data/spec/units/file_locator_spec.rb +129 -0
- data/spec/units/global_id_spec.rb +12 -11
- data/spec/units/input_spec.rb +8 -5
- data/spec/units/output_spec.rb +8 -5
- data/spec/units/persistence_spec.rb +15 -11
- data/spec/units/polling_job_spec.rb +7 -6
- data/spec/units/polling_spec.rb +1 -0
- data/spec/units/status_spec.rb +3 -3
- metadata +184 -18
- data/.travis.yml +0 -19
@@ -0,0 +1,90 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<MediaInfo
|
3
|
+
xmlns="https://mediaarea.net/mediainfo"
|
4
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
5
|
+
xsi:schemaLocation="https://mediaarea.net/mediainfo https://mediaarea.net/mediainfo/mediainfo_2_0.xsd"
|
6
|
+
version="2.0">
|
7
|
+
<creatingLibrary version="18.05" url="https://mediaarea.net/MediaInfo">MediaInfoLib</creatingLibrary>
|
8
|
+
<media ref="file:///Bars_512kb.mp4">
|
9
|
+
<track type="General">
|
10
|
+
<VideoCount>1</VideoCount>
|
11
|
+
<AudioCount>1</AudioCount>
|
12
|
+
<FileExtension>mp4</FileExtension>
|
13
|
+
<Format>MPEG-4</Format>
|
14
|
+
<Format_Profile>Base Media</Format_Profile>
|
15
|
+
<CodecID>isom</CodecID>
|
16
|
+
<FileSize>24876</FileSize>
|
17
|
+
<Duration>4.928</Duration>
|
18
|
+
<OverallBitRate_Mode>VBR</OverallBitRate_Mode>
|
19
|
+
<OverallBitRate>40383</OverallBitRate>
|
20
|
+
<FrameRate>1.000</FrameRate>
|
21
|
+
<FrameCount>1</FrameCount>
|
22
|
+
<StreamSize>3216</StreamSize>
|
23
|
+
<HeaderSize>3208</HeaderSize>
|
24
|
+
<DataSize>21668</DataSize>
|
25
|
+
<FooterSize>0</FooterSize>
|
26
|
+
<IsStreamable>Yes</IsStreamable>
|
27
|
+
<Title>Bars - http://www.archive.org/details/ColorBarsWTone</Title>
|
28
|
+
<Movie>Bars - http://www.archive.org/details/ColorBarsWTone</Movie>
|
29
|
+
<Recorded_Date>2008</Recorded_Date>
|
30
|
+
<Encoded_Date>UTC 1970-01-01 00:00:00</Encoded_Date>
|
31
|
+
<Tagged_Date>UTC 2008-11-23 18:44:25</Tagged_Date>
|
32
|
+
<Encoded_Application>Lavf51.10.0</Encoded_Application>
|
33
|
+
<Comment>license: http://creativecommons.org/licenses/publicdomain/</Comment>
|
34
|
+
</track>
|
35
|
+
<track type="Video">
|
36
|
+
<StreamOrder>0</StreamOrder>
|
37
|
+
<ID>1</ID>
|
38
|
+
<Format>AVC</Format>
|
39
|
+
<Format_Profile>Baseline</Format_Profile>
|
40
|
+
<Format_Level>1.3</Format_Level>
|
41
|
+
<Format_Settings_CABAC>No</Format_Settings_CABAC>
|
42
|
+
<Format_Settings_RefFrames>1</Format_Settings_RefFrames>
|
43
|
+
<CodecID>avc1</CodecID>
|
44
|
+
<Duration>1.000</Duration>
|
45
|
+
<BitRate>18504</BitRate>
|
46
|
+
<Width>360</Width>
|
47
|
+
<Height>240</Height>
|
48
|
+
<Stored_Width>368</Stored_Width>
|
49
|
+
<Sampled_Width>360</Sampled_Width>
|
50
|
+
<Sampled_Height>240</Sampled_Height>
|
51
|
+
<PixelAspectRatio>1.000</PixelAspectRatio>
|
52
|
+
<DisplayAspectRatio>1.500</DisplayAspectRatio>
|
53
|
+
<Rotation>0.000</Rotation>
|
54
|
+
<FrameRate_Mode>CFR</FrameRate_Mode>
|
55
|
+
<FrameRate>1.000</FrameRate>
|
56
|
+
<FrameRate_Original>0.200</FrameRate_Original>
|
57
|
+
<FrameCount>1</FrameCount>
|
58
|
+
<ColorSpace>YUV</ColorSpace>
|
59
|
+
<ChromaSubsampling>4:2:0</ChromaSubsampling>
|
60
|
+
<BitDepth>8</BitDepth>
|
61
|
+
<ScanType>Progressive</ScanType>
|
62
|
+
<StreamSize>2313</StreamSize>
|
63
|
+
<Encoded_Date>UTC 1970-01-01 00:00:00</Encoded_Date>
|
64
|
+
<Tagged_Date>UTC 1970-01-01 00:00:00</Tagged_Date>
|
65
|
+
</track>
|
66
|
+
<track type="Audio">
|
67
|
+
<StreamOrder>1</StreamOrder>
|
68
|
+
<ID>2</ID>
|
69
|
+
<Format>AAC</Format>
|
70
|
+
<Format_Profile>LC</Format_Profile>
|
71
|
+
<CodecID>mp4a-40-2</CodecID>
|
72
|
+
<Duration>4.928</Duration>
|
73
|
+
<BitRate_Mode>VBR</BitRate_Mode>
|
74
|
+
<BitRate>31407</BitRate>
|
75
|
+
<Channels>2</Channels>
|
76
|
+
<ChannelPositions>Front: L R</ChannelPositions>
|
77
|
+
<ChannelLayout>L R</ChannelLayout>
|
78
|
+
<SamplesPerFrame>1024</SamplesPerFrame>
|
79
|
+
<SamplingRate>48000</SamplingRate>
|
80
|
+
<SamplingCount>236544</SamplingCount>
|
81
|
+
<FrameRate>46.875</FrameRate>
|
82
|
+
<FrameCount>231</FrameCount>
|
83
|
+
<Compression_Mode>Lossy</Compression_Mode>
|
84
|
+
<StreamSize>19347</StreamSize>
|
85
|
+
<StreamSize_Proportion>0.77774</StreamSize_Proportion>
|
86
|
+
<Encoded_Date>UTC 1970-01-01 00:00:00</Encoded_Date>
|
87
|
+
<Tagged_Date>UTC 1970-01-01 00:00:00</Tagged_Date>
|
88
|
+
</track>
|
89
|
+
</media>
|
90
|
+
</MediaInfo>
|
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'spec_helper'
|
2
|
-
require 'aws-sdk'
|
3
|
+
require 'aws-sdk-elastictranscoder'
|
3
4
|
require 'json'
|
4
|
-
require 'shared_specs
|
5
|
+
require 'active_encode/spec/shared_specs'
|
5
6
|
|
6
7
|
describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
|
7
|
-
around
|
8
|
+
around do |example|
|
8
9
|
# Setting this before each test works around a stubbing + memoization limitation
|
9
10
|
ActiveEncode::Base.engine_adapter = :elastic_transcoder
|
10
11
|
example.run
|
@@ -12,33 +13,37 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
|
|
12
13
|
end
|
13
14
|
|
14
15
|
let(:client) { Aws::ElasticTranscoder::Client.new(stub_responses: true) }
|
16
|
+
let(:s3client) { Aws::S3::Client.new(stub_responses: true) }
|
15
17
|
|
16
18
|
before do
|
17
19
|
allow(Aws::ElasticTranscoder::Client).to receive(:new).and_return(client)
|
20
|
+
allow(Aws::S3::Client).to receive(:new).and_return(s3client)
|
18
21
|
end
|
19
22
|
|
20
23
|
let(:created_job) do
|
21
24
|
j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_created.json'))
|
22
25
|
j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_generic.json')))
|
23
|
-
j.outputs = [
|
26
|
+
j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_submitted.json')))]
|
24
27
|
|
25
28
|
client.stub_responses(:create_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
|
26
29
|
|
27
30
|
ActiveEncode::Base.create(
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
"spec/fixtures/fireworks.mp4",
|
32
|
+
pipeline_id: "1471963629141-kmcocm",
|
33
|
+
masterfile_bucket: "BucketName",
|
34
|
+
output_key_prefix: "elastic-transcoder-samples/output/hls/",
|
35
|
+
outputs: [{
|
36
|
+
key: 'hls0400k/' + "e8fe80f5bsomefilesource_bucket7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a",
|
37
|
+
preset_id: "1351620000001-200050",
|
38
|
+
segment_duration: "2"
|
39
|
+
}]
|
40
|
+
)
|
36
41
|
end
|
37
42
|
|
38
43
|
let(:running_job) do
|
39
44
|
j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_progressing.json'))
|
40
45
|
j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_progressing.json')))
|
41
|
-
j.outputs = [
|
46
|
+
j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_progressing.json')))]
|
42
47
|
|
43
48
|
client.stub_responses(:read_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
|
44
49
|
ActiveEncode::Base.find('running-id')
|
@@ -47,7 +52,7 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
|
|
47
52
|
let(:canceled_job) do
|
48
53
|
j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_canceled.json'))
|
49
54
|
j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_generic.json')))
|
50
|
-
j.outputs = [
|
55
|
+
j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_canceled.json')))]
|
51
56
|
|
52
57
|
client.stub_responses(:read_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
|
53
58
|
|
@@ -57,11 +62,11 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
|
|
57
62
|
let(:cancelling_job) do
|
58
63
|
j1 = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_progressing.json'))
|
59
64
|
j1.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_progressing.json')))
|
60
|
-
j1.outputs = [
|
65
|
+
j1.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_progressing.json')))]
|
61
66
|
|
62
67
|
j2 = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_canceled.json'))
|
63
68
|
j2.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_generic.json')))
|
64
|
-
j2.outputs = [
|
69
|
+
j2.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_canceled.json')))]
|
65
70
|
|
66
71
|
client.stub_responses(:read_job, [Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j1), Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j2)])
|
67
72
|
|
@@ -75,7 +80,7 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
|
|
75
80
|
let(:completed_job) do
|
76
81
|
j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_completed.json'))
|
77
82
|
j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_completed.json')))
|
78
|
-
j.outputs = [
|
83
|
+
j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_completed.json')))]
|
79
84
|
|
80
85
|
client.stub_responses(:read_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
|
81
86
|
ActiveEncode::Base.find('completed-id')
|
@@ -84,29 +89,28 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
|
|
84
89
|
let(:failed_job) do
|
85
90
|
j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_failed.json'))
|
86
91
|
j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_generic.json')))
|
87
|
-
j.outputs = [
|
92
|
+
j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_failed.json')))]
|
88
93
|
|
89
94
|
client.stub_responses(:read_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
|
90
95
|
ActiveEncode::Base.find('failed-id')
|
91
96
|
end
|
92
97
|
|
93
|
-
let(:completed_output) { [{ id: "2", url: "elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k", :
|
94
|
-
let(:completed_tech_metadata) { { :
|
98
|
+
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: 6_901_104, duration: 117_353 }] }
|
99
|
+
let(:completed_tech_metadata) { { width: 1280, height: 720, frame_rate: 25, file_size: 21_069_678, duration: 117_312 } }
|
95
100
|
let(:failed_tech_metadata) { {} }
|
96
101
|
|
97
102
|
it_behaves_like "an ActiveEncode::EngineAdapter"
|
98
103
|
|
99
104
|
describe "#create" do
|
100
|
-
let(:create_output) { [{ id: "2", url: "elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
|
101
|
-
|
102
105
|
subject { created_job }
|
106
|
+
let(:create_output) { [{ id: "2", url: "s3://BucketName/elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
|
103
107
|
|
104
108
|
it { is_expected.to be_running }
|
105
|
-
|
109
|
+
it { expect(subject.current_operations).to be_empty }
|
106
110
|
|
107
111
|
it 'output has technical metadata' do
|
108
112
|
subject.output.each do |output|
|
109
|
-
expected_output = create_output.find {|expected_out| expected_out[:id] == output.id }
|
113
|
+
expected_output = create_output.find { |expected_out| expected_out[:id] == output.id }
|
110
114
|
expect(output.as_json.symbolize_keys).to include expected_output
|
111
115
|
end
|
112
116
|
end
|
@@ -114,12 +118,11 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
|
|
114
118
|
|
115
119
|
describe "#find" do
|
116
120
|
context "a running encode" do
|
117
|
-
let(:running_output) { [{ id: "2", url: "elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
|
118
|
-
let(:running_tech_metadata) { {:width=>1280, :height=>720, :frame_rate=>25, :file_size=>21069678, :duration=>117312} }
|
119
|
-
|
120
121
|
subject { running_job }
|
122
|
+
let(:running_output) { [{ id: "2", url: "s3://BucketName/elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
|
123
|
+
let(:running_tech_metadata) { { width: 1280, height: 720, frame_rate: 25, file_size: 21_069_678, duration: 117_312 } }
|
121
124
|
|
122
|
-
|
125
|
+
it { expect(subject.current_operations).to be_empty }
|
123
126
|
|
124
127
|
it 'input has technical metadata' do
|
125
128
|
expect(subject.input.as_json.symbolize_keys).to include running_tech_metadata
|
@@ -127,10 +130,41 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
|
|
127
130
|
|
128
131
|
it 'output has technical metadata' do
|
129
132
|
subject.output.each do |output|
|
130
|
-
expected_output = running_output.find {|expected_out| expected_out[:id] == output.id }
|
133
|
+
expected_output = running_output.find { |expected_out| expected_out[:id] == output.id }
|
131
134
|
expect(output.as_json.symbolize_keys).to include expected_output
|
132
135
|
end
|
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
|
@@ -1,22 +1,27 @@
|
|
1
|
-
|
2
|
-
require '
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'rails_helper'
|
3
|
+
require 'active_encode/spec/shared_specs'
|
3
4
|
|
4
5
|
describe ActiveEncode::EngineAdapters::FfmpegAdapter do
|
5
|
-
around
|
6
|
+
around do |example|
|
6
7
|
ActiveEncode::Base.engine_adapter = :ffmpeg
|
7
8
|
|
8
9
|
Dir.mktmpdir do |dir|
|
9
10
|
@dir = dir
|
10
11
|
example.run
|
12
|
+
Dir.foreach(dir) do |e|
|
13
|
+
next if e == "." || e == ".."
|
14
|
+
FileUtils.rm_rf(File.join(dir, e))
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
18
|
ActiveEncode::Base.engine_adapter = :test
|
14
19
|
end
|
15
20
|
|
16
21
|
let!(:work_dir) { stub_const "ActiveEncode::EngineAdapters::FfmpegAdapter::WORK_DIR", @dir }
|
17
|
-
let(:file) { "file
|
22
|
+
let(:file) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'fireworks.mp4').to_s }
|
18
23
|
let(:created_job) do
|
19
|
-
ActiveEncode::Base.create(file,
|
24
|
+
ActiveEncode::Base.create(file, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: "mp4" }, { label: "high", ffmpeg_opt: "-s 1280x720", extension: "mp4" }])
|
20
25
|
end
|
21
26
|
let(:running_job) do
|
22
27
|
allow(Process).to receive(:getpgid).and_return 8888
|
@@ -27,28 +32,33 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
|
|
27
32
|
end
|
28
33
|
let(:cancelling_job) do
|
29
34
|
allow(Process).to receive(:kill).and_return(nil)
|
30
|
-
find_encode 'running-id'
|
35
|
+
encode = find_encode 'running-id'
|
36
|
+
File.write "#{work_dir}/running-id/cancelled", ""
|
37
|
+
encode
|
31
38
|
end
|
32
39
|
let(:completed_job) { find_encode "completed-id" }
|
33
40
|
let(:failed_job) { find_encode 'failed-id' }
|
34
|
-
let(:completed_tech_metadata)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
let(:completed_tech_metadata) do
|
42
|
+
{
|
43
|
+
audio_bitrate: 171_030,
|
44
|
+
audio_codec: 'mp4a-40-2',
|
45
|
+
duration: 6315,
|
46
|
+
file_size: 199_160,
|
47
|
+
frame_rate: 23.719,
|
48
|
+
height: 110.0,
|
49
|
+
id: "99999",
|
50
|
+
url: "/home/pdinh/Downloads/videoshort.mp4",
|
51
|
+
video_bitrate: 74_477,
|
52
|
+
video_codec: 'avc1',
|
53
|
+
width: 200.0
|
54
|
+
}
|
55
|
+
end
|
46
56
|
let(:completed_output) { [{ id: "99999" }] }
|
47
|
-
let(:failed_tech_metadata) { {
|
57
|
+
let(:failed_tech_metadata) { {} }
|
48
58
|
|
49
59
|
it_behaves_like "an ActiveEncode::EngineAdapter"
|
50
60
|
|
51
|
-
def find_encode
|
61
|
+
def find_encode(id)
|
52
62
|
# Precreate ffmpeg output directory and files
|
53
63
|
FileUtils.copy_entry "spec/fixtures/ffmpeg/#{id}", "#{work_dir}/#{id}"
|
54
64
|
|
@@ -57,7 +67,7 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
|
|
57
67
|
FileUtils.touch "#{work_dir}/#{id}/progress"
|
58
68
|
FileUtils.touch Dir.glob("#{work_dir}/#{id}/*.mp4")
|
59
69
|
|
60
|
-
#
|
70
|
+
# Stub out system calls
|
61
71
|
allow_any_instance_of(ActiveEncode::EngineAdapters::FfmpegAdapter).to receive(:`).and_return(1234)
|
62
72
|
|
63
73
|
ActiveEncode::Base.find(id)
|
@@ -82,7 +92,7 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
|
|
82
92
|
|
83
93
|
context "input file doesn't exist" do
|
84
94
|
let(:missing_file) { "file:///a_bogus_file.mp4" }
|
85
|
-
let(:missing_job) { ActiveEncode::Base.create(missing_file,
|
95
|
+
let(:missing_job) { ActiveEncode::Base.create(missing_file, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
|
86
96
|
|
87
97
|
it "returns the encode with correct error" do
|
88
98
|
expect(missing_job.errors).to include("#{missing_file} does not exist or is not accessible")
|
@@ -91,14 +101,61 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
|
|
91
101
|
end
|
92
102
|
|
93
103
|
context "input file is not media" do
|
94
|
-
let(:nonmedia_file) { "file
|
95
|
-
let(:nonmedia_job) { ActiveEncode::Base.create(nonmedia_file,
|
104
|
+
let(:nonmedia_file) { "file://" + Rails.root.join('Gemfile').to_s }
|
105
|
+
let(:nonmedia_job) { ActiveEncode::Base.create(nonmedia_file, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
|
96
106
|
|
97
107
|
it "returns the encode with correct error" do
|
98
108
|
expect(nonmedia_job.errors).to include("Error inspecting input: #{nonmedia_file}")
|
99
109
|
expect(nonmedia_job.percent_complete).to be 1
|
100
110
|
end
|
101
111
|
end
|
112
|
+
|
113
|
+
context "input filename with spaces" do
|
114
|
+
let(:file_with_space) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file with space.mp4').to_s }
|
115
|
+
let!(:create_space_job) { ActiveEncode::Base.create(file_with_space, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
|
116
|
+
let(:find_space_job) { ActiveEncode::Base.find create_space_job.id }
|
117
|
+
|
118
|
+
it "does not have errors" do
|
119
|
+
sleep 2
|
120
|
+
expect(find_space_job.errors).to be_empty
|
121
|
+
end
|
122
|
+
|
123
|
+
it "has the input technical metadata in a file" do
|
124
|
+
expect(File.read("#{work_dir}/#{create_space_job.id}/input_metadata")).not_to be_empty
|
125
|
+
end
|
126
|
+
|
127
|
+
it "has the pid in a file" do
|
128
|
+
expect(File.read("#{work_dir}/#{create_space_job.id}/pid")).not_to be_empty
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when uri encoded' do
|
132
|
+
let(:file_with_space) { URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', 'file with space.mp4').to_s) }
|
133
|
+
|
134
|
+
it "does not have errors" do
|
135
|
+
sleep 2
|
136
|
+
expect(find_space_job.errors).to be_empty
|
137
|
+
end
|
138
|
+
|
139
|
+
it "has the input technical metadata in a file" do
|
140
|
+
expect(File.read("#{work_dir}/#{create_space_job.id}/input_metadata")).not_to be_empty
|
141
|
+
end
|
142
|
+
|
143
|
+
it "has the pid in a file" do
|
144
|
+
expect(File.read("#{work_dir}/#{create_space_job.id}/pid")).not_to be_empty
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'when failed' do
|
150
|
+
subject { created_job }
|
151
|
+
|
152
|
+
before do
|
153
|
+
allow_any_instance_of(Object).to receive(:`).and_raise Errno::ENOENT
|
154
|
+
end
|
155
|
+
|
156
|
+
it { is_expected.to be_failed }
|
157
|
+
it { expect(subject.errors).to be_present }
|
158
|
+
end
|
102
159
|
end
|
103
160
|
|
104
161
|
describe "#find" do
|
@@ -116,5 +173,20 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
|
|
116
173
|
expect(Process).to receive(:kill).with('SIGTERM', running_job.input.id.to_i)
|
117
174
|
running_job.cancel!
|
118
175
|
end
|
176
|
+
|
177
|
+
it "does not attempt to stop a non-running encode" do
|
178
|
+
expect(Process).not_to receive(:kill).with('SIGTERM', completed_job.input.id.to_i)
|
179
|
+
completed_job.cancel!
|
180
|
+
end
|
181
|
+
|
182
|
+
it "raises an error if the process can not be found" do
|
183
|
+
expect(Process).to receive(:kill).with('SIGTERM', running_job.input.id.to_i).and_raise(Errno::ESRCH)
|
184
|
+
expect { running_job.cancel! }.to raise_error(ActiveEncode::NotRunningError)
|
185
|
+
end
|
186
|
+
|
187
|
+
it "raises an error" do
|
188
|
+
expect(Process).to receive(:kill).with('SIGTERM', running_job.input.id.to_i).and_raise(Errno::EPERM)
|
189
|
+
expect { running_job.cancel! }.to raise_error(ActiveEncode::CancelError)
|
190
|
+
end
|
119
191
|
end
|
120
192
|
end
|