active_encode 1.1.3 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bec033b26b07c4ce739d3eed03b59ded7708c8482bcb6e217e1f39a3ffbe124e
4
- data.tar.gz: 15256720816a5d6bd5bc7e2e7dc2fb4ff68da5be8e76510983195cece3649406
3
+ metadata.gz: e8c41200059443abc28d64ef9748086b36a4a1c97537138da24bd1b5016e902d
4
+ data.tar.gz: 3fec8ac243d8e7ff3c96abaa26b08c23e5c409337c2f63463f465096e29e7cba
5
5
  SHA512:
6
- metadata.gz: b50c4b4a821ac341b0d206379298ddc973178dea565c8f268770850559cc400cf1e6afe280ee2785b0e30a37e571aaff97f4c5a11fb90e1eff00d8bbef1875f7
7
- data.tar.gz: 62e66bf1a4f2792f11eb545c3fc09d510d42d0a5f1a392c4f73994e0bd0fc6170bcec18a6ce7c7c255b63e94822af2a3266c9a14608855250c9e735d5a236b9d
6
+ metadata.gz: 5cae3d56d8c068e3cb73e01b4d15d7ba99d305f6c7bd09e7de51def230d247fa3b423d8b32674cadbf66ad7845ce235399b6b53fa068ef305b65bfad1c829f85
7
+ data.tar.gz: 7265200be814de1969887dd1275a8e89da3e0befe6a92542800c42119f93307f0bf7584a70dcfe8127ef11c7a7a76f5f14fab7b9a5f47ef9408cb474f64d2039
data/.rubocop_todo.yml CHANGED
@@ -37,6 +37,7 @@ Layout/LineLength:
37
37
  Metrics/MethodLength:
38
38
  Exclude:
39
39
  - 'lib/active_encode/engine_adapters/*'
40
+ - 'lib/active_encode/engine_adapters/ffmpeg_adapter/*'
40
41
  - 'lib/file_locator.rb'
41
42
 
42
43
  Metrics/PerceivedComplexity:
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+ module ActiveEncode
3
+ module EngineAdapters
4
+ class FfmpegAdapter
5
+ module Cleaner
6
+ # This method is to clean up files leftover from the ffmpeg encode process.
7
+ # File names for the pass_through adapter are the same, so this will clean up
8
+ # pass_through encodes as well.
9
+ def remove_old_files!(options = {})
10
+ default_options = {
11
+ older_than: 2.weeks,
12
+ no_outputs: ['input_metadata', 'duration_input_metadata', 'error.log', 'exit_status.code', 'progress', 'completed', 'pid', 'output_metadata-*'],
13
+ outputs: false,
14
+ all: false
15
+ }
16
+ options.reverse_merge!(default_options)
17
+
18
+ if options[:all]
19
+ files = build_file_list(WORK_DIR, "*")
20
+ directories = remove_files(files, options[:older_than])
21
+ remove_empty_directories(directories)
22
+ elsif options[:outputs]
23
+ output_directories = build_file_list(WORK_DIR, "outputs")
24
+ remove_child_files(output_directories, options[:older_than])
25
+ remove_empty_directories(output_directories)
26
+ else
27
+ files = []
28
+ options[:no_outputs].each { |fn| files += build_file_list(WORK_DIR, fn) }
29
+ remove_files(files, options[:older_than])
30
+ end
31
+ end
32
+
33
+ def build_file_list(directory, filename)
34
+ file_path = File.join(directory, "**", filename)
35
+ # Some of the files generated by the ffmpeg encode seem to be hidden files.
36
+ # This uses File::FNM_DOTMATCH to include them in the results.
37
+ Dir.glob(file_path, File::FNM_DOTMATCH)
38
+ end
39
+
40
+ def file_check(path, older_than)
41
+ File.mtime(path) < DateTime.now - older_than && File.file?(path)
42
+ end
43
+
44
+ def remove_files(files, older_than)
45
+ files_to_delete = files.select { |f| file_check(f, older_than) }
46
+ FileUtils.rm(files_to_delete) unless files_to_delete.empty?
47
+
48
+ # Return a list of any directories that were included in the files list for further processing.
49
+ # The files list can include directories such as "/tmp/.." which should not be included in directory list.
50
+ files.select { |f| File.directory?(f) unless f.end_with?(".") }
51
+ end
52
+
53
+ def remove_empty_directories(directories)
54
+ directories_to_delete = directories.select { |d| Dir.empty?(d) }
55
+ non_empty_directories = directories - directories_to_delete
56
+ directories_to_delete += non_empty_directories.select { |ned| Dir.children(ned) == ["outputs"] && directories_to_delete.include?(File.join(ned, "outputs")) }
57
+ FileUtils.rmdir(directories_to_delete) unless directories_to_delete.empty?
58
+ end
59
+
60
+ def remove_child_files(directories, older_than)
61
+ files_to_delete = []
62
+ directories.each do |d|
63
+ files = Dir.children(d).select { |ch| file_check(File.join(d, ch), older_than) }
64
+ files_to_delete += files.collect { |f| File.join(d, f) }
65
+ end
66
+ FileUtils.rm(files_to_delete) unless files_to_delete.empty?
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
+ require 'English'
2
3
  require 'fileutils'
