active_encode 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|