active_encode 0.5.0 → 0.8.1

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.
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