3
4
  require 'nokogiri'
4
5
  require 'shellwords'
5
6
  require 'addressable/uri'
7
+ require 'active_encode/engine_adapters/ffmpeg_adapter/cleaner'
6
8
 
7
9
  module ActiveEncode
8
10
  module EngineAdapters
9
11
  class FfmpegAdapter
12
+ extend ActiveEncode::EngineAdapters::FfmpegAdapter::Cleaner
13
+
10
14
  WORK_DIR = ENV["ENCODE_WORK_DIR"] || "encodes" # Should read from config
11
15
  MEDIAINFO_PATH = ENV["MEDIAINFO_PATH"] || "mediainfo"
12
16
  FFMPEG_PATH = ENV["FFMPEG_PATH"] || "ffmpeg"
@@ -51,18 +55,38 @@ module ActiveEncode
51
55
 
52
56
  new_encode.input = build_input new_encode
53
57
 
54
- if new_encode.input.duration.blank?
55
- new_encode.state = :failed
56
- new_encode.percent_complete = 1
58
+ # Log error if file is empty or inaccessible
59
+ if new_encode.input.duration.blank? && new_encode.input.file_size.blank?
60
+ file_error(new_encode, input_url)
61
+ return new_encode
62
+ # If file is not empty, try copying file to generate missing metadata
63
+ elsif new_encode.input.duration.blank? && new_encode.input.file_size.present?
64
+
65
+ # This regex removes the query string from URIs. This is necessary to
66
+ # properly process files originating from S3 or similar providers.
67
+ filepath = clean_url.to_s.gsub(/\?.*/, '')
68
+ copy_url = filepath.gsub(filepath, "#{File.basename(filepath, File.extname(filepath))}_temp#{File.extname(filepath)}")
69
+ copy_path = working_path(copy_url, new_encode.id)
70
+
71
+ # -map 0 sets ffmpeg to copy all available streams.
72
+ # -c copy sets ffmpeg to copy all codecs
73
+ # -y automatically overwrites the temp file if one already exists
74
+ `#{FFMPEG_PATH} -loglevel level+fatal -i \"#{input_url}\" -map 0 -c copy -y \"#{copy_path}\"`
75
+
76
+ # If ffmpeg copy fails, log error because file is either not a media file
77
+ # or the file extension does not match the codecs used to encode the file
78
+ unless $CHILD_STATUS.success?
79
+ file_error(new_encode, input_url)
80
+ return new_encode
81
+ end
57
82
 
58
- new_encode.errors = if new_encode.input.file_size.blank?
59
- ["#{input_url} does not exist or is not accessible"]
60
- else
61
- ["Error inspecting input: #{input_url}"]
62
- end
83
+ # Write the mediainfo output to a separate file to preserve metadata from original file
84
+ `#{MEDIAINFO_PATH} #{curl_option} --Output=XML --LogFile=#{working_path("duration_input_metadata", new_encode.id)} "#{copy_path}"`
63
85
 
64
- write_errors new_encode
65
- return new_encode
86
+ File.delete(copy_path)
87
+
88
+ # Assign duration to the encode created for the original file.
89
+ new_encode.input.duration = fixed_duration(working_path("duration_input_metadata", new_encode.id))
66
90
  end
