active_encode 0.5.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +80 -0
  3. data/.rubocop.yml +9 -70
  4. data/.rubocop_todo.yml +68 -0
  5. data/Gemfile +5 -4
  6. data/README.md +69 -0
  7. data/active_encode.gemspec +12 -3
  8. data/app/controllers/active_encode/encode_record_controller.rb +1 -0
  9. data/app/jobs/active_encode/polling_job.rb +1 -1
  10. data/app/models/active_encode/encode_record.rb +1 -0
  11. data/db/migrate/20180822021048_create_active_encode_encode_records.rb +1 -0
  12. data/db/migrate/20190702153755_add_create_options_to_active_encode_encode_records.rb +6 -0
  13. data/db/migrate/20190712174821_add_progress_to_active_encode_encode_records.rb +6 -0
  14. data/lib/active_encode.rb +1 -0
  15. data/lib/active_encode/base.rb +2 -2
  16. data/lib/active_encode/callbacks.rb +1 -0
  17. data/lib/active_encode/core.rb +4 -3
  18. data/lib/active_encode/engine.rb +1 -0
  19. data/lib/active_encode/engine_adapter.rb +1 -0
  20. data/lib/active_encode/engine_adapters.rb +4 -1
  21. data/lib/active_encode/engine_adapters/elastic_transcoder_adapter.rb +31 -29
  22. data/lib/active_encode/engine_adapters/ffmpeg_adapter.rb +138 -87
  23. data/lib/active_encode/engine_adapters/matterhorn_adapter.rb +5 -4
  24. data/lib/active_encode/engine_adapters/media_convert_adapter.rb +399 -0
  25. data/lib/active_encode/engine_adapters/media_convert_output.rb +104 -0
  26. data/lib/active_encode/engine_adapters/pass_through_adapter.rb +239 -0
  27. data/lib/active_encode/engine_adapters/test_adapter.rb +5 -4
  28. data/lib/active_encode/engine_adapters/zencoder_adapter.rb +3 -2
  29. data/lib/active_encode/errors.rb +6 -0
  30. data/lib/active_encode/global_id.rb +2 -1
  31. data/lib/active_encode/input.rb +3 -2
  32. data/lib/active_encode/output.rb +3 -2
  33. data/lib/active_encode/persistence.rb +11 -5
  34. data/lib/active_encode/polling.rb +3 -2
  35. data/lib/active_encode/spec/shared_specs.rb +2 -0
  36. data/{spec/shared_specs/engine_adapter_specs.rb → lib/active_encode/spec/shared_specs/engine_adapter.rb} +37 -38
  37. data/lib/active_encode/status.rb +1 -0
  38. data/lib/active_encode/technical_metadata.rb +3 -2
  39. data/lib/active_encode/version.rb +2 -1
  40. data/lib/file_locator.rb +8 -9
  41. data/spec/controllers/encode_record_controller_spec.rb +4 -3
  42. data/spec/fixtures/ffmpeg/cancelled-id/cancelled +0 -0
  43. data/spec/fixtures/file with space.low.mp4 +0 -0
  44. data/spec/fixtures/file with space.mp4 +0 -0
  45. data/spec/fixtures/fireworks.low.mp4 +0 -0
  46. data/spec/fixtures/media_convert/endpoints.json +1 -0
  47. data/spec/fixtures/media_convert/job_canceled.json +412 -0
  48. data/spec/fixtures/media_convert/job_canceling.json +1 -0
  49. data/spec/fixtures/media_convert/job_completed.json +359 -0
  50. data/spec/fixtures/media_convert/job_completed_detail.json +1 -0
  51. data/spec/fixtures/media_convert/job_completed_detail_query.json +1 -0
  52. data/spec/fixtures/media_convert/job_completed_empty_detail.json +1 -0
  53. data/spec/fixtures/media_convert/job_created.json +408 -0
  54. data/spec/fixtures/media_convert/job_failed.json +406 -0
  55. data/spec/fixtures/media_convert/job_progressing.json +414 -0
  56. data/spec/fixtures/pass_through/cancelled-id/cancelled +0 -0
  57. data/spec/fixtures/pass_through/cancelled-id/input_metadata +90 -0
  58. data/spec/fixtures/pass_through/completed-id/completed +0 -0
  59. data/spec/fixtures/pass_through/completed-id/input_metadata +102 -0
  60. data/spec/fixtures/pass_through/completed-id/output_metadata-high +90 -0
  61. data/spec/fixtures/pass_through/completed-id/output_metadata-low +90 -0
  62. data/spec/fixtures/pass_through/completed-id/video-high.mp4 +0 -0
  63. data/spec/fixtures/pass_through/completed-id/video-low.mp4 +0 -0
  64. data/spec/fixtures/pass_through/failed-id/error.log +1 -0
  65. data/spec/fixtures/pass_through/failed-id/input_metadata +90 -0
  66. data/spec/fixtures/pass_through/running-id/input_metadata +90 -0
  67. data/spec/integration/elastic_transcoder_adapter_spec.rb +30 -30
  68. data/spec/integration/ffmpeg_adapter_spec.rb +93 -25
  69. data/spec/integration/matterhorn_adapter_spec.rb +45 -44
  70. data/spec/integration/media_convert_adapter_spec.rb +152 -0
  71. data/spec/integration/pass_through_adapter_spec.rb +151 -0
  72. data/spec/integration/zencoder_adapter_spec.rb +210 -209
  73. data/spec/rails_helper.rb +1 -0
  74. data/spec/routing/encode_record_controller_routing_spec.rb +1 -0
  75. data/spec/spec_helper.rb +2 -2
  76. data/spec/test_app_templates/lib/generators/test_app_generator.rb +13 -12
  77. data/spec/units/callbacks_spec.rb +3 -2
  78. data/spec/units/core_spec.rb +26 -25
  79. data/spec/units/engine_adapter_spec.rb +1 -0
  80. data/spec/units/file_locator_spec.rb +20 -19
  81. data/spec/units/global_id_spec.rb +12 -11
  82. data/spec/units/input_spec.rb +8 -5
  83. data/spec/units/output_spec.rb +8 -5
  84. data/spec/units/persistence_spec.rb +15 -11
  85. data/spec/units/polling_job_spec.rb +7 -6
  86. data/spec/units/polling_spec.rb +1 -0
  87. data/spec/units/status_spec.rb +3 -3
  88. metadata +158 -14
  89. 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/engine_adapter_specs'
