active_encode 0.4.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|