67
91
 
68
92
  new_encode.state = :running
@@ -99,6 +123,7 @@ module ActiveEncode
99
123
  encode.output = []
100
124
  encode.created_at, encode.updated_at = get_times encode.id
101
125
  encode.input = build_input encode
126
+ encode.input.duration ||= fixed_duration(working_path("duration_input_metadata", encode.id)) if File.exist?(working_path("duration_input_metadata", encode.id))
102
127
  encode.percent_complete = calculate_percent_complete encode
103
128
 
104
129
  pid = get_pid(id)
@@ -302,6 +327,24 @@ module ActiveEncode
302
327
  def get_xpath_text(doc, xpath, cast_method)
303
328
  doc.xpath(xpath).first&.text&.send(cast_method)
304
329
  end
330
+
331
+ def fixed_duration(path)
332
+ get_tech_metadata(path)[:duration]
333
+ end
334
+
335
+ def file_error(new_encode, input_url)
336
+ new_encode.state = :failed
337
+ new_encode.percent_complete = 1
338
+
339
+ new_encode.errors = if new_encode.input.file_size.blank?
340
+ ["#{input_url} does not exist or is not accessible"]
341
+ else
342
+ ["Error inspecting input: #{input_url}"]
343
+ end
344
+
345
+ write_errors new_encode
346
+ new_encode
347
+ end
305
348
  end
306
349
  end
307
350
  end
@@ -15,6 +15,7 @@ module ActiveEncode
15
15
  class PassThroughAdapter
16
16
  WORK_DIR = ENV["ENCODE_WORK_DIR"] || "encodes" # Should read from config
17
17
  MEDIAINFO_PATH = ENV["MEDIAINFO_PATH"] || "mediainfo"
18
+ FFMPEG_PATH = ENV["FFMPEG_PATH"] || "ffmpeg"
18
19
 
19
20
  def create(input_url, options = {})
20
21
  # Decode file uris for ffmpeg (mediainfo works either way)
@@ -36,18 +37,38 @@ module ActiveEncode
36
37
  new_encode.input.id = new_encode.id
37
38
  new_encode.created_at, new_encode.updated_at = get_times new_encode.id
38
39
 
39
- if new_encode.input.duration.blank?
40
- new_encode.state = :failed
41
- new_encode.percent_complete = 1
42
-
43
- new_encode.errors = if new_encode.input.file_size.blank?
44
- ["#{input_url} does not exist or is not accessible"]
45
- else
46
- ["Error inspecting input: #{input_url}"]
47
- end
48
-
49
- write_errors new_encode
40
+ # Log error if file is empty or inaccessible
41
+ if new_encode.input.duration.blank? && new_encode.input.file_size.blank?
42
+ file_error(new_encode, input_url)
50
43
  return new_encode
44
+ # If file is not empty, try copying file to generate missing metadata
45
+ elsif new_encode.input.duration.blank? && new_encode.input.file_size.present?
46
+
47
+ # This regex removes the query string from URIs. This is necessary to
48
+ # properly process files originating from S3 or similar providers.
49
+ filepath = input_url.to_s.gsub(/\?.*/, '')
50
+ copy_url = filepath.gsub(filepath, "#{File.basename(filepath, File.extname(filepath))}_temp#{File.extname(input_url)}")
51
+ copy_path = working_path(copy_url, new_encode.id)
52
+
53
+ # -map 0 sets ffmpeg to copy all available streams.
54
+ # -c copy sets ffmpeg to copy all codecs
55
+ # -y automatically overwrites the temp file if one already exists
56
+ `#{FFMPEG_PATH} -loglevel level+fatal -i \"#{input_url}\" -map 0 -c copy -y \"#{copy_path}\"`
57
+
58
+ # If ffmpeg copy fails, log error because file is either not a media file
59
+ # or the file extension does not match the codecs used to encode the file
60
+ unless $CHILD_STATUS.success?
61
+ file_error(new_encode, input_url)
62
+ return new_encode
63
+ end
64
+
65
+ # Write the mediainfo output to a separate file to preserve metadata from original file
66
+ `#{MEDIAINFO_PATH} --Output=XML --LogFile=#{working_path("duration_input_metadata", new_encode.id)} \"#{copy_path}\"`
67
+
68
+ File.delete(copy_path)
69
+
70
+ # Assign duration to the encode created for the original file.
71
+ new_encode.input.duration = fixed_duration(working_path("duration_input_metadata", new_encode.id))
51
72
  end