5
+ require 'active_encode/spec/shared_specs'
5
6
 
6
7
  describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
7
- around(:example) do |example|
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
@@ -22,26 +23,27 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
22
23
  let(:created_job) do
23
24
  j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_created.json'))
24
25
  j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_generic.json')))
25
- j.outputs = [ Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_submitted.json')))]
26
+ j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_submitted.json')))]
26
27
 
27
28
  client.stub_responses(:create_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
28
29
 
29
30
  ActiveEncode::Base.create(
30
- "spec/fixtures/fireworks.mp4",
31
- pipeline_id: "1471963629141-kmcocm",
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
- }])
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
+ )
39
41
  end
40
42
 
41
43
  let(:running_job) do
42
44
  j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_progressing.json'))
43
45
  j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_progressing.json')))
44
- j.outputs = [ Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_progressing.json')))]
46
+ j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_progressing.json')))]
45
47
 
46
48
  client.stub_responses(:read_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
47
49
  ActiveEncode::Base.find('running-id')
@@ -50,7 +52,7 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
50
52
  let(:canceled_job) do
51
53
  j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_canceled.json'))
52
54
  j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_generic.json')))
53
- j.outputs = [ Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_canceled.json')))]
55
+ j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_canceled.json')))]
54
56
 
55
57
  client.stub_responses(:read_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
56
58
 
@@ -60,11 +62,11 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
60
62
  let(:cancelling_job) do
61
63
  j1 = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_progressing.json'))
62
64
  j1.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_progressing.json')))
63
- j1.outputs = [ Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_progressing.json')))]
65
+ j1.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_progressing.json')))]
64
66
 
65
67
  j2 = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_canceled.json'))
66
68
  j2.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_generic.json')))
67
- j2.outputs = [ Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_canceled.json')))]
69
+ j2.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_canceled.json')))]
68
70
 
69
71
  client.stub_responses(:read_job, [Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j1), Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j2)])
70
72
 
@@ -78,7 +80,7 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
78
80
  let(:completed_job) do
79
81
  j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_completed.json'))
80
82
  j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_completed.json')))
81
- j.outputs = [ Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_completed.json')))]
83
+ j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_completed.json')))]
82
84
 
83
85
  client.stub_responses(:read_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
84
86
  ActiveEncode::Base.find('completed-id')
@@ -87,29 +89,28 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
87
89
  let(:failed_job) do
88
90
  j = Aws::ElasticTranscoder::Types::Job.new JSON.parse(File.read('spec/fixtures/elastic_transcoder/job_failed.json'))
89
91
  j.input = Aws::ElasticTranscoder::Types::JobInput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/input_generic.json')))
90
- j.outputs = [ Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_failed.json')))]
92
+ j.outputs = [Aws::ElasticTranscoder::Types::JobOutput.new(JSON.parse(File.read('spec/fixtures/elastic_transcoder/output_failed.json')))]
91
93
 
92
94
  client.stub_responses(:read_job, Aws::ElasticTranscoder::Types::ReadJobResponse.new(job: j))
93
95
  ActiveEncode::Base.find('failed-id')
94
96
  end
95
97
 
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 }] }
97
- let(:completed_tech_metadata) { { :width=>1280, :height=>720, :frame_rate=>25, :file_size=>21069678, :duration=>117312 } }
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 } }
98
100
  let(:failed_tech_metadata) { {} }
99
101
 
100
102
  it_behaves_like "an ActiveEncode::EngineAdapter"
101
103
 
102
104
  describe "#create" do
103
- let(:create_output) { [{ id: "2", url: "s3://BucketName/elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
104
-
105
105
  subject { created_job }
106
+ let(:create_output) { [{ id: "2", url: "s3://BucketName/elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
106
107
 
107
108
  it { is_expected.to be_running }
108
- its(:current_operations) { is_expected.to be_empty }
109
+ it { expect(subject.current_operations).to be_empty }
109
110
 
110
111
  it 'output has technical metadata' do
111
112
  subject.output.each do |output|
112
- 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 }
113
114
  expect(output.as_json.symbolize_keys).to include expected_output
114
115
  end
115
116
  end
@@ -117,12 +118,11 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
117
118
 
118
119
  describe "#find" do
119
120
  context "a running encode" do
120
- let(:running_output) { [{ id: "2", url: "s3://BucketName/elastic-transcoder-samples/output/hls/hls0400k/e8fe80f5b7063b12d567b90c0bdf6322116bba11ac458fe9d62921644159fe4a", label: "hls0400k" }] }
121
- let(:running_tech_metadata) { {:width=>1280, :height=>720, :frame_rate=>25, :file_size=>21069678, :duration=>117312} }
122
-
123
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 } }
124
124
 
125
- its(:current_operations) { is_expected.to be_empty }
125
+ it { expect(subject.current_operations).to be_empty }
126
126
 
127
127
  it 'input has technical metadata' do
128
128
  expect(subject.input.as_json.symbolize_keys).to include running_tech_metadata
@@ -130,7 +130,7 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
130
130
 
131
131
  it 'output has technical metadata' do
132
132
  subject.output.each do |output|
133
- 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 }
134
134
  expect(output.as_json.symbolize_keys).to include expected_output
135
135
  end
136
136
  end
@@ -1,8 +1,9 @@
1
- require 'spec_helper'
2
- require 'shared_specs/engine_adapter_specs'
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(:example) do |example|
6
+ around do |example|
6
7
  ActiveEncode::Base.engine_adapter = :ffmpeg
7
8
 
8
9
  Dir.mktmpdir do |dir|
@@ -10,7 +11,7 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
10
11
  example.run
11
12
  Dir.foreach(dir) do |e|
12
13
  next if e == "." || e == ".."
13
- FileUtils.rm_rf(File.join(dir,e))
14
+ FileUtils.rm_rf(File.join(dir, e))
14
15
  end
15
16
  end
16
17
 
@@ -18,9 +19,9 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
18
19
  end
19
20
 
20
21
  let!(:work_dir) { stub_const "ActiveEncode::EngineAdapters::FfmpegAdapter::WORK_DIR", @dir }
21
- let(:file) { "file://#{File.absolute_path "spec/fixtures/fireworks.mp4"}" }
22
+ let(:file) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'fireworks.mp4').to_s }
22
23
  let(:created_job) do
23
- ActiveEncode::Base.create(file, { outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: "mp4"}, { label: "high", ffmpeg_opt: "-s 1280x720", extension: "mp4"}] })
24
+ ActiveEncode::Base.create(file, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: "mp4" }, { label: "high", ffmpeg_opt: "-s 1280x720", extension: "mp4" }])
24
25
  end
