mm_tool 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitattributes +4 -0
- data/.gitignore +24 -0
- data/Gemfile +3 -0
- data/LICENSE.md +22 -0
- data/README.md +15 -0
- data/Rakefile +2 -0
- data/Truth Tables.xlsx +0 -0
- data/bin/console +14 -0
- data/bin/mm_tool +19 -0
- data/bin/setup +8 -0
- data/lib/mm_tool.rb +21 -0
- data/lib/mm_tool/application_main.rb +172 -0
- data/lib/mm_tool/mm_movie.rb +217 -0
- data/lib/mm_tool/mm_movie_ignore_list.rb +68 -0
- data/lib/mm_tool/mm_movie_stream.rb +492 -0
- data/lib/mm_tool/mm_tool_cli.rb +322 -0
- data/lib/mm_tool/mm_user_defaults.rb +290 -0
- data/lib/mm_tool/output_helper.rb +121 -0
- data/lib/mm_tool/user_defaults.rb +377 -0
- data/lib/mm_tool/version.rb +3 -0
- data/mm_tool.gemspec +50 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: eb195a344b506ce9706580970d5e8db8843bf53b537161d63577dc84577089dc
|
4
|
+
data.tar.gz: b06f0eff5f6b0aeddd08b5ac1792d9cdf0869df5babab29fb771f9d3fac28e9d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a68290286f7db0bbcf5a0440c22ad3a213224439530c73280e70426fc52601a2d52d9c9dac278c7558de9b06317b8e284aae78ca6031b7b74f56f3b699c5823c
|
7
|
+
data.tar.gz: 769b79d23c56d70d9bbf4fa857629bad1376e332e7b6c87280a86d7d21dd0c54fe029c222a000dd7861c7aa764988372f81fc4e2dc754de3110bd79dd8a0bcb6
|
data/.gitattributes
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Rubymine noise
|
2
|
+
.idea/
|
3
|
+
|
4
|
+
# BBedit noise
|
5
|
+
*.bbprojectd
|
6
|
+
|
7
|
+
# Mac OS X noise
|
8
|
+
.DS_Store
|
9
|
+
|
10
|
+
# Ignore bundler lock files
|
11
|
+
Gemfile.lock
|
12
|
+
|
13
|
+
# Ignore pkg folder
|
14
|
+
/pkg
|
15
|
+
|
16
|
+
# Ignore build folders
|
17
|
+
build/*
|
18
|
+
|
19
|
+
# Yard cruft
|
20
|
+
/doc/
|
21
|
+
/.yardoc
|
22
|
+
|
23
|
+
# Aruba
|
24
|
+
/tmp/
|
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
===============
|
3
|
+
|
4
|
+
Copyright (c) 2020 Jim Derry
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/Truth Tables.xlsx
ADDED
Binary file
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mm_tool"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/mm_tool
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#=============================================================================
|
4
|
+
# This module consolidates all of the classes the make up a complete MmTool
|
5
|
+
# application, and is spread throughout several files based mostly on the
|
6
|
+
# classes they contain.
|
7
|
+
#=============================================================================
|
8
|
+
module MmTool
|
9
|
+
require_relative '../lib/mm_tool'
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
#=============================================================================
|
14
|
+
# Main
|
15
|
+
#=============================================================================
|
16
|
+
|
17
|
+
cli = MmTool::MmToolCli.new(MmTool::ApplicationMain.shared_application)
|
18
|
+
cli.validate_prerequisites
|
19
|
+
cli.run(ARGV)
|
data/bin/setup
ADDED
data/lib/mm_tool.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Setup our load paths
|
2
|
+
libdir = File.expand_path(File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
4
|
+
|
5
|
+
require "mm_tool/output_helper"
|
6
|
+
require "mm_tool/user_defaults"
|
7
|
+
require "mm_tool/version"
|
8
|
+
|
9
|
+
require "mm_tool/application_main"
|
10
|
+
require "mm_tool/mm_tool_cli"
|
11
|
+
|
12
|
+
require 'mm_tool/mm_movie'
|
13
|
+
require 'mm_tool/mm_movie_stream'
|
14
|
+
|
15
|
+
require 'mm_tool/mm_movie_ignore_list'
|
16
|
+
require "mm_tool/mm_user_defaults"
|
17
|
+
|
18
|
+
module MmTool
|
19
|
+
class Error < StandardError
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module MmTool
|
2
|
+
|
3
|
+
#=============================================================================
|
4
|
+
# The main application.
|
5
|
+
#=============================================================================
|
6
|
+
class ApplicationMain
|
7
|
+
|
8
|
+
require 'mm_tool.rb'
|
9
|
+
|
10
|
+
#------------------------------------------------------------
|
11
|
+
# Attributes
|
12
|
+
#------------------------------------------------------------
|
13
|
+
attr_accessor :tempfile
|
14
|
+
|
15
|
+
#------------------------------------------------------------
|
16
|
+
# Initialize
|
17
|
+
#------------------------------------------------------------
|
18
|
+
def initialize
|
19
|
+
@tempfile = nil
|
20
|
+
@defaults = MmUserDefaults.shared_user_defaults
|
21
|
+
end
|
22
|
+
|
23
|
+
#------------------------------------------------------------
|
24
|
+
# Singleton accessor
|
25
|
+
#------------------------------------------------------------
|
26
|
+
def self.shared_application
|
27
|
+
unless @self
|
28
|
+
@self = self.new
|
29
|
+
end
|
30
|
+
@self
|
31
|
+
end
|
32
|
+
|
33
|
+
#------------------------------------------------------------
|
34
|
+
# Output a message to the screen, and if applicable, to
|
35
|
+
# the temporary file for later opening.
|
36
|
+
#------------------------------------------------------------
|
37
|
+
def output(message, command = false)
|
38
|
+
puts message
|
39
|
+
if @tempfile
|
40
|
+
if command
|
41
|
+
message = message + "\n"
|
42
|
+
else
|
43
|
+
message = message.lines.collect {|line| "# #{line}"}.join + "\n"
|
44
|
+
end
|
45
|
+
@tempfile&.write(message)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#------------------------------------------------------------
|
50
|
+
# Return the transcode file header.
|
51
|
+
#------------------------------------------------------------
|
52
|
+
def transcode_script_header
|
53
|
+
<<~HEREDOC
|
54
|
+
#!/bin/sh
|
55
|
+
|
56
|
+
# Check this file, make any changes, and save it. It will be executed as soon
|
57
|
+
# as you close it. By default, this script will exit per the exit command
|
58
|
+
# below. Please further confirm that you wish to proceed with the proposed
|
59
|
+
# actions by commenting or removing the line below.
|
60
|
+
|
61
|
+
exit 1
|
62
|
+
|
63
|
+
HEREDOC
|
64
|
+
end
|
65
|
+
|
66
|
+
#------------------------------------------------------------
|
67
|
+
# Return the report header.
|
68
|
+
#------------------------------------------------------------
|
69
|
+
#noinspection RubyResolve
|
70
|
+
def information_header
|
71
|
+
info_table_src = TTY::Table.new
|
72
|
+
@defaults.label_value_pairs.each {|pair| info_table_src << pair}
|
73
|
+
info_table_src << ["Disposition Columns:", MmMovie.dispositions.join(', ')]
|
74
|
+
info_table_src << ["Transcode File Location:", self.tempfile ? tempfile&.path : 'n/a']
|
75
|
+
|
76
|
+
info_table = C.bold("Looking for file(s) and processing them with the following options:\n")
|
77
|
+
info_table << info_table_src.render(:basic) do |renderer|
|
78
|
+
a = @defaults.label_value_pairs.map {|p| p[0].length }.max + 1
|
79
|
+
b = OutputHelper.console_width - a - 2
|
80
|
+
renderer.alignments = [:right, :left]
|
81
|
+
renderer.multiline = true
|
82
|
+
renderer.column_widths = [a,b]
|
83
|
+
end << "\n\n"
|
84
|
+
end
|
85
|
+
|
86
|
+
#------------------------------------------------------------
|
87
|
+
# The main run loop, to be run for each file.
|
88
|
+
#------------------------------------------------------------
|
89
|
+
def run_loop(file_name)
|
90
|
+
|
91
|
+
if @defaults[:ignore_files]
|
92
|
+
MmMovieIgnoreList.shared_ignore_list.add(path: file_name)
|
93
|
+
output("Note: added #{file_name} to the list of files to be ignored.")
|
94
|
+
elsif @defaults[:unignore_files]
|
95
|
+
MmMovieIgnoreList.shared_ignore_list.remove(path: file_name)
|
96
|
+
output("Note: removed #{file_name} to the list of files to be ignored.")
|
97
|
+
else
|
98
|
+
@file_count[:processed] = @file_count[:processed] + 1
|
99
|
+
movie = MmMovie.new(with_file: file_name)
|
100
|
+
a = movie.interesting?
|
101
|
+
b = MmMovieIgnoreList.shared_ignore_list.include?(file_name)
|
102
|
+
c = movie.low_quality?
|
103
|
+
s = @defaults[:scan_type]&.to_sym
|
104
|
+
|
105
|
+
if (s == :normal && a && !b && !c) ||
|
106
|
+
(s == :all && !b) ||
|
107
|
+
(s == :flagged && b) ||
|
108
|
+
(s == :quality && c ) ||
|
109
|
+
(s == :force)
|
110
|
+
|
111
|
+
@file_count[:displayed] = @file_count[:displayed] + 1
|
112
|
+
output(file_name)
|
113
|
+
output(movie.format_table)
|
114
|
+
output(movie.stream_table)
|
115
|
+
output("#{movie.command_rename} ; \\", true)
|
116
|
+
output("#{movie.command_transcode} ; \\", true)
|
117
|
+
output(movie.command_review_post, true)
|
118
|
+
output("\n\n", true)
|
119
|
+
end
|
120
|
+
end # if
|
121
|
+
end
|
122
|
+
|
123
|
+
#------------------------------------------------------------
|
124
|
+
# Run the application with the given file/directory.
|
125
|
+
#------------------------------------------------------------
|
126
|
+
def run(file_or_dir)
|
127
|
+
|
128
|
+
@file_count = { :processed => 0, :displayed => 0 }
|
129
|
+
|
130
|
+
if @defaults[:transcode]
|
131
|
+
@tempfile = Tempfile.create(['mm_tool-', '.sh'])
|
132
|
+
@tempfile&.write(transcode_script_header)
|
133
|
+
# @tempfile.flush
|
134
|
+
end
|
135
|
+
|
136
|
+
if @defaults[:info_header]
|
137
|
+
output information_header
|
138
|
+
end
|
139
|
+
|
140
|
+
if File.file?(file_or_dir)
|
141
|
+
|
142
|
+
original_scan_type = @defaults[:scan_type]
|
143
|
+
@defaults[:scan_type] = :force
|
144
|
+
run_loop(file_or_dir)
|
145
|
+
@defaults[:scan_type] = original_scan_type
|
146
|
+
|
147
|
+
elsif File.directory?(file_or_dir)
|
148
|
+
|
149
|
+
extensions = @defaults[:container_files]&.join(',')
|
150
|
+
Dir.chdir(file_or_dir) do
|
151
|
+
Dir.glob("**/*.{#{extensions}}").map {|path| File.expand_path(path) }.sort.each do |file|
|
152
|
+
run_loop(file)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
else
|
157
|
+
output "Error: Execution should never have reached this point."
|
158
|
+
exit 1
|
159
|
+
end
|
160
|
+
|
161
|
+
output("#{File.basename($0)} processed #{@file_count[:processed]} files and displayed data for #{@file_count[:displayed]} of them.")
|
162
|
+
|
163
|
+
ensure
|
164
|
+
if @tempfile
|
165
|
+
@tempfile&.close
|
166
|
+
# @tempfile.unlink
|
167
|
+
end
|
168
|
+
end # run
|
169
|
+
|
170
|
+
end # class
|
171
|
+
|
172
|
+
end # module
|
@@ -0,0 +1,217 @@
|
|
1
|
+
module MmTool
|
2
|
+
|
3
|
+
#=============================================================================
|
4
|
+
# A movie as a self contained class. Instances of this class consist of
|
5
|
+
# one or more MmMovieStream instances, and contains intelligence about itself
|
6
|
+
# so that it can provide commands to ffmpeg and/or mkvpropedit as required.
|
7
|
+
# Upon creation it must be provided with a filename.
|
8
|
+
#=============================================================================
|
9
|
+
class MmMovie
|
10
|
+
|
11
|
+
require 'streamio-ffmpeg'
|
12
|
+
require 'tty-table'
|
13
|
+
require 'bytesize'
|
14
|
+
|
15
|
+
#------------------------------------------------------------
|
16
|
+
# This class method returns the known dispositions supported
|
17
|
+
# by ffmpeg. This array also reflects the output orders in
|
18
|
+
# the dispositions table field. Not combining them would
|
19
|
+
# result in a too-long table row.
|
20
|
+
#------------------------------------------------------------
|
21
|
+
def self.dispositions
|
22
|
+
%i(default dub original comment lyrics karaoke forced hearing_impaired visual_impaired clean_effects attached_pic timed_thumbnails)
|
23
|
+
end
|
24
|
+
|
25
|
+
#------------------------------------------------------------
|
26
|
+
# Initialize
|
27
|
+
#------------------------------------------------------------
|
28
|
+
def initialize(with_file:)
|
29
|
+
@defaults = MmUserDefaults.shared_user_defaults
|
30
|
+
@streams = MmMovieStream::streams(with_files: all_paths(with_file: with_file))
|
31
|
+
@format_metadata = FFMPEG::Movie.new(with_file).metadata[:format]
|
32
|
+
end
|
33
|
+
|
34
|
+
#------------------------------------------------------------
|
35
|
+
# Get the file-level 'duration' metadata.
|
36
|
+
#------------------------------------------------------------
|
37
|
+
def format_duration
|
38
|
+
seconds = @format_metadata[:duration]
|
39
|
+
seconds ? Time.at(seconds.to_i).utc.strftime("%H:%M:%S") : nil
|
40
|
+
end
|
41
|
+
|
42
|
+
#------------------------------------------------------------
|
43
|
+
# Get the file-level 'size' metadata.
|
44
|
+
#------------------------------------------------------------
|
45
|
+
def format_size
|
46
|
+
size = @format_metadata[:size]
|
47
|
+
size ? ByteSize.new(size) : 'unknown'
|
48
|
+
end
|
49
|
+
|
50
|
+
#------------------------------------------------------------
|
51
|
+
# Get the file-level 'title' metadata.
|
52
|
+
#------------------------------------------------------------
|
53
|
+
def format_title
|
54
|
+
@format_metadata&.dig(:tags, :title)
|
55
|
+
end
|
56
|
+
|
57
|
+
#------------------------------------------------------------
|
58
|
+
# Indicates whether any of the streams are of a lower
|
59
|
+
# quality than desired by the user.
|
60
|
+
#------------------------------------------------------------
|
61
|
+
def low_quality?
|
62
|
+
@streams.count {|stream| stream.low_quality?} > 0
|
63
|
+
end
|
64
|
+
|
65
|
+
#------------------------------------------------------------
|
66
|
+
# Indicates whether any of the streams are interesting.
|
67
|
+
#------------------------------------------------------------
|
68
|
+
def interesting?
|
69
|
+
@streams.count {|stream| stream.interesting?} > 0 || format_title
|
70
|
+
end
|
71
|
+
|
72
|
+
#------------------------------------------------------------
|
73
|
+
# Get the rendered text of the format_table.
|
74
|
+
#------------------------------------------------------------
|
75
|
+
def format_table
|
76
|
+
unless @format_table
|
77
|
+
@format_table = format_table_datasource.render(:basic) do |renderer|
|
78
|
+
renderer.column_widths = [10,10, 160]
|
79
|
+
renderer.multiline = true
|
80
|
+
renderer.padding = [0,1]
|
81
|
+
renderer.width = 1000
|
82
|
+
end
|
83
|
+
end
|
84
|
+
@format_table
|
85
|
+
end
|
86
|
+
|
87
|
+
#------------------------------------------------------------
|
88
|
+
# For the given table, get the rendered text of the table
|
89
|
+
# for output.
|
90
|
+
#------------------------------------------------------------
|
91
|
+
def stream_table
|
92
|
+
unless @stream_table
|
93
|
+
@stream_table = stream_table_datasource.render(:unicode) do |renderer|
|
94
|
+
renderer.alignments = [:center, :left, :left, :right, :right, :left, :left, :left, :left]
|
95
|
+
renderer.column_widths = [5,10,10,5,10,5,23,50,35]
|
96
|
+
renderer.multiline = true
|
97
|
+
renderer.padding = [0,1]
|
98
|
+
renderer.width = 1000
|
99
|
+
end # do
|
100
|
+
end
|
101
|
+
@stream_table
|
102
|
+
end
|
103
|
+
|
104
|
+
#------------------------------------------------------------
|
105
|
+
# The complete command to rename the main input file to
|
106
|
+
# include a tag indicating that it's the original.
|
107
|
+
#------------------------------------------------------------
|
108
|
+
def command_rename
|
109
|
+
src = @streams[0].source_file
|
110
|
+
dst = File.join(File.dirname(src), File.basename(src, '.*') + @defaults[:suffix] + File.extname(src))
|
111
|
+
"mv \"#{src}\" \"#{dst}\""
|
112
|
+
end
|
113
|
+
|
114
|
+
#------------------------------------------------------------
|
115
|
+
# The complete, proposed ffmpeg command to transcode the
|
116
|
+
# input file to an output file. It uses the 'new_input_path'
|
117
|
+
# as the input file.
|
118
|
+
#------------------------------------------------------------
|
119
|
+
def command_transcode
|
120
|
+
command = ["ffmpeg \\"]
|
121
|
+
@streams.each {|stream| command |= [" #{stream.instruction_input}"] if stream.instruction_input }
|
122
|
+
@streams.each {|stream| command << " #{stream.instruction_map}" if stream.instruction_map }
|
123
|
+
@streams.each {|stream| command << " #{stream.instruction_action}" if stream.instruction_action }
|
124
|
+
@streams.each {|stream| command << " #{stream.instruction_disposition}" if stream.instruction_disposition }
|
125
|
+
@streams.each {|stream| command << " #{stream.instruction_metadata}" if stream.instruction_metadata }
|
126
|
+
command << " -metadata title=\"#{format_title}\" \\" if format_title
|
127
|
+
command << " \"#{output_path}\""
|
128
|
+
command.join("\n")
|
129
|
+
end
|
130
|
+
|
131
|
+
#------------------------------------------------------------
|
132
|
+
# The complete command to view the output file after
|
133
|
+
# running the transcode command
|
134
|
+
#------------------------------------------------------------
|
135
|
+
def command_review_post
|
136
|
+
"\"#{$0}\" --no-use-external-subs \"#{output_path}\""
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
#============================================================
|
141
|
+
private
|
142
|
+
#============================================================
|
143
|
+
|
144
|
+
#------------------------------------------------------------
|
145
|
+
# Given the initial file, return an array of the initial
|
146
|
+
# file and associated SRTs, which are valid if they have
|
147
|
+
# no language, or a language specified in options.
|
148
|
+
#------------------------------------------------------------
|
149
|
+
def all_paths(with_file:)
|
150
|
+
all_paths = [with_file]
|
151
|
+
if @defaults[:use_external_subs]
|
152
|
+
base_path = File.join(File.dirname(with_file), File.basename(with_file, '.*'))
|
153
|
+
all_paths |= ([""] | @defaults[:keep_langs_subs]&.map {|lang| ".#{lang}" })
|
154
|
+
.select {|lang| File.file?("#{base_path}#{lang}.srt")}
|
155
|
+
.map {|lang| "#{base_path}#{lang}.srt"}
|
156
|
+
end
|
157
|
+
all_paths
|
158
|
+
end
|
159
|
+
|
160
|
+
#------------------------------------------------------------
|
161
|
+
# The name of the proposed output file, if different from
|
162
|
+
# the input file. This would be set in the event that the
|
163
|
+
# container of the input file is not one of the approved
|
164
|
+
# containers.
|
165
|
+
#------------------------------------------------------------
|
166
|
+
def output_path
|
167
|
+
path = @streams[0].source_file
|
168
|
+
if @defaults[:containers_preferred]&.include?(File.extname(path))
|
169
|
+
path
|
170
|
+
else
|
171
|
+
File.join(File.dirname(path), File.basename(path, '.*') + '.' + @defaults[:containers_preferred][0])
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
#------------------------------------------------------------
|
176
|
+
# Return a TTY::Table of the relevant format data.
|
177
|
+
#------------------------------------------------------------
|
178
|
+
def format_table_datasource
|
179
|
+
unless @format_table
|
180
|
+
@format_table = TTY::Table.new(header: %w(Duration: Size: Title:))
|
181
|
+
@format_table << [format_duration, format_size, format_title]
|
182
|
+
end
|
183
|
+
@format_table
|
184
|
+
end
|
185
|
+
|
186
|
+
#------------------------------------------------------------
|
187
|
+
# Return a TTY::Table of the movie, populated with the
|
188
|
+
# pertinent data of each stream.
|
189
|
+
#------------------------------------------------------------
|
190
|
+
def stream_table_datasource
|
191
|
+
unless @table
|
192
|
+
# Ensure that when we add the headers, they specifically are left-aligned.
|
193
|
+
headers = %w(index codec type w/# h/layout lang disposition title action(s))
|
194
|
+
.map { |header| {:value => header, :alignment => :left} }
|
195
|
+
|
196
|
+
@table = TTY::Table.new(header: headers)
|
197
|
+
|
198
|
+
@streams.each do |stream|
|
199
|
+
row = []
|
200
|
+
row << stream.input_specifier
|
201
|
+
row << stream.codec_name
|
202
|
+
row << stream.codec_type
|
203
|
+
row << stream.quality_01
|
204
|
+
row << stream.quality_02
|
205
|
+
row << stream.language
|
206
|
+
row << stream.dispositions
|
207
|
+
row << stream.title
|
208
|
+
row << stream.action_label
|
209
|
+
@table << row
|
210
|
+
end
|
211
|
+
end
|
212
|
+
@table
|
213
|
+
end # table
|
214
|
+
|
215
|
+
end # class
|
216
|
+
|
217
|
+
end # module
|