52
73
 
53
74
  # For saving filename to label map used to find the label when building outputs
@@ -86,6 +107,7 @@ module ActiveEncode
86
107
  encode.created_at, encode.updated_at = get_times encode.id
87
108
  encode.input = build_input encode
88
109
  encode.input.id = encode.id
110
+ encode.input.duration ||= fixed_duration(working_path("duration_input_metadata", encode.id)) if File.exist?(working_path("duration_input_metadata", encode.id))
89
111
  encode.output = []
90
112
  encode.current_operations = []
91
113
 
@@ -231,6 +253,24 @@ module ActiveEncode
231
253
  def get_xpath_text(doc, xpath, cast_method)
232
254
  doc.xpath(xpath).first&.text&.send(cast_method)
233
255
  end
256
+
257
+ def fixed_duration(path)
258
+ get_tech_metadata(path)[:duration]
259
+ end
260
+
261
+ def file_error(new_encode, input_url)
262
+ new_encode.state = :failed
263
+ new_encode.percent_complete = 1
264
+
265
+ new_encode.errors = if new_encode.input.file_size.blank?
266
+ ["#{input_url} does not exist or is not accessible"]
267
+ else
268
+ ["Error inspecting input: #{input_url}"]
269
+ end
270
+
271
+ write_errors new_encode
272
+ new_encode
273
+ end
234
274
  end
235
275
  end
236
276
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ActiveEncode
3
- VERSION = '1.1.3'
3
+ VERSION = '1.2.0'
4
4
  end
@@ -112,6 +112,62 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
112
112
  end
113
113
  end
114
114
 
