mm_tool 0.1.8 → 0.1.9
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/bin/mm_tool +0 -1
- data/lib/mm_tool/application_main.rb +20 -0
- data/lib/mm_tool/mm_movie.rb +19 -4
- data/lib/mm_tool/mm_movie_stream.rb +41 -6
- data/lib/mm_tool/mm_tool_cli.rb +33 -8
- data/lib/mm_tool/user_defaults.rb +16 -0
- data/lib/mm_tool/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92055cbeccfc0cd1628a7d89d281c1c179bace5eae9a099c62082e66a4d82744
|
4
|
+
data.tar.gz: 48327dccb7a80c5f1636a88bfc3406851eae42ece5e0c82eb747e664aab54880
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 767a31ed4096a56bfa3a481a7922692d7d42421f4ceec1f87e81dc178968a49f6ffba46f8caadf5b01b8cd244a5a85500942adafb61d0fcc18137a12605c5449
|
7
|
+
data.tar.gz: c518d44843912f60101507ede5ca4d4840850043375edc0be6b710465b6f6e5f2b9879c67fa62bff5702684a1dd69fb3784d10b78a5cfdb2a8b13b38072b99c2
|
data/bin/mm_tool
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
module MmTool
|
2
2
|
|
3
|
+
@@encoder_list = nil
|
4
|
+
|
5
|
+
#------------------------------------------------------------
|
6
|
+
# Module-level attribute provides list of installed encoders.
|
7
|
+
#------------------------------------------------------------
|
8
|
+
def self.encoder_list
|
9
|
+
unless @@encoder_list
|
10
|
+
@@encoder_list = %w()
|
11
|
+
codecs_allowed = %w(libx264 libx265 h264_qsv hevc_qsv h264_videotoolbox hevc_videotoolbox)
|
12
|
+
task = TTY::Command.new(printer: :null)
|
13
|
+
|
14
|
+
codecs_allowed.each do |codec|
|
15
|
+
result = task.run!("ffprobe -v quiet -codecs | grep #{codec}")
|
16
|
+
@@encoder_list << codec unless result.failure?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
@@encoder_list
|
20
|
+
end
|
21
|
+
|
22
|
+
|
3
23
|
#=============================================================================
|
4
24
|
# The main application.
|
5
25
|
#=============================================================================
|
data/lib/mm_tool/mm_movie.rb
CHANGED
@@ -27,7 +27,7 @@ module MmTool
|
|
27
27
|
#------------------------------------------------------------
|
28
28
|
def initialize(with_file:)
|
29
29
|
@defaults = MmUserDefaults.shared_user_defaults
|
30
|
-
@streams = MmMovieStream::streams(with_files: all_paths(with_file: with_file))
|
30
|
+
@streams = MmMovieStream::streams(with_files: all_paths(with_file: with_file), owner_ref: self)
|
31
31
|
@format_metadata = FFMPEG::Movie.new(with_file).metadata[:format]
|
32
32
|
end
|
33
33
|
|
@@ -47,6 +47,21 @@ module MmTool
|
|
47
47
|
size ? ByteSize.new(size) : 'unknown'
|
48
48
|
end
|
49
49
|
|
50
|
+
#------------------------------------------------------------
|
51
|
+
# Get the file-level 'bitrate' metadata.
|
52
|
+
#------------------------------------------------------------
|
53
|
+
def format_bitrate
|
54
|
+
size = @format_metadata[:bit_rate]
|
55
|
+
ByteSize.new(size).to_kb.to_i.to_s + "K"
|
56
|
+
end
|
57
|
+
|
58
|
+
#------------------------------------------------------------
|
59
|
+
# Get the file-level 'bitrate' metadata.
|
60
|
+
#------------------------------------------------------------
|
61
|
+
def raw_bitrate
|
62
|
+
@format_metadata[:bit_rate].to_i
|
63
|
+
end
|
64
|
+
|
50
65
|
#------------------------------------------------------------
|
51
66
|
# Get the file-level 'title' metadata.
|
52
67
|
#------------------------------------------------------------
|
@@ -84,7 +99,7 @@ module MmTool
|
|
84
99
|
def format_table
|
85
100
|
unless @format_table
|
86
101
|
@format_table = format_table_datasource.render(:basic) do |renderer|
|
87
|
-
renderer.column_widths = [10,10,
|
102
|
+
renderer.column_widths = [10,10,10,160]
|
88
103
|
renderer.multiline = true
|
89
104
|
renderer.padding = [0,1]
|
90
105
|
renderer.width = 1000
|
@@ -192,8 +207,8 @@ module MmTool
|
|
192
207
|
#------------------------------------------------------------
|
193
208
|
def format_table_datasource
|
194
209
|
unless @format_table
|
195
|
-
@format_table = TTY::Table.new(header: %w(Duration: Size: Title:))
|
196
|
-
@format_table << [format_duration, format_size, format_title]
|
210
|
+
@format_table = TTY::Table.new(header: %w(Duration: Size: Bitrate: Title:))
|
211
|
+
@format_table << [format_duration, format_size, format_bitrate, format_title]
|
197
212
|
end
|
198
213
|
@format_table
|
199
214
|
end
|
@@ -15,7 +15,7 @@ module MmTool
|
|
15
15
|
# an array of MmMovieStreams reflecting the streams present
|
16
16
|
# in each of them.
|
17
17
|
#------------------------------------------------------------
|
18
|
-
def self.streams(with_files:)
|
18
|
+
def self.streams(with_files:, owner_ref:)
|
19
19
|
# Arrays are passed around by reference; when this array is created and
|
20
20
|
# used as a reference in each stream, and *also* returned from this class
|
21
21
|
# method, everyone will still be using the same reference. It's important
|
@@ -24,7 +24,7 @@ module MmTool
|
|
24
24
|
with_files.each_with_index do |path, i|
|
25
25
|
ff_movie = FFMPEG::Movie.new(path)
|
26
26
|
ff_movie.metadata[:streams].each do |stream|
|
27
|
-
streams << MmMovieStream.new(stream_data: stream, source_file: path, file_number: i, streams_ref: streams)
|
27
|
+
streams << MmMovieStream.new(stream_data: stream, source_file: path, file_number: i, streams_ref: streams, owner_ref: owner_ref)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
streams
|
@@ -33,12 +33,13 @@ module MmTool
|
|
33
33
|
#------------------------------------------------------------
|
34
34
|
# Initialize
|
35
35
|
#------------------------------------------------------------
|
36
|
-
def initialize(stream_data:, source_file:, file_number:, streams_ref:)
|
36
|
+
def initialize(stream_data:, source_file:, file_number:, streams_ref:, owner_ref:)
|
37
37
|
@defaults = MmUserDefaults.shared_user_defaults
|
38
38
|
@data = stream_data
|
39
39
|
@source_file = source_file
|
40
40
|
@file_number = file_number
|
41
41
|
@streams = streams_ref
|
42
|
+
@owner_ref = owner_ref
|
42
43
|
end
|
43
44
|
|
44
45
|
#------------------------------------------------------------
|
@@ -463,13 +464,47 @@ module MmTool
|
|
463
464
|
# Given a codec, return the ffmpeg encoder string.
|
464
465
|
#------------------------------------------------------------
|
465
466
|
def encoder_string(for_codec:)
|
467
|
+
|
468
|
+
# If we have to use bitrate for controlling quality, we want to ensure that we don't
|
469
|
+
# exceed the current bitrate, which would be a lossy conversion to a larger file.
|
470
|
+
# This only applies to videotoolbox on Intel. We'll use constant quality for everything else.
|
471
|
+
# Pretty much, we don't want to use videotoolbox on macOS. Hey? Why can't we use qsv on macOS?
|
472
|
+
rate_h264 = (@owner_ref.raw_bitrate * 0.750).to_i
|
473
|
+
rate_h265 = (@owner_ref.raw_bitrate * 0.500).to_i
|
474
|
+
|
475
|
+
string_h264 = ByteSize.new(rate_h264).to_kb.to_i.to_s + "K"
|
476
|
+
string_h265 = ByteSize.new(rate_h265).to_kb.to_i.to_s + "K"
|
477
|
+
|
478
|
+
# Constant quality mode is only available for Apple Silicon!
|
479
|
+
# Can't use -q:v 65 without Apple Silicon.
|
480
|
+
# Have to use -b:v 6000K (for example) on Intel.
|
481
|
+
# HEVC doesn't seem to work on Intel without -pix_fmt yuv420p10le.
|
482
|
+
encoder_strings = {
|
483
|
+
:libx264 => "libx264 -crf 23 -force_key_frames chapters",
|
484
|
+
:libx265 => "libx265 -crf 28 -x265-params log-level=error -force_key_frames chapters",
|
485
|
+
:h264_qsv => "h264_qsv -global_quality 22 -lookahead 1 -force_key_frames chapters",
|
486
|
+
:hevc_qsv => "hevc_qsv -global_quality 22 -lookahead 1 -force_key_frames chapters",
|
487
|
+
:h264_videotoolbox => "h264_videotoolbox -b:v #{string_h264} -force_key_frames chapters",
|
488
|
+
:hevc_videotoolbox => "hevc_videotoolbox -b:v #{string_h265} -pix_fmt yuv420p10le -force_key_frames chapters"
|
489
|
+
}
|
490
|
+
if RUBY_PLATFORM.downcase.start_with?('arm64')
|
491
|
+
encoder_strings[:h264_videotoolbox] = "h264_videotoolbox -q:v 50 -force_key_frames chapters"
|
492
|
+
encoder_strings[:hevc_videotoolbox] = "hevc_videotoolbox -q:v 50 -force_key_frames chapters"
|
493
|
+
end
|
494
|
+
|
495
|
+
encoder = @defaults[:encoder].to_sym
|
496
|
+
|
466
497
|
case for_codec.downcase
|
467
498
|
when 'hevc'
|
468
|
-
|
499
|
+
return encoder_strings[:libx265] if [:auto, :libx265].include?(encoder)
|
500
|
+
return encoder_strings[:hevc_qsv] if encoder == :hevc_qsv
|
501
|
+
return encoder_strings[:hevc_videotoolbox] if encoder == :hevc_videotoolbox
|
469
502
|
when 'h264'
|
470
|
-
|
503
|
+
return encoder_strings[:libx264] if [:auto, :libx264].include?(encoder)
|
504
|
+
return encoder_strings[:h264_qsv] if encoder == :h264_qsv
|
505
|
+
return encoder_strings[:h264_videotoolbox] if encoder == :h264_videotoolbox
|
471
506
|
when 'aac'
|
472
|
-
"libfdk_aac"
|
507
|
+
return "libfdk_aac"
|
473
508
|
else
|
474
509
|
raise Exception.new "Error: somehow an unsupported codec '#{for_codec}' was specified."
|
475
510
|
end
|
data/lib/mm_tool/mm_tool_cli.rb
CHANGED
@@ -12,6 +12,11 @@ module MmTool
|
|
12
12
|
# Initialize
|
13
13
|
#------------------------------------------------------------
|
14
14
|
def initialize(app_instance = MmTool::ApplicationMain.shared_application)
|
15
|
+
self.validate_prerequisites
|
16
|
+
|
17
|
+
decorated_list = MmTool.encoder_list.map{|s| C.bold(s)}.join(', ')
|
18
|
+
USER_DEFAULTS[:encoder][:help_desc].gsub!('ENCODER_LIST', decorated_list)
|
19
|
+
|
15
20
|
@application = app_instance
|
16
21
|
@defaults = MmUserDefaults.shared_user_defaults
|
17
22
|
@defaults.register_defaults(with_hash: USER_DEFAULTS)
|
@@ -58,10 +63,10 @@ module MmTool
|
|
58
63
|
#------------------------------------------------------------
|
59
64
|
#noinspection RubyResolve
|
60
65
|
def validate_prerequisites
|
61
|
-
commands
|
62
|
-
|
63
|
-
task
|
64
|
-
success
|
66
|
+
commands = %w(ffmpeg ffprobe)
|
67
|
+
codecs_required = %w(libx264 libx265 libfdk_aac)
|
68
|
+
task = TTY::Command.new(printer: :null)
|
69
|
+
success = true
|
65
70
|
|
66
71
|
# We'll check everything in items before failing, so that we can provide
|
67
72
|
# a comprehensive list to the user. No one wants to see what's missing
|
@@ -74,17 +79,17 @@ module MmTool
|
|
74
79
|
end
|
75
80
|
exit 1 unless success
|
76
81
|
|
77
|
-
# Now we'll make sure that all of the codecs
|
78
|
-
#
|
79
|
-
# binary distribution supports non-free.
|
82
|
+
# Now we'll make sure that all of the required codecs are installed as part of ffmpeg.
|
83
|
+
# This is necessary because not every binary distribution supports non-free.
|
80
84
|
|
81
85
|
# Again, we'll check them all before failing in order to list everything.
|
82
|
-
|
86
|
+
codecs_required.each do |codec|
|
83
87
|
result = task.run!("ffprobe -v quiet -codecs | grep #{codec}")
|
84
88
|
OutputHelper.print_error("Error: ffmpeg was built without support for the #{C.bold(codec)} codec, which is required.") if result.failure?
|
85
89
|
success = success && result.success?
|
86
90
|
end
|
87
91
|
exit 1 unless success
|
92
|
+
|
88
93
|
end
|
89
94
|
|
90
95
|
#------------------------------------------------------------
|
@@ -271,6 +276,9 @@ module MmTool
|
|
271
276
|
when '--fix-undefined-language'
|
272
277
|
@defaults[:fix_undefined_language] = true
|
273
278
|
|
279
|
+
when '--encoder'
|
280
|
+
@defaults[:encoder] = validate_encoder_value(args[0], args[1])
|
281
|
+
|
274
282
|
#-----------------------
|
275
283
|
# Quality
|
276
284
|
#-----------------------
|
@@ -328,6 +336,23 @@ module MmTool
|
|
328
336
|
value
|
329
337
|
end
|
330
338
|
|
339
|
+
#------------------------------------------------------------
|
340
|
+
# Perform a really simple validation of the given value for
|
341
|
+
# the given argument, returning the value if successful.
|
342
|
+
# ------------------------------------------------------------
|
343
|
+
#noinspection RubyResolve
|
344
|
+
def validate_encoder_value(arg, value)
|
345
|
+
if !value
|
346
|
+
OutputHelper.print_error_and_exit("Error: option #{C.bold(arg)} was specified, but no value was given.")
|
347
|
+
elsif value =~ /^-.*$/
|
348
|
+
OutputHelper.print_error_and_exit("Error: option #{C.bold(arg)} was specified, but the value #{C.bold(value)} looks like another option argument.")
|
349
|
+
end
|
350
|
+
unless MmTool.encoder_list.include?(value)
|
351
|
+
OutputHelper.print_error_and_exit("Error: Unknown encoder #{C.bold(value)} specified for option #{C.bold(arg)}. Use #{C.bold(File.basename($0) << '--help')} for supported encoders.")
|
352
|
+
end
|
353
|
+
value
|
354
|
+
end
|
355
|
+
|
331
356
|
end # class
|
332
357
|
|
333
358
|
end # module
|
@@ -356,6 +356,22 @@ module MmTool
|
|
356
356
|
HEREDOC
|
357
357
|
},
|
358
358
|
|
359
|
+
:encoder => {
|
360
|
+
:default => 'auto',
|
361
|
+
:value => nil,
|
362
|
+
:arg_short => nil,
|
363
|
+
:arg_long => '--encoder',
|
364
|
+
:arg_format => nil,
|
365
|
+
:item_label => 'Specify Non-default Encoder',
|
366
|
+
:help_group => 'Transcoding Options',
|
367
|
+
:help_desc => <<~HEREDOC
|
368
|
+
You can use #{C.bold('--encoder')} to specify the encoder to use when mm_tool determines that
|
369
|
+
transcoding should take place. Use one of ENCODER_LIST, or #{C.bold('auto')}. The default is
|
370
|
+
#{C.bold('%s')}. Note that #{C.bold('auto')} will choose #{C.bold('libx264')} or
|
371
|
+
#{C.bold('libx265')}, depending on the value of #{C.bold('--codecs-video-preferred')}.
|
372
|
+
HEREDOC
|
373
|
+
},
|
374
|
+
|
359
375
|
#----------------------------
|
360
376
|
# Quality Options
|
361
377
|
#----------------------------
|
data/lib/mm_tool/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mm_tool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Derry
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tty-table
|
@@ -185,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: '0'
|
187
187
|
requirements: []
|
188
|
-
rubygems_version: 3.
|
188
|
+
rubygems_version: 3.1.6
|
189
189
|
signing_key:
|
190
190
|
specification_version: 4
|
191
191
|
summary: Curate your movie files.
|