25
26
  let(:running_job) do
26
27
  allow(Process).to receive(:getpgid).and_return 8888
@@ -31,28 +32,33 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
31
32
  end
32
33
  let(:cancelling_job) do
33
34
  allow(Process).to receive(:kill).and_return(nil)
34
- find_encode 'running-id'
35
+ encode = find_encode 'running-id'
36
+ File.write "#{work_dir}/running-id/cancelled", ""
37
+ encode
35
38
  end
36
39
  let(:completed_job) { find_encode "completed-id" }
37
40
  let(:failed_job) { find_encode 'failed-id' }
38
- let(:completed_tech_metadata) { {:audio_bitrate => 171030,
39
- :audio_codec => 'mp4a-40-2',
40
- :duration => 6315,
41
- :file_size => 199160,
42
- :frame_rate => 23.719,
43
- :height => 110.0,
44
- :id => "99999",
45
- :url => "/home/pdinh/Downloads/videoshort.mp4",
46
- :video_bitrate => 74477,
47
- :video_codec => 'avc1',
48
- :width => 200.0
49
- } }
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
50
56
  let(:completed_output) { [{ id: "99999" }] }
51
- let(:failed_tech_metadata) { { } }
57
+ let(:failed_tech_metadata) { {} }
52
58
 
53
59
  it_behaves_like "an ActiveEncode::EngineAdapter"