115
+ context "input file format does not match extension" do
116
+ let(:improper_format_file) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file_without_metadata.mp4').to_s }
117
+ let(:improper_format_job) { ActiveEncode::Base.create(improper_format_file, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
118
+
119
+ it "returns the encode with correct error" do
120
+ expect(improper_format_job.errors).to include("Error inspecting input: #{improper_format_file}")
121
+ expect(improper_format_job.percent_complete).to be 1
122
+ end
123
+ end
124
+
125
+ context "input file with missing metadata" do
126
+ let(:file_without_metadata) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file_without_metadata.webm').to_s }
127
+ let!(:create_without_metadata_job) { ActiveEncode::Base.create(file_without_metadata, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
128
+ let(:find_without_metadata_job) { ActiveEncode::Base.find create_without_metadata_job.id }
129
+
130
+ it "does not have errors" do
131
+ sleep 2
132
+ expect(find_without_metadata_job.errors).to be_empty
133
+ end
134
+
135
+ it "has the input technical metadata in a file" do
136
+ expect(File.read("#{work_dir}/#{create_without_metadata_job.id}/input_metadata")).not_to be_empty
137
+ end
138
+
139
+ it "has the pid in a file" do
140
+ expect(File.read("#{work_dir}/#{create_without_metadata_job.id}/pid")).not_to be_empty
141
+ end
142
+
143
+ it "assigns the correct duration to the encode" do
144
+ expect(create_without_metadata_job.input.duration).to eq 68_653
145
+ expect(find_without_metadata_job.input.duration).to eq 68_653
146
+ end
147
+
148
+ context 'when uri encoded' do
149
+ let(:file_without_metadata) { Addressable::URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', 'file_without_metadata.webm').to_s) }
150
+
151
+ it "does not have errors" do
152
+ sleep 2
153
+ expect(find_without_metadata_job.errors).to be_empty
154
+ end
155
+
156
+ it "has the input technical metadata in a file" do
157
+ expect(File.read("#{work_dir}/#{create_without_metadata_job.id}/input_metadata")).not_to be_empty
158
+ end
159
+
160
+ it "has the pid in a file" do
161
+ expect(File.read("#{work_dir}/#{create_without_metadata_job.id}/pid")).not_to be_empty
162
+ end
163
+
164
+ it "assigns the correct duration to the encode" do
165
+ expect(create_without_metadata_job.input.duration).to eq 68_653
166
+ expect(find_without_metadata_job.input.duration).to eq 68_653
167
+ end
168
+ end
169
+ end
170
+
115
171
  context "input filename with spaces" do
116
172
  let(:file_with_space) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file with space.mp4').to_s }
117
173
  let!(:create_space_job) { ActiveEncode::Base.create(file_with_space, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: 'mp4' }]) }
@@ -372,4 +428,108 @@ describe ActiveEncode::EngineAdapters::FfmpegAdapter do
372
428
  expect { running_job.cancel! }.to raise_error(ActiveEncode::CancelError)
373
429
  end
374
430
  end
431
+
432
+ describe "#remove_old_files!" do
433
+ subject { created_job }
434
+ # 'exit_status.code' and 'progress' seem to be hidden files so rspec does not see them.
435
+ # That is why they are not explicitly included in the tests even though they are in the filenames list.
436
+ # If they were not being deleted they would cause other tests to fail.
437
+ let(:base_path) { "#{work_dir}/#{subject.id}" }
438
+ let(:input_metadata_file) { "#{base_path}/input_metadata" }
439
+ let(:error_log_file) { "#{base_path}/error.log" }
440
+ let(:pid_file) { "#{base_path}/pid" }
441
+ let(:exit_status_file) { "#{base_path}/exit_status.code" }
442
+ let(:progress_file) { "#{base_path}/progress" }
443
+ let(:pathnames) { [input_metadata_file, error_log_file, pid_file, exit_status_file, progress_file] }
444
+
445
+ # There was some flaky behavior with the file creation for created_job that
446
+ # would cause tests to fail. This ensures the files are created.
447
+ before :each do
448
+ FileUtils.touch(pathnames)
449
+ end
450
+
451
+ context ":no_outputs" do
452
+ it "deletes files created from encode process older than 2 weeks" do
453
+ # Another measure to give files time to be created.
454
+ sleep 1
455
+ travel 3.weeks do
456
+ expect { described_class.remove_old_files! }
457
+ .to change { File.exist?(input_metadata_file) }.from(true).to(false)
458
+ .and change { File.exist?(error_log_file) }.from(true).to(false)
459
+ .and change { File.exist?(pid_file) }.from(true).to(false)
460
+ .and not_change { Dir.children("#{work_dir}/#{subject.id}/outputs").count }.from(2)
461
+ end
462
+ end
463
+
464
+ it "does not delete files younger than 2 weeks" do
465
+ sleep 1
466
+ expect { described_class.remove_old_files! }
467
+ .to not_change { File.exist?(input_metadata_file) }.from(true)
468
+ .and not_change { File.exist?(error_log_file) }.from(true)
469
+ .and not_change { File.exist?(pid_file) }.from(true)
470
+ .and not_change { Dir.children("#{work_dir}/#{subject.id}/outputs").count }.from(2)
471
+ end
472
+ end
473
+
474
+ context ":outputs" do
475
+ it "deletes outputs created from encode process older than 2 weeks" do
476
+ sleep 1
477
+ travel 3.weeks do
478
+ expect { described_class.remove_old_files!(outputs: true) }
479
+ .to not_change { File.exist?(input_metadata_file) }.from(true)
480
+ .and not_change { File.exist?(error_log_file) }.from(true)
481
+ .and not_change { File.exist?(pid_file) }.from(true)
482
+ .and change { Dir.exist?("#{work_dir}/#{subject.id}/outputs") }.from(true).to(false)
483
+ end
484
+ end
485
+
486
+ it "does not delete outputs younger than 2 weeks" do
487
+ sleep 1
488
+ expect { described_class.remove_old_files!(outputs: true) }
489
+ .to not_change { File.exist?(input_metadata_file) }.from(true)
490
+ .and not_change { File.exist?(error_log_file) }.from(true)
491
+ .and not_change { File.exist?(pid_file) }.from(true)
492
+ .and not_change { Dir.children("#{work_dir}/#{subject.id}/outputs").count }.from(2)
493
+ end
494
+
495
+ it "does not delete outputs directory containing files younger than 2 weeks" do
496
+ sleep 1
497
+ travel 3.weeks do
498
+ allow(File).to receive(:mtime).and_call_original
499
+ allow(File).to receive(:mtime).with("#{work_dir}/#{subject.id}/outputs/fireworks-low.mp4").and_return(DateTime.now)
500
+ expect { described_class.remove_old_files!(outputs: true) }
501
+ .to not_change { Dir.exist?("#{work_dir}/#{subject.id}/outputs") }.from(true)
502
+ expect(Dir.children("#{work_dir}/#{subject.id}/outputs")).to eq(["fireworks-low.mp4"])
503
+ end
504
+ end
505
+ end
506
+
507
+ context ":all" do
508
+ it "deletes all files and directories older than 2 weeks" do
509
+ sleep 1
510
+ travel 3.weeks do
511
+ expect { described_class.remove_old_files!(all: true) }
512
+ .to change { Dir.exist?("#{work_dir}/#{subject.id}") }.from(true).to(false)
513
+ end
514
+ end
515
+
516
+ it "does not delete files and directories younger than 2 weeks" do
517
+ sleep 1
518
+ expect { described_class.remove_old_files!(all: true) }
519
+ .to not_change { Dir.exist?("#{work_dir}/#{subject.id}") }.from(true)
520
+ .and not_change { Dir.children("#{work_dir}/#{subject.id}").count }
521
+ end
522
+
523
+ it "does not delete directories containing files younger than 2 weeks" do
524
+ sleep 1
525
+ travel 3.weeks do
526
+ allow(File).to receive(:mtime).and_call_original
527
+ allow(File).to receive(:mtime).with("#{work_dir}/#{subject.id}/input_metadata").and_return(DateTime.now)
528
+ expect { described_class.remove_old_files!(all: true) }
529
+ .to not_change { Dir.exist?("#{work_dir}/#{subject.id}") }.from(true)
530
+ expect(Dir.children("#{work_dir}/#{subject.id}")).to eq(["input_metadata"])
531
+ end
532
+ end
533
+ end
534
+ end
375
535
  end
@@ -109,6 +109,54 @@ describe ActiveEncode::EngineAdapters::PassThroughAdapter do
109
109
  end
110
110
  end
111
111
 
112
+ context "input file format does not match extension" do
113
+ let(:improper_format_file) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file_without_metadata.mp4').to_s }
114
+ let(:improper_format_job) { ActiveEncode::Base.create(improper_format_file, outputs: [{ label: "low", url: improper_format_file }]) }
115
+
116
+ it "returns the encode with correct error" do
117
+ expect(improper_format_job.errors).to include("Error inspecting input: #{improper_format_file}")
118
+ expect(improper_format_job.percent_complete).to be 1
119
+ end
120
+ end
121
+
122
+ context "input file without metadata" do
123
+ let(:file_without_metadata) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file_without_metadata.webm').to_s }
124
+ let(:file_without_metadata_derivative) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file_without_metadata.low.webm').to_s }
125
+ let(:create_without_metadata_job) { ActiveEncode::Base.create(file_without_metadata, outputs: [{ label: "low", url: file_without_metadata_derivative }]) }
126
+ let(:find_without_metadata_job) { ActiveEncode::Base.find create_without_metadata_job.id }
127
+
128
+ it "does not have errors" do
129
+ expect(find_without_metadata_job.errors).to be_empty
130
+ end
131
+
132
+ it "has the input technical metadata in a file" do
133
+ expect(File.read("#{work_dir}/#{create_without_metadata_job.id}/input_metadata")).not_to be_empty
134
+ end
135
+
136
+ it "assigns the correct duration to the encode" do
137
+ expect(create_without_metadata_job.input.duration).to eq 68_653
138
+ expect(find_without_metadata_job.input.duration).to eq 68_653
139
+ end
140
+
141
+ context 'when uri encoded' do
142
+ let(:file_without_metadata) { Addressable::URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', 'file_without_metadata.webm').to_s) }
143
+ let(:file_without_metadata_derivative) { Addressable::URI.encode("file://" + Rails.root.join('..', 'spec', 'fixtures', 'file_without_metadata.low.webm').to_s) }
144
+
145
+ it "does not have errors" do
146
+ expect(find_without_metadata_job.errors).to be_empty
147
+ end
148
+
149
+ it "has the input technical metadata in a file" do
150
+ expect(File.read("#{work_dir}/#{create_without_metadata_job.id}/input_metadata")).not_to be_empty
151
+ end
152
+
153
+ it "assigns the correct duration to the encode" do
154
+ expect(create_without_metadata_job.input.duration).to eq 68_653
155
+ expect(find_without_metadata_job.input.duration).to eq 68_653
156
+ end
157
+ end
158
+ end
159
+
112
160
  context "input filename with spaces" do
113
161
  let(:file_with_space) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file with space.mp4').to_s }
114
162
  let(:file_with_space_derivative) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'file with space.low.mp4').to_s }
data/spec/rails_helper.rb CHANGED
@@ -20,4 +20,6 @@ RSpec.configure do |config|
20
20
  config.after do |example|
21
21
  DatabaseCleaner.clean if example.metadata[:db_clean]
22
22
  end
23
+
24
+ config.include ActiveSupport::Testing::TimeHelpers
23
25
  end
data/spec/spec_helper.rb CHANGED
@@ -16,3 +16,5 @@ RSpec::Matchers.define :be_the_same_time_as do |expected|
16
16
  expect(Time.parse(expected).utc).to eq(Time.parse(actual).utc)
17
17
  end
18
18
  end
19
+
20
+ RSpec::Matchers.define_negated_matcher :not_change, :change
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.1.3
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Klein, Chris Colvard, Phuong Dinh
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-28 00:00:00.000000000 Z
11
+ date: 2023-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -278,6 +278,7 @@ files:
278
278
  - lib/active_encode/engine_adapters.rb
279
279
  - lib/active_encode/engine_adapters/elastic_transcoder_adapter.rb
280
280
  - lib/active_encode/engine_adapters/ffmpeg_adapter.rb
281
+ - lib/active_encode/engine_adapters/ffmpeg_adapter/cleaner.rb
281
282
  - lib/active_encode/engine_adapters/matterhorn_adapter.rb
282
283
  - lib/active_encode/engine_adapters/media_convert_adapter.rb
283
284
  - lib/active_encode/engine_adapters/media_convert_output.rb
@@ -364,6 +365,9 @@ files:
364
365
  - spec/fixtures/file with space.mp4
365
366
  - spec/fixtures/file.with :=+%sp3c!l-ch4cts().mp4
366
367
  - spec/fixtures/file.with...periods.mp4
368
+ - spec/fixtures/file_without_metadata.low.webm
369
+ - spec/fixtures/file_without_metadata.mp4
370
+ - spec/fixtures/file_without_metadata.webm
367
371
  - spec/fixtures/fireworks.low.mp4
368
372
  - spec/fixtures/fireworks.mp4
369
373
  - spec/fixtures/matterhorn/cancelled_response.xml
@@ -434,7 +438,7 @@ licenses:
434
438
  - Apache-2.0
435
439
  metadata:
436
440
  rubygems_mfa_required: 'true'
437
- post_install_message:
441
+ post_install_message:
438
442
  rdoc_options: []
439
443
  require_paths:
440
444
  - lib
@@ -449,8 +453,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
449
453
  - !ruby/object:Gem::Version
450
454
  version: '0'
451
455
  requirements: []
452
- rubygems_version: 3.2.32
453
- signing_key:
456
+ rubygems_version: 3.1.6
457
+ signing_key:
454
458
  specification_version: 4
455
459
  summary: Declare encode job classes that can be run by a variety of encoding services
456
460
  test_files:
@@ -521,6 +525,9 @@ test_files:
521
525
  - spec/fixtures/file with space.mp4
522
526
  - spec/fixtures/file.with :=+%sp3c!l-ch4cts().mp4
523
527
  - spec/fixtures/file.with...periods.mp4
528
+ - spec/fixtures/file_without_metadata.low.webm
529
+ - spec/fixtures/file_without_metadata.mp4
530
+ - spec/fixtures/file_without_metadata.webm
524
531
  - spec/fixtures/fireworks.low.mp4
525
532
  - spec/fixtures/fireworks.mp4
526
533
  - spec/fixtures/matterhorn/cancelled_response.xml
OSZAR »