active_encode 1.0.0 → 1.1.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 +4 -4
- data/lib/active_encode/engine_adapters/elastic_transcoder_adapter.rb +5 -3
- data/lib/active_encode/engine_adapters/ffmpeg_adapter.rb +9 -14
- data/lib/active_encode/engine_adapters/media_convert_adapter.rb +9 -3
- data/lib/active_encode/engine_adapters/pass_through_adapter.rb +1 -5
- data/lib/active_encode/filename_sanitizer.rb +22 -0
- data/lib/active_encode/version.rb +1 -1
- data/lib/active_encode.rb +5 -0
- data/spec/fixtures//"file_with_double_quote/".low.mp4 +0 -0
- data/spec/fixtures//"file_with_double_quote/".mp4 +0 -0
- data/spec/fixtures/'file_with_single_quote'.low.mp4 +0 -0
- data/spec/fixtures/'file_with_single_quote'.mp4 +0 -0
- data/spec/fixtures/ffmpeg/incomplete-id/error.log +0 -0
- data/spec/fixtures/ffmpeg/incomplete-id/exit_status.code +1 -0
- data/spec/fixtures/ffmpeg/incomplete-id/input_metadata +102 -0
- data/spec/fixtures/ffmpeg/incomplete-id/output_metadata-high +90 -0
- data/spec/fixtures/ffmpeg/incomplete-id/output_metadata-low +90 -0
- data/spec/fixtures/ffmpeg/incomplete-id/pid +1 -0
- data/spec/fixtures/ffmpeg/incomplete-id/progress +11 -0
- data/spec/fixtures/ffmpeg/incomplete-id/video-high.mp4 +0 -0
- data/spec/fixtures/ffmpeg/incomplete-id/video-low.mp4 +0 -0
- data/spec/fixtures/file.with :=+%sp3c!l-ch4cts().mp4 +0 -0
- data/spec/fixtures/file.with...periods.mp4 +0 -0
- data/spec/integration/elastic_transcoder_adapter_spec.rb +48 -0
- data/spec/integration/ffmpeg_adapter_spec.rb +124 -0
- data/spec/integration/media_convert_adapter_spec.rb +51 -0
- data/spec/integration/pass_through_adapter_spec.rb +84 -0
- metadata +33 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 165b612fc067a4f2513bb6090876f015216dcb69986416f580256b58ee5979d0
|
4
|
+
data.tar.gz: 8730c65eb2b3f38a22f245992c4885a494f83bcff169af6746b35f2d2d75a5bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz: '
|
6
|
+
metadata.gz: '094791342ae22c10e682762e61a8f83934a742fa326746a5733a304cf14a657dc048560e251734b06a9833d7fe7b978fed0196361e8cca199e017b195d8e239f'
|
7
|
+
data.tar.gz: '087bff74b89364c3e2795a104038324846d7bf10e33e3e239a901db189445273cd7dc40f252a555cdb800d9adf82457ccb055fc3d2bcf0184ff250c891b7ff02'
|
@@ -140,9 +140,10 @@ module ActiveEncode
|
|
140
140
|
# logger.info("Already in bucket `#{source_bucket}'")
|
141
141
|
s3_object.key
|
142
142
|
else
|
143
|
-
|
143
|
+
cleaned_url = ActiveEncode.sanitize_filename input_url
|
144
|
+
s3_key = File.join(SecureRandom.uuid, File.basename(cleaned_url))
|
144
145
|
# logger.info("Copying to `#{source_bucket}/#{input_url}'")
|
145
|
-
target = Aws::S3::Object.new(bucket_name: source_bucket, key:
|
146
|
+
target = Aws::S3::Object.new(bucket_name: source_bucket, key: s3_key)
|
146
147
|
target.copy_from(s3_object, multipart_copy: s3_object.size > 15_728_640) # 15.megabytes
|
147
148
|
s3_key
|
148
149
|
end
|
@@ -152,7 +153,8 @@ module ActiveEncode
|
|
152
153
|
# original_input = input_url
|
153
154
|
bucket = Aws::S3::Resource.new(client: s3client).bucket(source_bucket)
|
154
155
|
filename = FileLocator.new(input_url).location
|
155
|
-
|
156
|
+
cleaned_url = ActiveEncode.sanitize_filename input_url
|
157
|
+
s3_key = File.join(SecureRandom.uuid, File.basename(cleaned_url))
|
156
158
|
# logger.info("Copying `#{original_input}' to `#{source_bucket}/#{input_url}'")
|
157
159
|
obj = bucket.object(s3_key)
|
158
160
|
obj.upload_file filename
|
@@ -11,11 +11,14 @@ module ActiveEncode
|
|
11
11
|
MEDIAINFO_PATH = ENV["MEDIAINFO_PATH"] || "mediainfo"
|
12
12
|
FFMPEG_PATH = ENV["FFMPEG_PATH"] || "ffmpeg"
|
13
13
|
|
14
|
+
class_attribute :completeness_threshold
|
15
|
+
|
14
16
|
def create(input_url, options = {})
|
15
17
|
# Decode file uris for ffmpeg (mediainfo works either way)
|
16
18
|
case input_url
|
17
19
|
when /^file\:\/\/\//
|
18
20
|
input_url = Addressable::URI.unencode(input_url)
|
21
|
+
input_url = ActiveEncode.sanitize_input(input_url)
|
19
22
|
when /^s3\:\/\//
|
20
23
|
require 'file_locator'
|
21
24
|
|
@@ -110,8 +113,8 @@ module ActiveEncode
|
|
110
113
|
encode.state = :completed
|
111
114
|
elsif cancelled? encode.id
|
112
115
|
encode.state = :cancelled
|
113
|
-
elsif encode.percent_complete < 100
|
114
|
-
encode.errors
|
116
|
+
elsif encode.percent_complete < (completeness_threshold || 100)
|
117
|
+
encode.errors.prepend("Encoding has completed but the output duration is shorter than the input")
|
115
118
|
encode.state = :failed
|
116
119
|
end
|
117
120
|
|
@@ -170,7 +173,7 @@ module ActiveEncode
|
|
170
173
|
err_path = working_path("error.log", id)
|
171
174
|
error = File.read(err_path) if File.file? err_path
|
172
175
|
if error.present?
|
173
|
-
[error]
|
176
|
+
["Error encountered during encoding. Check ffmpeg log for more details.", error]
|
174
177
|
else
|
175
178
|
[]
|
176
179
|
end
|
@@ -200,7 +203,7 @@ module ActiveEncode
|
|
200
203
|
Dir["#{File.absolute_path(working_path('outputs', id))}/*"].each do |file_path|
|
201
204
|
output = ActiveEncode::Output.new
|
202
205
|
output.url = "file://#{file_path}"
|
203
|
-
sanitized_filename = sanitize_base encode.input.url
|
206
|
+
sanitized_filename = ActiveEncode.sanitize_base encode.input.url
|
204
207
|
output.label = file_path[/#{Regexp.quote(sanitized_filename)}\-(.*?)#{Regexp.quote(File.extname(file_path))}$/, 1]
|
205
208
|
output.id = "#{encode.input.id}-#{output.label}"
|
206
209
|
output.created_at = encode.created_at
|
@@ -219,7 +222,7 @@ module ActiveEncode
|
|
219
222
|
|
220
223
|
def ffmpeg_command(input_url, id, opts)
|
221
224
|
output_opt = opts[:outputs].collect do |output|
|
222
|
-
sanitized_filename = sanitize_base input_url
|
225
|
+
sanitized_filename = ActiveEncode.sanitize_base input_url
|
223
226
|
file_name = "outputs/#{sanitized_filename}-#{output[:label]}.#{output[:extension]}"
|
224
227
|
" #{output[:ffmpeg_opt]} #{working_path(file_name, id)}"
|
225
228
|
end.join(" ")
|
@@ -227,15 +230,7 @@ module ActiveEncode
|
|
227
230
|
"#{k}: #{v}\r\n"
|
228
231
|
end.join
|
229
232
|
header_opt = "-headers '#{header_opt}'" if header_opt.present?
|
230
|
-
"#{FFMPEG_PATH} #{header_opt} -y -loglevel
|
231
|
-
end
|
232
|
-
|
233
|
-
def sanitize_base(input_url)
|
234
|
-
if input_url.is_a? URI::HTTP
|
235
|
-
File.basename(input_url.path, File.extname(input_url.path))
|
236
|
-
else
|
237
|
-
File.basename(input_url, File.extname(input_url)).gsub(/[^0-9A-Za-z.\-]/, '_')
|
238
|
-
end
|
233
|
+
"#{FFMPEG_PATH} #{header_opt} -y -loglevel level+fatal -progress #{working_path('progress', id)} -i \"#{input_url}\" #{output_opt}"
|
239
234
|
end
|
240
235
|
|
241
236
|
def get_pid(id)
|
@@ -482,6 +482,10 @@ module ActiveEncode
|
|
482
482
|
end
|
483
483
|
end
|
484
484
|
|
485
|
+
def s3client
|
486
|
+
Aws::S3::Client.new
|
487
|
+
end
|
488
|
+
|
485
489
|
def s3_uri(url, options = {})
|
486
490
|
bucket = options[:masterfile_bucket]
|
487
491
|
|
@@ -503,9 +507,10 @@ module ActiveEncode
|
|
503
507
|
# logger.info("Already in bucket `#{source_bucket}'")
|
504
508
|
s3_object.key
|
505
509
|
else
|
506
|
-
|
510
|
+
cleaned_url = ActiveEncode.sanitize_filename input_url
|
511
|
+
s3_key = File.join(SecureRandom.uuid, File.basename(cleaned_url))
|
507
512
|
# logger.info("Copying to `#{source_bucket}/#{input_url}'")
|
508
|
-
target = Aws::S3::Object.new(bucket_name: source_bucket, key:
|
513
|
+
target = Aws::S3::Object.new(bucket_name: source_bucket, key: s3_key)
|
509
514
|
target.copy_from(s3_object, multipart_copy: s3_object.size > 15_728_640) # 15.megabytes
|
510
515
|
s3_key
|
511
516
|
end
|
@@ -515,7 +520,8 @@ module ActiveEncode
|
|
515
520
|
# original_input = input_url
|
516
521
|
bucket = Aws::S3::Resource.new(client: s3client).bucket(source_bucket)
|
517
522
|
filename = FileLocator.new(input_url).location
|
518
|
-
|
523
|
+
cleaned_url = ActiveEncode.sanitize_filename input_url
|
524
|
+
s3_key = File.join(SecureRandom.uuid, File.basename(cleaned_url))
|
519
525
|
# logger.info("Copying `#{original_input}' to `#{source_bucket}/#{input_url}'")
|
520
526
|
obj = bucket.object(s3_key)
|
521
527
|
obj.upload_file filename
|
@@ -55,7 +55,7 @@ module ActiveEncode
|
|
55
55
|
# Copy derivatives to work directory
|
56
56
|
options[:outputs].each do |opt|
|
57
57
|
url = opt[:url]
|
58
|
-
output_path = working_path("outputs/#{sanitize_base opt[:url]}#{File.extname opt[:url]}", new_encode.id)
|
58
|
+
output_path = working_path("outputs/#{ActiveEncode.sanitize_base opt[:url]}#{File.extname opt[:url]}", new_encode.id)
|
59
59
|
FileUtils.cp FileLocator.new(url).location, output_path
|
60
60
|
filename_label_hash[output_path] = opt[:label]
|
61
61
|
end
|
@@ -206,10 +206,6 @@ module ActiveEncode
|
|
206
206
|
outputs
|
207
207
|
end
|
208
208
|
|
209
|
-
def sanitize_base(input_url)
|
210
|
-
File.basename(input_url, File.extname(input_url)).gsub(/[^0-9A-Za-z.\-]/, '_')
|
211
|
-
end
|
212
|
-
|
213
209
|
def working_path(path, id)
|
214
210
|
File.join(WORK_DIR, id, path)
|
215
211
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ActiveEncode
|
3
|
+
module FilenameSanitizer
|
4
|
+
# ffmpeg has trouble with double quotes in file names. Escape them to get ffmpeg to find the file.
|
5
|
+
def sanitize_input(input_url)
|
6
|
+
input_url.gsub(/["]/, '\\\\\0')
|
7
|
+
end
|
8
|
+
|
9
|
+
def sanitize_base(input_url)
|
10
|
+
filepath = input_url.is_a?(URI::HTTP) ? input_url.path : input_url
|
11
|
+
# Replace special characters with underscores and remove excess periods.
|
12
|
+
# This removes the extension before processing so it is safe to delete all detected periods.
|
13
|
+
File.basename(filepath, File.extname(filepath)).gsub(/[^0-9A-Za-z.\-\/]/, '_').delete('.')
|
14
|
+
end
|
15
|
+
|
16
|
+
def sanitize_filename(input_url)
|
17
|
+
filepath = input_url.is_a?(URI::HTTP) ? input_url.path : input_url
|
18
|
+
# Replace special characters with underscores and remove excess periods.
|
19
|
+
File.basename(filepath).gsub(/[^0-9A-Za-z.\-\/]/, '_').gsub(/\.(?=.*\.)/, '')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/active_encode.rb
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
0
|
@@ -0,0 +1,102 @@
|
|
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="/home/pdinh/Downloads/videoshort.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>mp42</CodecID>
|
16
|
+
<FileSize>199160</FileSize>
|
17
|
+
<Duration>6.315</Duration>
|
18
|
+
<OverallBitRate_Mode>VBR</OverallBitRate_Mode>
|
19
|
+
<OverallBitRate>252301</OverallBitRate>
|
20
|
+
<FrameRate>23.719</FrameRate>
|
21
|
+
<FrameCount>149</FrameCount>
|
22
|
+
<StreamSize>5679</StreamSize>
|
23
|
+
<HeaderSize>160</HeaderSize>
|
24
|
+
<DataSize>193489</DataSize>
|
25
|
+
<FooterSize>5511</FooterSize>
|
26
|
+
<IsStreamable>No</IsStreamable>
|
27
|
+
<Encoded_Date>UTC 2010-09-23 00:37:25</Encoded_Date>
|
28
|
+
<Tagged_Date>UTC 2010-09-23 00:37:27</Tagged_Date>
|
29
|
+
<File_Modified_Date>UTC 2017-12-14 19:29:35</File_Modified_Date>
|
30
|
+
<File_Modified_Date_Local>2017-12-14 14:29:35</File_Modified_Date_Local>
|
31
|
+
<Encoded_Application>HandBrake 0.9.4 2009112300</Encoded_Application>
|
32
|
+
</track>
|
33
|
+
<track type="Video">
|
34
|
+
<StreamOrder>0</StreamOrder>
|
35
|
+
<ID>1</ID>
|
36
|
+
<Format>AVC</Format>
|
37
|
+
<Format_Profile>Main</Format_Profile>
|
38
|
+
<Format_Level>1.1</Format_Level>
|
39
|
+
<Format_Settings_CABAC>Yes</Format_Settings_CABAC>
|
40
|
+
<Format_Settings_RefFrames>2</Format_Settings_RefFrames>
|
41
|
+
<CodecID>avc1</CodecID>
|
42
|
+
<Duration>6.282</Duration>
|
43
|
+
<BitRate>74477</BitRate>
|
44
|
+
<Width>200</Width>
|
45
|
+
<Height>110</Height>
|
46
|
+
<Stored_Width>208</Stored_Width>
|
47
|
+
<Stored_Height>112</Stored_Height>
|
48
|
+
<Sampled_Width>200</Sampled_Width>
|
49
|
+
<Sampled_Height>110</Sampled_Height>
|
50
|
+
<PixelAspectRatio>1.000</PixelAspectRatio>
|
51
|
+
<DisplayAspectRatio>1.818</DisplayAspectRatio>
|
52
|
+
<Rotation>0.000</Rotation>
|
53
|
+
<FrameRate_Mode>VFR</FrameRate_Mode>
|
54
|
+
<FrameRate>23.719</FrameRate>
|
55
|
+
<FrameRate_Minimum>12.500</FrameRate_Minimum>
|
56
|
+
<FrameRate_Maximum>24.390</FrameRate_Maximum>
|
57
|
+
<FrameRate_Original>24.000</FrameRate_Original>
|
58
|
+
<FrameCount>149</FrameCount>
|
59
|
+
<ColorSpace>YUV</ColorSpace>
|
60
|
+
<ChromaSubsampling>4:2:0</ChromaSubsampling>
|
61
|
+
<BitDepth>8</BitDepth>
|
62
|
+
<ScanType>Progressive</ScanType>
|
63
|
+
<StreamSize>58482</StreamSize>
|
64
|
+
<Encoded_Library>x264 - core 79 r1347 5ddd61b</Encoded_Library>
|
65
|
+
<Encoded_Library_Name>x264</Encoded_Library_Name>
|
66
|
+
<Encoded_Library_Version>core 79 r1347 5ddd61b</Encoded_Library_Version>
|
67
|
+
<Encoded_Library_Settings>cabac=1 / ref=2 / deblock=1:0:0 / analyse=0x1:0x111 / me=hex / subme=6 / psy=1 / psy_rd=1.0:0.0 / mixed_ref=0 / me_range=16 / chroma_me=1 / trellis=0 / 8x8dct=0 / cqm=0 / deadzone=21,11 / chroma_qp_offset=-2 / threads=3 / nr=0 / decimate=1 / mbaff=0 / constrained_intra=0 / bframes=2 / b_pyramid=0 / b_adapt=1 / b_bias=0 / direct=1 / wpredb=0 / wpredp=2 / keyint=240 / keyint_min=24 / scenecut=40 / rc_lookahead=40 / rc=crf / mbtree=1 / crf=25.5 / qcomp=0.60 / qpmin=10 / qpmax=51 / qpstep=4 / ip_ratio=1.40 / aq=1:1.00</Encoded_Library_Settings>
|
68
|
+
<Encoded_Date>UTC 2010-09-23 00:37:25</Encoded_Date>
|
69
|
+
<Tagged_Date>UTC 2010-09-23 00:37:27</Tagged_Date>
|
70
|
+
<colour_range>Limited</colour_range>
|
71
|
+
<colour_description_present>Yes</colour_description_present>
|
72
|
+
<colour_primaries>BT.601 NTSC</colour_primaries>
|
73
|
+
<transfer_characteristics>BT.709</transfer_characteristics>
|
74
|
+
<matrix_coefficients>BT.601</matrix_coefficients>
|
75
|
+
</track>
|
76
|
+
<track type="Audio">
|
77
|
+
<StreamOrder>1</StreamOrder>
|
78
|
+
<ID>2</ID>
|
79
|
+
<Format>AAC</Format>
|
80
|
+
<Format_Profile>LC</Format_Profile>
|
81
|
+
<CodecID>mp4a-40-2</CodecID>
|
82
|
+
<Duration>6.315</Duration>
|
83
|
+
<BitRate_Mode>VBR</BitRate_Mode>
|
84
|
+
<BitRate>171030</BitRate>
|
85
|
+
<BitRate_Maximum>201736</BitRate_Maximum>
|
86
|
+
<Channels>1</Channels>
|
87
|
+
<ChannelPositions>Front: C</ChannelPositions>
|
88
|
+
<ChannelLayout>C</ChannelLayout>
|
89
|
+
<SamplesPerFrame>1024</SamplesPerFrame>
|
90
|
+
<SamplingRate>48000</SamplingRate>
|
91
|
+
<SamplingCount>303120</SamplingCount>
|
92
|
+
<FrameRate>46.875</FrameRate>
|
93
|
+
<FrameCount>296</FrameCount>
|
94
|
+
<Compression_Mode>Lossy</Compression_Mode>
|
95
|
+
<StreamSize>134999</StreamSize>
|
96
|
+
<StreamSize_Proportion>0.67784</StreamSize_Proportion>
|
97
|
+
<Title>Stereo</Title>
|
98
|
+
<Encoded_Date>UTC 2010-09-23 00:37:25</Encoded_Date>
|
99
|
+
<Tagged_Date>UTC 2010-09-23 00:37:27</Tagged_Date>
|
100
|
+
</track>
|
101
|
+
</media>
|
102
|
+
</MediaInfo>
|
@@ -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="spec/fixtures/ffmpeg/completed-id/low.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>125403</FileSize>
|
17
|
+
<Duration>6.336</Duration>
|
18
|
+
<OverallBitRate>158337</OverallBitRate>
|
19
|
+
<FrameRate>24.000</FrameRate>
|
20
|
+
<FrameCount>150</FrameCount>
|
21
|
+
<StreamSize>5648</StreamSize>
|
22
|
+
<HeaderSize>40</HeaderSize>
|
23
|
+
<DataSize>119763</DataSize>
|
24
|
+
<FooterSize>5600</FooterSize>
|
25
|
+
<IsStreamable>No</IsStreamable>
|
26
|
+
<File_Modified_Date>UTC 2018-09-07 17:36:26</File_Modified_Date>
|
27
|
+
<File_Modified_Date_Local>2018-09-07 13:36:26</File_Modified_Date_Local>
|
28
|
+
<Encoded_Application>Lavf58.12.100</Encoded_Application>
|
29
|
+
</track>
|
30
|
+
<track type="Video">
|
31
|
+
<StreamOrder>0</StreamOrder>
|
32
|
+
<ID>1</ID>
|
33
|
+
<Format>AVC</Format>
|
34
|
+
<Format_Profile>High</Format_Profile>
|
35
|
+
<Format_Level>1.1</Format_Level>
|
36
|
+
<Format_Settings_CABAC>Yes</Format_Settings_CABAC>
|
37
|
+
<Format_Settings_RefFrames>4</Format_Settings_RefFrames>
|
38
|
+
<CodecID>avc1</CodecID>
|
39
|
+
<Duration>6.250</Duration>
|
40
|
+
<BitRate>79302</BitRate>
|
41
|
+
<Width>200</Width>
|
42
|
+
<Height>110</Height>
|
43
|
+
<Stored_Width>208</Stored_Width>
|
44
|
+
<Stored_Height>112</Stored_Height>
|
45
|
+
<Sampled_Width>200</Sampled_Width>
|
46
|
+
<Sampled_Height>110</Sampled_Height>
|
47
|
+
<PixelAspectRatio>1.000</PixelAspectRatio>
|
48
|
+
<DisplayAspectRatio>1.818</DisplayAspectRatio>
|
49
|
+
<Rotation>0.000</Rotation>
|
50
|
+
<FrameRate_Mode>CFR</FrameRate_Mode>
|
51
|
+
<FrameRate_Mode_Original>VFR</FrameRate_Mode_Original>
|
52
|
+
<FrameRate>24.000</FrameRate>
|
53
|
+
<FrameCount>150</FrameCount>
|
54
|
+
<ColorSpace>YUV</ColorSpace>
|
55
|
+
<ChromaSubsampling>4:2:0</ChromaSubsampling>
|
56
|
+
<BitDepth>8</BitDepth>
|
57
|
+
<ScanType>Progressive</ScanType>
|
58
|
+
<StreamSize>61955</StreamSize>
|
59
|
+
<Encoded_Library>x264 - core 152 r2854 e9a5903</Encoded_Library>
|
60
|
+
<Encoded_Library_Name>x264</Encoded_Library_Name>
|
61
|
+
<Encoded_Library_Version>core 152 r2854 e9a5903</Encoded_Library_Version>
|
62
|
+
<Encoded_Library_Settings>cabac=1 / ref=3 / deblock=1:0:0 / analyse=0x3:0x113 / me=hex / subme=7 / psy=1 / psy_rd=1.00:0.00 / mixed_ref=1 / me_range=16 / chroma_me=1 / trellis=1 / 8x8dct=1 / cqm=0 / deadzone=21,11 / fast_pskip=1 / chroma_qp_offset=-2 / threads=3 / lookahead_threads=1 / sliced_threads=0 / nr=0 / decimate=1 / interlaced=0 / bluray_compat=0 / constrained_intra=0 / bframes=3 / b_pyramid=2 / b_adapt=1 / b_bias=0 / direct=1 / weightb=1 / open_gop=0 / weightp=2 / keyint=250 / keyint_min=24 / scenecut=40 / intra_refresh=0 / rc_lookahead=40 / rc=crf / mbtree=1 / crf=23.0 / qcomp=0.60 / qpmin=0 / qpmax=69 / qpstep=4 / ip_ratio=1.40 / aq=1:1.00</Encoded_Library_Settings>
|
63
|
+
</track>
|
64
|
+
<track type="Audio">
|
65
|
+
<StreamOrder>1</StreamOrder>
|
66
|
+
<ID>2</ID>
|
67
|
+
<Format>AAC</Format>
|
68
|
+
<Format_Profile>LC</Format_Profile>
|
69
|
+
<Format_Settings_SBR>No (Explicit)</Format_Settings_SBR>
|
70
|
+
<CodecID>mp4a-40-2</CodecID>
|
71
|
+
<Duration>6.336</Duration>
|
72
|
+
<BitRate_Mode>CBR</BitRate_Mode>
|
73
|
+
<BitRate>72000</BitRate>
|
74
|
+
<Channels>2</Channels>
|
75
|
+
<Channels_Original>1</Channels_Original>
|
76
|
+
<ChannelPositions>Front: C</ChannelPositions>
|
77
|
+
<ChannelLayout>C</ChannelLayout>
|
78
|
+
<SamplesPerFrame>1024</SamplesPerFrame>
|
79
|
+
<SamplingRate>48000</SamplingRate>
|
80
|
+
<SamplingCount>304128</SamplingCount>
|
81
|
+
<FrameRate>46.875</FrameRate>
|
82
|
+
<FrameCount>297</FrameCount>
|
83
|
+
<Compression_Mode>Lossy</Compression_Mode>
|
84
|
+
<StreamSize>57800</StreamSize>
|
85
|
+
<StreamSize_Proportion>0.46091</StreamSize_Proportion>
|
86
|
+
<Default>Yes</Default>
|
87
|
+
<AlternateGroup>1</AlternateGroup>
|
88
|
+
</track>
|
89
|
+
</media>
|
90
|
+
</MediaInfo>
|
@@ -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="spec/fixtures/ffmpeg/completed-id/low.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>125403</FileSize>
|
17
|
+
<Duration>6.336</Duration>
|
18
|
+
<OverallBitRate>158337</OverallBitRate>
|
19
|
+
<FrameRate>24.000</FrameRate>
|
20
|
+
<FrameCount>150</FrameCount>
|
21
|
+
<StreamSize>5648</StreamSize>
|
22
|
+
<HeaderSize>40</HeaderSize>
|
23
|
+
<DataSize>119763</DataSize>
|
24
|
+
<FooterSize>5600</FooterSize>
|
25
|
+
<IsStreamable>No</IsStreamable>
|
26
|
+
<File_Modified_Date>UTC 2018-09-07 17:36:26</File_Modified_Date>
|
27
|
+
<File_Modified_Date_Local>2018-09-07 13:36:26</File_Modified_Date_Local>
|
28
|
+
<Encoded_Application>Lavf58.12.100</Encoded_Application>
|
29
|
+
</track>
|
30
|
+
<track type="Video">
|
31
|
+
<StreamOrder>0</StreamOrder>
|
32
|
+
<ID>1</ID>
|
33
|
+
<Format>AVC</Format>
|
34
|
+
<Format_Profile>High</Format_Profile>
|
35
|
+
<Format_Level>1.1</Format_Level>
|
36
|
+
<Format_Settings_CABAC>Yes</Format_Settings_CABAC>
|
37
|
+
<Format_Settings_RefFrames>4</Format_Settings_RefFrames>
|
38
|
+
<CodecID>avc1</CodecID>
|
39
|
+
<Duration>6.250</Duration>
|
40
|
+
<BitRate>79302</BitRate>
|
41
|
+
<Width>200</Width>
|
42
|
+
<Height>110</Height>
|
43
|
+
<Stored_Width>208</Stored_Width>
|
44
|
+
<Stored_Height>112</Stored_Height>
|
45
|
+
<Sampled_Width>200</Sampled_Width>
|
46
|
+
<Sampled_Height>110</Sampled_Height>
|
47
|
+
<PixelAspectRatio>1.000</PixelAspectRatio>
|
48
|
+
<DisplayAspectRatio>1.818</DisplayAspectRatio>
|
49
|
+
<Rotation>0.000</Rotation>
|
50
|
+
<FrameRate_Mode>CFR</FrameRate_Mode>
|
51
|
+
<FrameRate_Mode_Original>VFR</FrameRate_Mode_Original>
|
52
|
+
<FrameRate>24.000</FrameRate>
|
53
|
+
<FrameCount>150</FrameCount>
|
54
|
+
<ColorSpace>YUV</ColorSpace>
|
55
|
+
<ChromaSubsampling>4:2:0</ChromaSubsampling>
|
56
|
+
<BitDepth>8</BitDepth>
|
57
|
+
<ScanType>Progressive</ScanType>
|
58
|
+
<StreamSize>61955</StreamSize>
|
59
|
+
<Encoded_Library>x264 - core 152 r2854 e9a5903</Encoded_Library>
|
60
|
+
<Encoded_Library_Name>x264</Encoded_Library_Name>
|
61
|
+
<Encoded_Library_Version>core 152 r2854 e9a5903</Encoded_Library_Version>
|
62
|
+
<Encoded_Library_Settings>cabac=1 / ref=3 / deblock=1:0:0 / analyse=0x3:0x113 / me=hex / subme=7 / psy=1 / psy_rd=1.00:0.00 / mixed_ref=1 / me_range=16 / chroma_me=1 / trellis=1 / 8x8dct=1 / cqm=0 / deadzone=21,11 / fast_pskip=1 / chroma_qp_offset=-2 / threads=3 / lookahead_threads=1 / sliced_threads=0 / nr=0 / decimate=1 / interlaced=0 / bluray_compat=0 / constrained_intra=0 / bframes=3 / b_pyramid=2 / b_adapt=1 / b_bias=0 / direct=1 / weightb=1 / open_gop=0 / weightp=2 / keyint=250 / keyint_min=24 / scenecut=40 / intra_refresh=0 / rc_lookahead=40 / rc=crf / mbtree=1 / crf=23.0 / qcomp=0.60 / qpmin=0 / qpmax=69 / qpstep=4 / ip_ratio=1.40 / aq=1:1.00</Encoded_Library_Settings>
|
63
|
+
</track>
|
64
|
+
<track type="Audio">
|
65
|
+
<StreamOrder>1</StreamOrder>
|
66
|
+
<ID>2</ID>
|
67
|
+
<Format>AAC</Format>
|
68
|
+
<Format_Profile>LC</Format_Profile>
|
69
|
+
<Format_Settings_SBR>No (Explicit)</Format_Settings_SBR>
|
70
|
+
<CodecID>mp4a-40-2</CodecID>
|
71
|
+
<Duration>6.336</Duration>
|
72
|
+
<BitRate_Mode>CBR</BitRate_Mode>
|
73
|
+
<BitRate>72000</BitRate>
|
74
|
+
<Channels>2</Channels>
|
75
|
+
<Channels_Original>1</Channels_Original>
|
76
|
+
<ChannelPositions>Front: C</ChannelPositions>
|
77
|
+
<ChannelLayout>C</ChannelLayout>
|
78
|
+
<SamplesPerFrame>1024</SamplesPerFrame>
|
79
|
+
<SamplingRate>48000</SamplingRate>
|
80
|
+
<SamplingCount>304128</SamplingCount>
|
81
|
+
<FrameRate>46.875</FrameRate>
|
82
|
+
<FrameCount>297</FrameCount>
|
83
|
+
<Compression_Mode>Lossy</Compression_Mode>
|
84
|
+
<StreamSize>57800</StreamSize>
|
85
|
+
<StreamSize_Proportion>0.46091</StreamSize_Proportion>
|
86
|
+
<Default>Yes</Default>
|
87
|
+
<AlternateGroup>1</AlternateGroup>
|
88
|
+
</track>
|
89
|
+
</media>
|
90
|
+
</MediaInfo>
|
@@ -0,0 +1 @@
|
|
1
|
+
99999
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -137,6 +137,54 @@ describe ActiveEncode::EngineAdapters::ElasticTranscoderAdapter do
|
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
140
|
+
describe "#copy_to_input_bucket" do
|
141
|
+
context "when filename has no special characters" do
|
142
|
+
context "non-s3 file" do
|
143
|
+
let(:input_url) { "spec/fixtures/fireworks.mp4" }
|
144
|
+
let(:source_bucket) { "bucket1" }
|
145
|
+
|
146
|
+
it "calls the #upload_to_s3 method" do
|
147
|
+
allow(SecureRandom).to receive(:uuid).and_return("randomstring")
|
148
|
+
expect(described_class.new.send(:copy_to_input_bucket, input_url, source_bucket)).to eq "randomstring/fireworks.mp4"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
context "s3 file" do
|
152
|
+
let(:input_url) { "s3://bucket1/file.mp4" }
|
153
|
+
let(:source_bucket) { "bucket1" }
|
154
|
+
|
155
|
+
it "calls the #check_s3_bucket method" do
|
156
|
+
expect(described_class.new.send(:copy_to_input_bucket, input_url, source_bucket)).to eq "file.mp4"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
context "when filename has special characters" do
|
161
|
+
context "non-s3 file" do
|
162
|
+
let(:input) { ["'file_with_single_quote'.mp4", '"file_with_double_quote".mp4', "file with space.mp4", "file.with...periods.mp4", "file.with :=+%sp3c!l-ch4cts().mp4"] }
|
163
|
+
let(:clean) { ["_file_with_single_quote_.mp4", "_file_with_double_quote_.mp4", "file_with_space.mp4", "filewithperiods.mp4", "filewith_____sp3c_l-ch4cts__.mp4"] }
|
164
|
+
let(:source_bucket) { "bucket1" }
|
165
|
+
|
166
|
+
it "calls the #upload_to_s3 method" do
|
167
|
+
allow(SecureRandom).to receive(:uuid).and_return("randomstring")
|
168
|
+
input.each_with_index do |url, index|
|
169
|
+
expect(described_class.new.send(:copy_to_input_bucket, "spec/fixtures/#{url}", source_bucket)).to eq "randomstring/#{clean[index]}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
context "s3 file" do
|
174
|
+
let(:input_urls) { ["s3://bucket1/'file_with_single_quote'.mp4", 's3://bucket1/"file_with_double_quote".mp4', "s3://bucket1/file with space.mp4", "s3://bucket1/file.with...periods.mp4", "s3://bucket1/file.with :=+%sp3c!l-ch4cts().mp4"] }
|
175
|
+
let(:clean) { ["_file_with_single_quote_.mp4", "_file_with_double_quote_.mp4", "file_with_space.mp4", "filewithperiods.mp4", "filewith_____sp3c_l-ch4cts__.mp4"] }
|
176
|
+
let(:source_bucket) { "bucket2" }
|
177
|
+
|
178
|
+
it "calls the #check_s3_bucket method" do
|
179
|
+
allow(SecureRandom).to receive(:uuid).and_return("randomstring")
|
180
|
+
input_urls.each_with_index do |url, index|
|
181
|
+
expect(described_class.new.send(:copy_to_input_bucket, url, source_bucket)).to eq "randomstring/#{clean[index]}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
140
188
|
describe "#check_s3_bucket" do
|
141
189
|
context "when file exists in masterfile_bucket" do
|
142
190
|
let(:input_url) { "s3://bucket1/file.mp4" }
|
@@ -38,6 +38,7 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
|
|
38
38
|
end
|
39
39
|
let(:completed_job) { find_encode "completed-id" }
|
40
40
|
let(:completed_with_warnings_job) { find_encode "completed-with-warnings-id" }
|
41
|
+
let(:incomplete_job) { find_encode "incomplete-id" }
|
41
42
|
let(:failed_job) { find_encode 'failed-id' }
|
42
43
|
let(:completed_tech_metadata) do
|
43
44
|
{
|
@@ -147,6 +148,114 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
|
|
147
148
|
end
|
148
149
|
end
|
149
150
|
|
151
|
+
context "input filename with single quotes" do
|
152
|
+
let(:file_with_single_quote) { "file://" + Rails.root.join('..', 'spec', 'fixtures', "'file_with_single_quote'.mp4").to_s }
|
153
|
+
let!(:create_single_quote_job) { ActiveEncode::Base.create(file_with_single_quote, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
|
154
|
+
let(:find_single_quote_job) { ActiveEncode::Base.find create_single_quote_job.id }
|
155
|
+
|
156
|
+
it "does not have errors" do
|
157
|
+
sleep 2
|
158
|
+
expect(find_single_quote_job.errors).to be_empty
|
159
|
+
end
|
160
|
+
|
161
|
+
it "has the input technical metadata in a file" do
|
162
|
+
expect(File.read("#{work_dir}/#{create_single_quote_job.id}/input_metadata")).not_to be_empty
|
163
|
+
end
|
164
|
+
|
165
|
+
it "has the pid in a file" do
|
166
|
+
expect(File.read("#{work_dir}/#{create_single_quote_job.id}/pid")).not_to be_empty
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'when uri encoded' do
|
170
|
+
let(:file_with_single_quote) { Addressable::URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', "'file_with_single_quote'.mp4").to_s) }
|
171
|
+
|
172
|
+
it "does not have errors" do
|
173
|
+
sleep 2
|
174
|
+
expect(find_single_quote_job.errors).to be_empty
|
175
|
+
end
|
176
|
+
|
177
|
+
it "has the input technical metadata in a file" do
|
178
|
+
expect(File.read("#{work_dir}/#{create_single_quote_job.id}/input_metadata")).not_to be_empty
|
179
|
+
end
|
180
|
+
|
181
|
+
it "has the pid in a file" do
|
182
|
+
expect(File.read("#{work_dir}/#{create_single_quote_job.id}/pid")).not_to be_empty
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "input filename with double quotes" do
|
188
|
+
let(:file_with_double_quote) { "file://" + Rails.root.join('..', 'spec', 'fixtures', '"file_with_double_quote".mp4').to_s }
|
189
|
+
let!(:create_double_quote_job) { ActiveEncode::Base.create(file_with_double_quote, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
|
190
|
+
let(:find_double_quote_job) { ActiveEncode::Base.find create_double_quote_job.id }
|
191
|
+
|
192
|
+
it "does not have errors" do
|
193
|
+
sleep 2
|
194
|
+
expect(find_double_quote_job.errors).to be_empty
|
195
|
+
end
|
196
|
+
|
197
|
+
it "has the input technical metadata in a file" do
|
198
|
+
expect(File.read("#{work_dir}/#{create_double_quote_job.id}/input_metadata")).not_to be_empty
|
199
|
+
end
|
200
|
+
|
201
|
+
it "has the pid in a file" do
|
202
|
+
expect(File.read("#{work_dir}/#{create_double_quote_job.id}/pid")).not_to be_empty
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'when uri encoded' do
|
206
|
+
let(:file_with_double_quote) { Addressable::URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', '"file_with_double_quote".mp4').to_s) }
|
207
|
+
|
208
|
+
it "does not have errors" do
|
209
|
+
sleep 2
|
210
|
+
expect(find_double_quote_job.errors).to be_empty
|
211
|
+
end
|
212
|
+
|
213
|
+
it "has the input technical metadata in a file" do
|
214
|
+
expect(File.read("#{work_dir}/#{create_double_quote_job.id}/input_metadata")).not_to be_empty
|
215
|
+
end
|
216
|
+
|
217
|
+
it "has the pid in a file" do
|
218
|
+
expect(File.read("#{work_dir}/#{create_double_quote_job.id}/pid")).not_to be_empty
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
context "input filename with other special characters" do
|
224
|
+
let(:file_with_special_characters) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file.with :=+%sp3c!l-ch4cts().mp4').to_s }
|
225
|
+
let!(:create_special_characters_job) { ActiveEncode::Base.create(file_with_special_characters, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
|
226
|
+
let(:find_special_characters_job) { ActiveEncode::Base.find create_special_characters_job.id }
|
227
|
+
|
228
|
+
it "does not have errors" do
|
229
|
+
sleep 2
|
230
|
+
expect(find_special_characters_job.errors).to be_empty
|
231
|
+
end
|
232
|
+
|
233
|
+
it "has the input technical metadata in a file" do
|
234
|
+
expect(File.read("#{work_dir}/#{create_special_characters_job.id}/input_metadata")).not_to be_empty
|
235
|
+
end
|
236
|
+
|
237
|
+
it "has the pid in a file" do
|
238
|
+
expect(File.read("#{work_dir}/#{create_special_characters_job.id}/pid")).not_to be_empty
|
239
|
+
end
|
240
|
+
|
241
|
+
context 'when uri encoded' do
|
242
|
+
let(:file_with_special_characters) { Addressable::URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', 'file.with :=+%sp3c!l-ch4cts().mp4').to_s) }
|
243
|
+
|
244
|
+
it "does not have errors" do
|
245
|
+
sleep 2
|
246
|
+
expect(find_special_characters_job.errors).to be_empty
|
247
|
+
end
|
248
|
+
|
249
|
+
it "has the input technical metadata in a file" do
|
250
|
+
expect(File.read("#{work_dir}/#{create_special_characters_job.id}/input_metadata")).not_to be_empty
|
251
|
+
end
|
252
|
+
|
253
|
+
it "has the pid in a file" do
|
254
|
+
expect(File.read("#{work_dir}/#{create_special_characters_job.id}/pid")).not_to be_empty
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
150
259
|
context 'when failed' do
|
151
260
|
subject { created_job }
|
152
261
|
|
@@ -212,6 +321,21 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
|
|
212
321
|
expect(File).to exist("#{work_dir}/#{subject.id}/exit_status.code")
|
213
322
|
expect(File.read("#{work_dir}/#{subject.id}/exit_status.code").to_i).to eq(-22)
|
214
323
|
end
|
324
|
+
|
325
|
+
context 'with less than 100 percent completeness' do
|
326
|
+
subject { incomplete_job }
|
327
|
+
|
328
|
+
it { is_expected.to be_failed }
|
329
|
+
it 'has an error' do
|
330
|
+
expect(incomplete_job.errors).to include "Encoding has completed but the output duration is shorter than the input"
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'succeeds with a configured completeness threshold' do
|
334
|
+
allow(ActiveEncode::EngineAdapters::FfmpegAdapter).to receive(:completeness_threshold).and_return(95)
|
335
|
+
expect(incomplete_job).not_to be_failed
|
336
|
+
expect(incomplete_job.errors).to be_empty
|
337
|
+
end
|
338
|
+
end
|
215
339
|
end
|
216
340
|
end
|
217
341
|
|
@@ -40,12 +40,15 @@ describe ActiveEncode::EngineAdapters::MediaConvertAdapter do
|
|
40
40
|
let(:cloudwatch_events) { Aws::CloudWatchEvents::Client.new(stub_responses: true) }
|
41
41
|
let(:cloudwatch_logs) { Aws::CloudWatchLogs::Client.new(stub_responses: true) }
|
42
42
|
|
43
|
+
let(:s3client) { Aws::S3::Client.new(stub_responses: true) }
|
44
|
+
|
43
45
|
before do
|
44
46
|
mediaconvert.stub_responses(:describe_endpoints, reconstitute_response("media_convert/endpoints.json"))
|
45
47
|
|
46
48
|
allow(Aws::MediaConvert::Client).to receive(:new).and_return(mediaconvert)
|
47
49
|
allow(Aws::CloudWatchEvents::Client).to receive(:new).and_return(cloudwatch_events)
|
48
50
|
allow(Aws::CloudWatchLogs::Client).to receive(:new).and_return(cloudwatch_logs)
|
51
|
+
allow(Aws::S3::Client).to receive(:new).and_return(s3client)
|
49
52
|
end
|
50
53
|
|
51
54
|
let(:created_job) do
|
@@ -267,4 +270,52 @@ describe ActiveEncode::EngineAdapters::MediaConvertAdapter do
|
|
267
270
|
completed_job
|
268
271
|
end
|
269
272
|
end
|
273
|
+
|
274
|
+
describe "#s3_uri" do
|
275
|
+
context "when filename has no special characters" do
|
276
|
+
context "non-s3 file" do
|
277
|
+
let(:input_url) { "spec/fixtures/fireworks.mp4" }
|
278
|
+
let(:source_bucket) { "bucket1" }
|
279
|
+
|
280
|
+
it "calls the #upload_to_s3 method" do
|
281
|
+
allow(SecureRandom).to receive(:uuid).and_return("randomstring")
|
282
|
+
expect(described_class.new.send(:s3_uri, input_url, { masterfile_bucket: source_bucket })).to eq "randomstring/fireworks.mp4"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
context "s3 file" do
|
286
|
+
let(:input_url) { "s3://bucket1/file.mp4" }
|
287
|
+
let(:source_bucket) { "bucket1" }
|
288
|
+
|
289
|
+
it "calls the #check_s3_bucket method" do
|
290
|
+
expect(described_class.new.send(:s3_uri, input_url, { masterfile_bucket: source_bucket })).to eq "file.mp4"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
context "when filename has special characters" do
|
295
|
+
context "non-s3 file" do
|
296
|
+
let(:input) { ["'file_with_single_quote'.mp4", '"file_with_double_quote".mp4', "file with space.mp4", "file.with...periods.mp4", "file.with :=+%sp3c!l-ch4cts().mp4"] }
|
297
|
+
let(:clean) { ["_file_with_single_quote_.mp4", "_file_with_double_quote_.mp4", "file_with_space.mp4", "filewithperiods.mp4", "filewith_____sp3c_l-ch4cts__.mp4"] }
|
298
|
+
let(:source_bucket) { "bucket1" }
|
299
|
+
|
300
|
+
it "calls the #upload_to_s3 method" do
|
301
|
+
allow(SecureRandom).to receive(:uuid).and_return("randomstring")
|
302
|
+
input.each_with_index do |url, index|
|
303
|
+
expect(described_class.new.send(:s3_uri, "spec/fixtures/#{url}", { masterfile_bucket: source_bucket })).to eq "randomstring/#{clean[index]}"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
context "s3 file" do
|
308
|
+
let(:input_urls) { ["s3://bucket1/'file_with_single_quote'.mp4", 's3://bucket1/"file_with_double_quote".mp4', "s3://bucket1/file with space.mp4", "s3://bucket1/file.with...periods.mp4", "s3://bucket1/file.with :=+%sp3c!l-ch4cts().mp4"] }
|
309
|
+
let(:clean) { ["_file_with_single_quote_.mp4", "_file_with_double_quote_.mp4", "file_with_space.mp4", "filewithperiods.mp4", "filewith_____sp3c_l-ch4cts__.mp4"] }
|
310
|
+
let(:source_bucket) { "bucket2" }
|
311
|
+
|
312
|
+
it "calls the #check_s3_bucket method" do
|
313
|
+
allow(SecureRandom).to receive(:uuid).and_return("randomstring")
|
314
|
+
input_urls.each_with_index do |url, index|
|
315
|
+
expect(described_class.new.send(:s3_uri, url, { masterfile_bucket: source_bucket })).to eq "randomstring/#{clean[index]}"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
270
321
|
end
|
@@ -137,6 +137,90 @@ describe ActiveEncode::EngineAdapters::PassThroughAdapter do
|
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
140
|
+
context "input filename with single quotes" do
|
141
|
+
let(:file_with_single_quote) { "file://" + Rails.root.join('..', 'spec', 'fixtures', "'file_with_single_quote'.mp4").to_s }
|
142
|
+
let(:file_with_single_quote_derivative) { "file://" + Rails.root.join('..', 'spec', 'fixtures', "'file_with_single_quote'.low.mp4").to_s }
|
143
|
+
let!(:create_single_quote_job) { ActiveEncode::Base.create(file_with_single_quote, outputs: [{ label: "low", url: file_with_single_quote_derivative }]) }
|
144
|
+
let(:find_single_quote_job) { ActiveEncode::Base.find create_single_quote_job.id }
|
145
|
+
|
146
|
+
it "does not have errors" do
|
147
|
+
expect(find_single_quote_job.errors).to be_empty
|
148
|
+
end
|
149
|
+
|
150
|
+
it "has the input technical metadata in a file" do
|
151
|
+
expect(File.read("#{work_dir}/#{create_single_quote_job.id}/input_metadata")).not_to be_empty
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'when uri encoded' do
|
155
|
+
let(:file_with_single_quote) { Addressable::URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', "'file_with_single_quote'.mp4").to_s) }
|
156
|
+
let(:file_with_single_quote_derivative) { "file://" + Rails.root.join('..', 'spec', 'fixtures', "'file_with_single_quote'.low.mp4").to_s }
|
157
|
+
|
158
|
+
it "does not have errors" do
|
159
|
+
expect(find_single_quote_job.errors).to be_empty
|
160
|
+
end
|
161
|
+
|
162
|
+
it "has the input technical metadata in a file" do
|
163
|
+
expect(File.read("#{work_dir}/#{create_single_quote_job.id}/input_metadata")).not_to be_empty
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context "input filename with double quotes" do
|
169
|
+
let(:file_with_double_quote) { "file://" + Rails.root.join('..', 'spec', 'fixtures', '"file_with_double_quote".mp4').to_s }
|
170
|
+
let(:file_with_double_quote_derivative) { "file://" + Rails.root.join('..', 'spec', 'fixtures', '"file_with_double_quote".mp4').to_s }
|
171
|
+
let!(:create_double_quote_job) { ActiveEncode::Base.create(file_with_double_quote, outputs: [{ label: "low", url: file_with_double_quote_derivative }]) }
|
172
|
+
let(:find_double_quote_job) { ActiveEncode::Base.find create_double_quote_job.id }
|
173
|
+
|
174
|
+
it "does not have errors" do
|
175
|
+
expect(find_double_quote_job.errors).to be_empty
|
176
|
+
end
|
177
|
+
|
178
|
+
it "has the input technical metadata in a file" do
|
179
|
+
expect(File.read("#{work_dir}/#{create_double_quote_job.id}/input_metadata")).not_to be_empty
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'when uri encoded' do
|
183
|
+
let(:file_with_double_quote) { Addressable::URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', '"file_with_double_quote".mp4').to_s) }
|
184
|
+
let(:file_with_double_quote_derivative) { "file://" + Rails.root.join('..', 'spec', 'fixtures', '"file_with_double_quote".mp4').to_s }
|
185
|
+
|
186
|
+
it "does not have errors" do
|
187
|
+
expect(find_double_quote_job.errors).to be_empty
|
188
|
+
end
|
189
|
+
|
190
|
+
it "has the input technical metadata in a file" do
|
191
|
+
expect(File.read("#{work_dir}/#{create_double_quote_job.id}/input_metadata")).not_to be_empty
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context "input filename with other special characters" do
|
197
|
+
let(:file_with_special_characters) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file.with :=+%sp3c!l-ch4cts().mp4').to_s }
|
198
|
+
let(:file_with_special_characters_derivative) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file.with :=+%sp3c!l-ch4cts().mp4').to_s }
|
199
|
+
let!(:create_special_characters_job) { ActiveEncode::Base.create(file_with_special_characters, outputs: [{ label: "low", url: file_with_special_characters_derivative }]) }
|
200
|
+
let(:find_special_characters_job) { ActiveEncode::Base.find create_special_characters_job.id }
|
201
|
+
|
202
|
+
it "does not have errors" do
|
203
|
+
expect(find_special_characters_job.errors).to be_empty
|
204
|
+
end
|
205
|
+
|
206
|
+
it "has the input technical metadata in a file" do
|
207
|
+
expect(File.read("#{work_dir}/#{create_special_characters_job.id}/input_metadata")).not_to be_empty
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'when uri encoded' do
|
211
|
+
let(:file_with_special_characters) { Addressable::URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', 'file.with :=+%sp3c!l-ch4cts().mp4').to_s) }
|
212
|
+
let(:file_with_special_characters_derivative) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file.with :=+%sp3c!l-ch4cts().mp4').to_s }
|
213
|
+
|
214
|
+
it "does not have errors" do
|
215
|
+
expect(find_special_characters_job.errors).to be_empty
|
216
|
+
end
|
217
|
+
|
218
|
+
it "has the input technical metadata in a file" do
|
219
|
+
expect(File.read("#{work_dir}/#{create_special_characters_job.id}/input_metadata")).not_to be_empty
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
140
224
|
context 'when failed' do
|
141
225
|
subject { created_job }
|
142
226
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_encode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Klein, Chris Colvard, Phuong Dinh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -285,6 +285,7 @@ files:
|
|
285
285
|
- lib/active_encode/engine_adapters/test_adapter.rb
|
286
286
|
- lib/active_encode/engine_adapters/zencoder_adapter.rb
|
287
287
|
- lib/active_encode/errors.rb
|
288
|
+
- lib/active_encode/filename_sanitizer.rb
|
288
289
|
- lib/active_encode/global_id.rb
|
289
290
|
- lib/active_encode/input.rb
|
290
291
|
- lib/active_encode/output.rb
|
@@ -297,6 +298,10 @@ files:
|
|
297
298
|
- lib/active_encode/version.rb
|
298
299
|
- lib/file_locator.rb
|
299
300
|
- spec/controllers/encode_record_controller_spec.rb
|
301
|
+
- spec/fixtures/"file_with_double_quote".low.mp4
|
302
|
+
- spec/fixtures/"file_with_double_quote".mp4
|
303
|
+
- spec/fixtures/'file_with_single_quote'.low.mp4
|
304
|
+
- spec/fixtures/'file_with_single_quote'.mp4
|
300
305
|
- spec/fixtures/Bars_512kb.mp4
|
301
306
|
- spec/fixtures/elastic_transcoder/input_completed.json
|
302
307
|
- spec/fixtures/elastic_transcoder/input_generic.json
|
@@ -340,12 +345,23 @@ files:
|
|
340
345
|
- spec/fixtures/ffmpeg/failed-id/input_metadata
|
341
346
|
- spec/fixtures/ffmpeg/failed-id/pid
|
342
347
|
- spec/fixtures/ffmpeg/failed-id/progress
|
348
|
+
- spec/fixtures/ffmpeg/incomplete-id/error.log
|
349
|
+
- spec/fixtures/ffmpeg/incomplete-id/exit_status.code
|
350
|
+
- spec/fixtures/ffmpeg/incomplete-id/input_metadata
|
351
|
+
- spec/fixtures/ffmpeg/incomplete-id/output_metadata-high
|
352
|
+
- spec/fixtures/ffmpeg/incomplete-id/output_metadata-low
|
353
|
+
- spec/fixtures/ffmpeg/incomplete-id/pid
|
354
|
+
- spec/fixtures/ffmpeg/incomplete-id/progress
|
355
|
+
- spec/fixtures/ffmpeg/incomplete-id/video-high.mp4
|
356
|
+
- spec/fixtures/ffmpeg/incomplete-id/video-low.mp4
|
343
357
|
- spec/fixtures/ffmpeg/running-id/error.log
|
344
358
|
- spec/fixtures/ffmpeg/running-id/input_metadata
|
345
359
|
- spec/fixtures/ffmpeg/running-id/pid
|
346
360
|
- spec/fixtures/ffmpeg/running-id/progress
|
347
361
|
- spec/fixtures/file with space.low.mp4
|
348
362
|
- spec/fixtures/file with space.mp4
|
363
|
+
- spec/fixtures/file.with :=+%sp3c!l-ch4cts().mp4
|
364
|
+
- spec/fixtures/file.with...periods.mp4
|
349
365
|
- spec/fixtures/fireworks.low.mp4
|
350
366
|
- spec/fixtures/fireworks.mp4
|
351
367
|
- spec/fixtures/matterhorn/cancelled_response.xml
|
@@ -435,6 +451,10 @@ specification_version: 4
|
|
435
451
|
summary: Declare encode job classes that can be run by a variety of encoding services
|
436
452
|
test_files:
|
437
453
|
- spec/controllers/encode_record_controller_spec.rb
|
454
|
+
- spec/fixtures/"file_with_double_quote".low.mp4
|
455
|
+
- spec/fixtures/"file_with_double_quote".mp4
|
456
|
+
- spec/fixtures/'file_with_single_quote'.low.mp4
|
457
|
+
- spec/fixtures/'file_with_single_quote'.mp4
|
438
458
|
- spec/fixtures/Bars_512kb.mp4
|
439
459
|
- spec/fixtures/elastic_transcoder/input_completed.json
|
440
460
|
- spec/fixtures/elastic_transcoder/input_generic.json
|
@@ -478,12 +498,23 @@ test_files:
|
|
478
498
|
- spec/fixtures/ffmpeg/failed-id/input_metadata
|
479
499
|
- spec/fixtures/ffmpeg/failed-id/pid
|
480
500
|
- spec/fixtures/ffmpeg/failed-id/progress
|
501
|
+
- spec/fixtures/ffmpeg/incomplete-id/error.log
|
502
|
+
- spec/fixtures/ffmpeg/incomplete-id/exit_status.code
|
503
|
+
- spec/fixtures/ffmpeg/incomplete-id/input_metadata
|
504
|
+
- spec/fixtures/ffmpeg/incomplete-id/output_metadata-high
|
505
|
+
- spec/fixtures/ffmpeg/incomplete-id/output_metadata-low
|
506
|
+
- spec/fixtures/ffmpeg/incomplete-id/pid
|
507
|
+
- spec/fixtures/ffmpeg/incomplete-id/progress
|
508
|
+
- spec/fixtures/ffmpeg/incomplete-id/video-high.mp4
|
509
|
+
- spec/fixtures/ffmpeg/incomplete-id/video-low.mp4
|
481
510
|
- spec/fixtures/ffmpeg/running-id/error.log
|
482
511
|
- spec/fixtures/ffmpeg/running-id/input_metadata
|
483
512
|
- spec/fixtures/ffmpeg/running-id/pid
|
484
513
|
- spec/fixtures/ffmpeg/running-id/progress
|
485
514
|
- spec/fixtures/file with space.low.mp4
|
486
515
|
- spec/fixtures/file with space.mp4
|
516
|
+
- spec/fixtures/file.with :=+%sp3c!l-ch4cts().mp4
|
517
|
+
- spec/fixtures/file.with...periods.mp4
|
487
518
|
- spec/fixtures/fireworks.low.mp4
|
488
519
|
- spec/fixtures/fireworks.mp4
|
489
520
|
- spec/fixtures/matterhorn/cancelled_response.xml
|