54
60
 
55
- def find_encode id
61
+ def find_encode(id)
56
62
  # Precreate ffmpeg output directory and files
57
63
  FileUtils.copy_entry "spec/fixtures/ffmpeg/#{id}", "#{work_dir}/#{id}"
58
64
 
@@ -61,7 +67,7 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
61
67
  FileUtils.touch "#{work_dir}/#{id}/progress"
62
68
  FileUtils.touch Dir.glob("#{work_dir}/#{id}/*.mp4")
63
69
 
64
- # # Stub out system calls
70
+ # Stub out system calls
65
71
  allow_any_instance_of(ActiveEncode::EngineAdapters::FfmpegAdapter).to receive(:`).and_return(1234)
66
72
 
67
73
  ActiveEncode::Base.find(id)
@@ -86,7 +92,7 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
86
92
 
87
93
  context "input file doesn't exist" do
88
94
  let(:missing_file) { "file:///a_bogus_file.mp4" }
89
- let(:missing_job) { ActiveEncode::Base.create(missing_file, { outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]}) }
95
+ let(:missing_job) { ActiveEncode::Base.create(missing_file, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
90
96
 
91
97
  it "returns the encode with correct error" do
92
98
  expect(missing_job.errors).to include("#{missing_file} does not exist or is not accessible")
@@ -95,14 +101,61 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
95
101
  end
96
102
 
97
103
  context "input file is not media" do
98
- let(:nonmedia_file) { "file://#{File.absolute_path "spec/integration/ffmpeg_adapter_spec.rb"}" }
99
- let(:nonmedia_job) { ActiveEncode::Base.create(nonmedia_file, { outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]}) }
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' }]) }
100
106
 
101
107
  it "returns the encode with correct error" do
102
108
  expect(nonmedia_job.errors).to include("Error inspecting input: #{nonmedia_file}")
103
109
  expect(nonmedia_job.percent_complete).to be 1
104
110
  end
105
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
106
159
  end
107
160
 
108
161
  describe "#find" do
@@ -120,5 +173,20 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
120
173
  expect(Process).to receive(:kill).with('SIGTERM', running_job.input.id.to_i)
121
174
  running_job.cancel!
122
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
123
191
  end
124
192
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
  require 'rubyhorn'
3
- require 'shared_specs/engine_adapter_specs'
4
+ require 'active_encode/spec/shared_specs'
4
5
 
5
6
  describe ActiveEncode::EngineAdapters::MatterhornAdapter do
6
7
  before(:all) do
@@ -26,13 +27,13 @@ describe ActiveEncode::EngineAdapters::MatterhornAdapter do
26
27
  let(:created_job) { ActiveEncode::Base.create(file) }
27
28
  let(:running_job) { ActiveEncode::Base.find('running-id') }
28
29
  let(:canceled_job) { ActiveEncode::Base.find('cancelled-id') }
29
- let(:cancelling_job) { ActiveEncode::Base.find('running-id')}
30
+ let(:cancelling_job) { ActiveEncode::Base.find('running-id') }
30
31
  let(:completed_job) { ActiveEncode::Base.find('completed-id') }
31
32
  let(:failed_job) { ActiveEncode::Base.find('failed-id') }
32
33
 
33
- let(:completed_output) { [{ id: "track-7", checksum: "77de9765545ef63d2c21f7557ead6176", duration: 6337, audio_codec: "AAC", audio_bitrate: 76502.0, video_codec: "AVC", video_bitrate: 2000000.0, frame_rate: 30.0, width: 1308, height: 720, url: "file:///home/cjcolvar/Code/avalon/avalon/red5/webapps/avalon/streams/f564d9de-9c35-4b74-95f0-f3013f32cc1a/b09c765f-b64e-4725-a863-736af66b688c/videoshort.mp4", label: "quality-high" }, { id: "track-8", checksum: "10e13cf51bf8a973011eec6a17ea47ff", duration: 6337, audio_codec: "AAC", audio_bitrate: 76502.0, video_codec: "AVC", video_bitrate: 500000.0, frame_rate: 30.0, width: 654, height: 360, url: "file:///home/cjcolvar/Code/avalon/avalon/red5/webapps/avalon/streams/f564d9de-9c35-4b74-95f0-f3013f32cc1a/8d5cd8a9-ad0e-484a-96f0-05e26a84a8f0/videoshort.mp4", label: "quality-low" }, { id: "track-9", checksum: "f2b16a2606dc76cb53c7017f0e166204", duration: 6337, audio_codec: "AAC", audio_bitrate: 76502.0, video_codec: "AVC", video_bitrate: 1000000.0, frame_rate: 30.0, width: 872, height: 480, url: "file:///home/cjcolvar/Code/avalon/avalon/red5/webapps/avalon/streams/f564d9de-9c35-4b74-95f0-f3013f32cc1a/0f81d426-0e26-4496-8f58-c675c86e6f4e/videoshort.mp4", label: "quality-medium" }] }
34
- let(:completed_tech_metadata) { { } }
35
- let(:failed_tech_metadata) { { checksum: "7ae24368ccb7a6c6422a14ff73f33c9a", duration: 6314, audio_codec: "AAC", audio_bitrate: 171030.0, video_codec: "AVC", video_bitrate: 74477.0, frame_rate: 23.719, width: 200, height: 110 } }
34
+ let(:completed_output) { [{ id: "track-7", checksum: "77de9765545ef63d2c21f7557ead6176", duration: 6337, audio_codec: "AAC", audio_bitrate: 76_502.0, video_codec: "AVC", video_bitrate: 2_000_000.0, frame_rate: 30.0, width: 1308, height: 720, url: "file:///home/cjcolvar/Code/avalon/avalon/red5/webapps/avalon/streams/f564d9de-9c35-4b74-95f0-f3013f32cc1a/b09c765f-b64e-4725-a863-736af66b688c/videoshort.mp4", label: "quality-high" }, { id: "track-8", checksum: "10e13cf51bf8a973011eec6a17ea47ff", duration: 6337, audio_codec: "AAC", audio_bitrate: 76_502.0, video_codec: "AVC", video_bitrate: 500_000.0, frame_rate: 30.0, width: 654, height: 360, url: "file:///home/cjcolvar/Code/avalon/avalon/red5/webapps/avalon/streams/f564d9de-9c35-4b74-95f0-f3013f32cc1a/8d5cd8a9-ad0e-484a-96f0-05e26a84a8f0/videoshort.mp4", label: "quality-low" }, { id: "track-9", checksum: "f2b16a2606dc76cb53c7017f0e166204", duration: 6337, audio_codec: "AAC", audio_bitrate: 76_502.0, video_codec: "AVC", video_bitrate: 1_000_000.0, frame_rate: 30.0, width: 872, height: 480, url: "file:///home/cjcolvar/Code/avalon/avalon/red5/webapps/avalon/streams/f564d9de-9c35-4b74-95f0-f3013f32cc1a/0f81d426-0e26-4496-8f58-c675c86e6f4e/videoshort.mp4", label: "quality-medium" }] }
35
+ let(:completed_tech_metadata) { {} }
36
+ let(:failed_tech_metadata) { { checksum: "7ae24368ccb7a6c6422a14ff73f33c9a", duration: 6314, audio_codec: "AAC", audio_bitrate: 171_030.0, video_codec: "AVC", video_bitrate: 74_477.0, frame_rate: 23.719, width: 200, height: 110 } }
36
37
  let(:failed_errors) { "org.opencastproject.workflow.api.WorkflowOperationException: org.opencastproject.workflow.api.WorkflowOperationException: One of the encoding jobs did not complete successfully" }
37
38
 
38
39
  # Enforce generic behavior
@@ -41,62 +42,62 @@ describe ActiveEncode::EngineAdapters::MatterhornAdapter do
41
42
  describe "#create" do
42
43
  subject { created_job }
43
44
 
44
- its(:output) { is_expected.to be_empty }
45
- its(:options) { is_expected.to include(preset: 'full') }
45
+ it { expect(subject.output).to be_empty }
46
+ it { expect(subject.options).to include(preset: 'full') }
46
47
  end
47
48
 
48
49
  describe "#find" do
49
50
  context "a running encode" do
50
51
  subject { running_job }
51
52
 
52
- its(:options) { is_expected.to include(preset: 'full') }
53
- its(:output) { is_expected.to be_empty }
54
- its(:current_operations) { is_expected.to include("Hold for workflow selection") }
53
+ it { expect(subject.options).to include(preset: 'full') }
54
+ it { expect(subject.output).to be_empty }
55
+ it { expect(subject.current_operations).to include("Hold for workflow selection") }
55
56
 
56
57
  context 'input' do
57
58
  subject { running_job.input }
58
59
 
59
- its(:width) { is_expected.to be_blank }
60
- its(:height) { is_expected.to be_blank }
61
- its(:frame_rate) { is_expected.to be_blank }
62
- its(:duration) { is_expected.to be_blank }
63
- its(:file_size) { is_expected.to be_blank }
64
- its(:checksum) { is_expected.to be_blank }
65
- its(:audio_codec) { is_expected.to be_blank }
66
- its(:video_codec) { is_expected.to be_blank }
67
- its(:audio_bitrate) { is_expected.to be_blank }
68
- its(:video_bitrate) { is_expected.to be_blank }
60
+ it { expect(subject.width).to be_blank }
61
+ it { expect(subject.height).to be_blank }
62
+ it { expect(subject.frame_rate).to be_blank }
63
+ it { expect(subject.duration).to be_blank }
64
+ it { expect(subject.file_size).to be_blank }
65
+ it { expect(subject.checksum).to be_blank }
66
+ it { expect(subject.audio_codec).to be_blank }
67
+ it { expect(subject.video_codec).to be_blank }
68
+ it { expect(subject.audio_bitrate).to be_blank }
69
+ it { expect(subject.video_bitrate).to be_blank }
69
70
  end
70
71
  end
71
72
  context "a cancelled encode" do
72
73
  subject { canceled_job }
73
74
 
74
- its(:options) { is_expected.to include(preset: 'full') }
75
- its(:current_operations) { is_expected.not_to be_empty }
76
- its(:current_operations) { is_expected.to include("Tagging dublin core catalogs for publishing") }
77
- its(:updated_at) { is_expected.to be > subject.created_at }
75
+ it { expect(subject.options).to include(preset: 'full') }
76
+ it { expect(subject.current_operations).not_to be_empty }
77
+ it { expect(subject.current_operations).to include("Tagging dublin core catalogs for publishing") }
78
+ it { expect(subject.updated_at).to be > subject.created_at }
78
79
  end
79
80
 
80
81
  context "a completed encode" do
81
82
  subject { completed_job }
82
83
 
83
- its(:options) { is_expected.to include(preset: 'avalon') }
84
- its(:current_operations) { is_expected.to include("Cleaning up") }
84
+ it { expect(subject.options).to include(preset: 'avalon') }
85
+ it { expect(subject.current_operations).to include("Cleaning up") }
85
86
  end
86
87
  context "a failed encode" do
87
88
  subject { failed_job }
88
89
 
89
- its(:options) { is_expected.to include(preset: 'error') }
90
- its(:current_operations) { is_expected.to include("Cleaning up after failure") }
90
+ it { expect(subject.options).to include(preset: 'error') }
91
+ it { expect(subject.current_operations).to include("Cleaning up after failure") }
91
92
  end
92
93
  end
93
94
 
94
95
  describe "#cancel!" do
95
- let(:encode) { ActiveEncode::Base.create(file) }
96
96
  subject { encode.cancel! }
97
+ let(:encode) { ActiveEncode::Base.create(file) }
97
98
 
98
99
  it { is_expected.to be_a ActiveEncode::Base }
99
- its(:id) { is_expected.to eq 'cancelled-id' }
100
+ it { expect(subject.id).to eq 'cancelled-id' }
100
101
  it { is_expected.to be_cancelled }
101
102
  end
102
103
 
@@ -107,24 +108,24 @@ describe ActiveEncode::EngineAdapters::MatterhornAdapter do
107
108
 
108
109
  subject { running_job.reload }
109
110
 
110
- its(:output) { is_expected.to be_empty }
111
- its(:options) { is_expected.to include(preset: 'full') }
112
- its(:current_operations) { is_expected.to include("Hold for workflow selection") }
113
- its(:percent_complete) { is_expected.to eq 0.43478260869565216 }
111
+ it { expect(subject.output).to be_empty }
112
+ it { expect(subject.options).to include(preset: 'full') }
113
+ it { expect(subject.current_operations).to include("Hold for workflow selection") }
114
+ it { expect(subject.percent_complete).to eq 0.43478260869565216 }
114
115
 
115
116
  context 'input' do
116
117
  subject { running_job.reload.input }
117
118
 
118
- its(:width) { is_expected.to be_blank }
119
- its(:height) { is_expected.to be_blank }
120
- its(:frame_rate) { is_expected.to be_blank }
121
- its(:duration) { is_expected.to be_blank }
122
- its(:file_size) { is_expected.to be_blank }
123
- its(:checksum) { is_expected.to be_blank }
124
- its(:audio_codec) { is_expected.to be_blank }
125
- its(:video_codec) { is_expected.to be_blank }
126
- its(:audio_bitrate) { is_expected.to be_blank }
127
- its(:video_bitrate) { is_expected.to be_blank }
119
+ it { expect(subject.width).to be_blank }
120
+ it { expect(subject.height).to be_blank }
121
+ it { expect(subject.frame_rate).to be_blank }
122
+ it { expect(subject.duration).to be_blank }
123
+ it { expect(subject.file_size).to be_blank }
124
+ it { expect(subject.checksum).to be_blank }
125
+ it { expect(subject.audio_codec).to be_blank }
126
+ it { expect(subject.video_codec).to be_blank }
127
+ it { expect(subject.audio_bitrate).to be_blank }
128
+ it { expect(subject.video_bitrate).to be_blank }
128
129
  end
129
130
  end
130
131
  end