ffmpeg_progress 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90f77bff70d2e03a8fcef49490199f652ee1c78f
4
- data.tar.gz: 45f4aff12498c91bfe9c3670ea0b53d51eb37285
3
+ metadata.gz: a87f8c15bd7699211c31d0b253c355884e9ea1f0
4
+ data.tar.gz: 3bfc1f6bd6c6cd176f34c93a90c272735eb01588
5
5
  SHA512:
6
- metadata.gz: 16c6f7766bb04438b666e84655916a8fc05b27107ec67ce2dcb1510e86e53b95eddbc2cf3da452dfb875b269d42c703f0adf37b1370b9a2f911acfa9bc8c96d5
7
- data.tar.gz: 6560808c7430e8f3c2b868039c3a46170e4cd719384831a1324e749ae3beb771632b283658f8164ed0e708343b4f858a31d818f242d39db12dc7f3af76de50b3
6
+ metadata.gz: 71f43b1a4be93308c0a3e049896d981f6d8d3f3ac057257548b2befabc996e89688f6a1f7576a767c2992daad98974dff1e69c1bae41db76e3afb0293f9138cf
7
+ data.tar.gz: 17e574551623658a375a4b8375b254f5abf59d833875e375841e70fcc6758fd0d3e022dae17a39ef3d62e81d5fc7b6b0b014ae12e486d512ad0db072ed90c7ee
@@ -1,4 +1,4 @@
1
- =FfmpegProgress
1
+ =FFmpegProgress
2
2
 
3
3
  {<img src="http://inch-ci.org/github/Winterbraid/ffmpeg_progress.svg?branch=master" alt="Inline docs" />}[http://inch-ci.org/github/Winterbraid/ffmpeg_progress]
4
4
  {<img src="https://codeclimate.com/github/Winterbraid/ffmpeg_progress/badges/gpa.svg" />}[https://codeclimate.com/github/Winterbraid/ffmpeg_progress]
@@ -12,14 +12,14 @@ usual garbage.
12
12
 
13
13
  Convert from the shell with default options:
14
14
  # Will save to ./converted/test.mp4
15
- ruby -rffmpeg_progress -e "FfmpegProgress::Ffmpeg.new('test.avi').run"
15
+ ruby -rffmpeg_progress -e "FFmpegProgress::FFmpeg.new('test.avi').run"
16
16
 
17
17
  ==Example Use
18
18
  require 'ffmpeg_progress'
19
19
 
20
- include FfmpegProgress
20
+ include FFmpegProgress
21
21
 
22
- f = Ffmpeg.new 'input.avi'
22
+ f = FFmpeg.new 'input.avi'
23
23
 
24
24
  # Occurrences of '_INPUT_' will be replaced with the input file name.
25
25
  f.options = '-y -threads 0 -c:v libx264 -crf 22 -preset medium -c:a copy'
@@ -1,253 +1,11 @@
1
1
  require 'fileutils'
2
2
 
3
- # {include:file:README.rdoc}
4
- module FfmpegProgress
5
- # The current software version.
6
- VERSION = '1.0.2'
7
-
8
- # The date of the current version.
9
- DATE = '2014-10-31'
10
-
11
- # A short description of the software.
12
- ABOUT = 'A fancy progress bar for ffmpeg.'
13
-
14
- # Contains various examples of +ffmpeg+ options.
15
- module Presets
16
- # The default options. Convert the video to x264 and audio to AAC.
17
- DEFAULT_OPTS = '-y -threads 0 -strict -2 -c:a aac -b:a 96k ' \
18
- '-c:v libx264 -preset fast -tune fastdecode -crf 22 '
19
-
20
- # Read the default subtitles embedded in an input MKV file and hardcode
21
- # them into the video. Any embedded fonts must be extracted and installed
22
- # first to preserve them in the output, for example:
23
- # mkvextract attachments input.mkv {1..10}
24
- # cp *.ttf *.ttc *.otf ~/.fonts
25
- BURN_MKV_SUBS = '-y -threads 0 -strict -2 -sn -c:a copy ' \
26
- '-c:v libx264 -preset fast -tune fastdecode -crf 22 ' \
27
- '-vf "subtitles=_INPUT_"'
28
-
29
- # Like {BURN_MKV_SUBS}, but also downscale the video to 800px width.
30
- BURN_800 = '-y -threads 0 -strict -2 -sn -c:a aac -b:a 96k ' \
31
- '-c:v libx264 -preset fast -tune fastdecode -crf 22 ' \
32
- '-vf "[in]scale=800:-2[tmp];[tmp]subtitles=_INPUT_[out]"'
33
- end
34
-
35
- # Helper methods.
36
- module Utils
37
- # Colorize a string for the terminal (256-color mode).
38
- #
39
- # @param [String] string
40
- # @param [Integer] color
41
- # @return [String]
42
- def colorize(string, color)
43
- "\x1b[38;5;#{color}m#{string}\x1b[0m"
44
- end
45
-
46
- # Parse a time string in the +ffmpeg+ HH:MM:SS.ms format and return seconds.
47
- #
48
- # @param [String] time_string
49
- # @return [Integer]
50
- def parse_ffmpeg_time(time_string)
51
- array = time_string.rpartition('.').first.split(':').map(&:to_i)
52
-
53
- array[0] * 3600 + array[1] * 60 + array[2]
54
- end
55
- end
56
-
57
- # The class that does the actual work.
58
- class Ffmpeg
59
- include Presets
60
- include Utils
61
-
62
- # The log file for ffmpeg. Progress data will be read from here.
63
- LOG_FILE = 'ffmpeg.log'
64
-
65
- # The extension for the default output file.
66
- DEFAULT_EXT = 'mp4'
67
-
68
- # The directory for the default output file.
69
- DEFAULT_DIR = 'converted/'
70
-
71
- # The default theme, change this with {#theme=}. {FfmpegProgress} expects
72
- # a 256-color capable terminal.
73
- DEFAULT_THEME = {
74
- bars: 63, head: '[', full: '=', empty: '-', tail: ']',
75
- end_fg: 202, full_fg: 214, empty_fg: 202,
76
- time_fg: 214, block_fg: 214, finish_fg: 40, cancel_fg: 1
77
- }
78
-
79
- # The input file.
80
- # @return [String]
81
- attr_reader :input
82
-
83
- # The output file. Directories will be auto-created on execution.
84
- # @return [String]
85
- attr_accessor :output
86
-
87
- # Returns the duration (in seconds) of the input file.
88
- # @return [Integer]
89
- attr_reader :duration
90
-
91
- # Returns the duration of the input file as a +ffmpeg+ format string
92
- # (+HH:MM:SS.ms+).
93
- #
94
- # @return [Integer]
95
- attr_reader :duration_ff
96
-
97
- # The options to pass to +ffmpeg+. Any occurrences of the string +_INPUT_+
98
- # will be converted to the value of the {#input} attribute.
99
- # @return [String]
100
- attr_accessor :options
101
-
102
- # The theme for the progress bar. See {DEFAULT_THEME} for details.
103
- # @return [Hash]
104
- attr_accessor :theme
105
-
106
- # The PID of the last spawned +ffmpeg+ process, or +nil+ if none spawned
107
- # yet.
108
- # @return [Integer]
109
- attr_reader :pid
110
-
111
- # Creates a new {Ffmpeg} instance.
112
- #
113
- # @param [String] input_file the source file name.
114
- # @param [String] ffmpeg_options the options to pass to ffmpeg.
115
- # See {#options} for details and {Presets} for examples.
116
- # @return [Ffmpeg]
117
- def initialize(input_file = nil, ffmpeg_options = DEFAULT_OPTS)
118
- self.input = input_file
119
- @options = ffmpeg_options
120
-
121
- @output ||= nil
122
- @duration_ff ||= nil
123
- @duration ||= nil
124
-
125
- @block = nil
126
-
127
- @pid = nil
128
- @theme = DEFAULT_THEME
129
-
130
- @last_bar = nil
131
- end
3
+ require 'ffmpeg_progress/metadata.rb'
132
4
 
133
- # Set the input file.
134
- #
135
- # @param [String] string
136
- # @return [String]
137
- def input=(string)
138
- @input = string
5
+ require 'ffmpeg_progress/presets.rb'
6
+ require 'ffmpeg_progress/utils.rb'
7
+ require 'ffmpeg_progress/theme.rb'
8
+ require 'ffmpeg_progress/ffmpeg.rb'
139
9
 
140
- return @input unless @input
141
-
142
- @duration_ff = `ffmpeg -i \'#{@input}\' 2>&1`.scan(/..:..:..\.../).first
143
- @duration = parse_ffmpeg_time(@duration_ff)
144
-
145
- @output ||= "#{DEFAULT_DIR}#{@input.rpartition('.').first}.#{DEFAULT_EXT}"
146
-
147
- @input
148
- end
149
-
150
- # Returns the current +ffmpeg+ command that will be executed by {#run}.
151
- #
152
- # @return [String]
153
- def command
154
- "ffmpeg -i \'#{@input}\' #{@options} \'#{@output}\' &> #{LOG_FILE}"
155
- .gsub('_INPUT_', "'#{@input}'")
156
- end
157
-
158
- # Run +ffmpeg+ while printing a progress bar to the terminal.
159
- # If a block is passed, its return value will be attached to the
160
- # progress bar. The current {Ffmpeg} instance will be passed to
161
- # the block.
162
- #
163
- # @return [0]
164
- def run(&block)
165
- File.delete(LOG_FILE) if File.exist?(LOG_FILE)
166
-
167
- FileUtils.mkpath(@output.rpartition('/').first) if output.include?('/')
168
-
169
- @pid = spawn command
170
- @block = block
171
-
172
- sleep 1 until current_time
173
- monitor_progress
174
-
175
- cleanup
176
-
177
- 0
178
- end
179
-
180
- private
181
-
182
- # Display the progress bar while waiting for +ffmpeg+ to finish.
183
- def monitor_progress
184
- until Process.waitpid(@pid, Process::WNOHANG)
185
- print "\r#{progress_bar}"
186
- sleep 1
187
- end
188
-
189
- puts "\r#{progress_bar(@theme[:finish_fg], @duration_ff)}"
190
- rescue Interrupt
191
- puts "\r#{progress_bar(@theme[:cancel_fg])}"
192
- end
193
-
194
- # Display the progress bar.
195
- def progress_bar(time_fg = @theme[:time_fg], time = current_time)
196
- elements = bar_elements(time_fg, time)
197
-
198
- bar =
199
- "#{elements.head}#{elements.full}#{elements.empty}#{elements.tail}" \
200
- "#{elements.head}#{elements.time}#{elements.tail}"
201
-
202
- if @block
203
- block_value = colorize(@block.call(self), @theme[:block_fg])
204
- bar << "#{elements.head}#{block_value}#{elements.tail}"
205
- end
206
-
207
- bar
208
- end
209
-
210
- # Get the current time (in +ffmpeg+ format) from the log file.
211
- # Returns +nil+ if the actual transcoding process has not started yet.
212
- #
213
- # @return [String]
214
- def current_time
215
- return nil unless File.exist?(LOG_FILE)
216
- match = File.read(LOG_FILE).scan(/time=..:..:..\.../).last
217
-
218
- return '00:00:00.00' unless match
219
- match.delete('time=')
220
- end
221
-
222
- # Generate bar elements for a given +ffmpeg+ time.
223
- def bar_elements(time_fg, time)
224
- elements = Struct.new(:head, :tail, :full, :empty, :time)
225
- bars = (@theme[:bars] * parse_ffmpeg_time(time) / @duration).round
226
-
227
- elements.new(
228
- colorize(@theme[:head], @theme[:end_fg]),
229
- colorize(@theme[:tail], @theme[:end_fg]),
230
- colorize(@theme[:full] * bars, @theme[:full_fg]),
231
- colorize(@theme[:empty] * (@theme[:bars] - bars), @theme[:empty_fg]),
232
- colorize(time, time_fg)
233
- )
234
- end
235
-
236
- # Kill +ffmpeg+ if still running, and delete the log file.
237
- def cleanup
238
- Process.detach(@pid)
239
-
240
- ffmpeg_alive =
241
- begin
242
- Process.kill(0, @pid)
243
- true
244
- rescue Errno::ESRCH
245
- false
246
- end
247
-
248
- Process.kill('TERM', @pid) if ffmpeg_alive
249
-
250
- File.delete(LOG_FILE) if File.exist?(LOG_FILE)
251
- end
252
- end
253
- end
10
+ # {include:file:README.rdoc}
11
+ module FFmpegProgress; end
@@ -0,0 +1,177 @@
1
+ module FFmpegProgress
2
+ # The class that does the actual work.
3
+ class FFmpeg
4
+ include Presets
5
+ include Utils
6
+
7
+ # The log file for +ffmpeg+. Progress data will be read from here.
8
+ LOG_FILE = 'ffmpeg.log'
9
+
10
+ # The extension for the default output file.
11
+ DEFAULT_EXT = 'mp4'
12
+
13
+ # The directory for the default output file.
14
+ DEFAULT_DIR = 'converted/'
15
+
16
+ # The input file.
17
+ # @return [String]
18
+ attr_reader :input
19
+
20
+ # The output file. Directories will be auto-created on execution.
21
+ # @return [String]
22
+ attr_accessor :output
23
+
24
+ # Returns the duration (in seconds) of the input file.
25
+ # @return [Integer]
26
+ attr_reader :duration
27
+
28
+ # Returns the duration of the input file as a +ffmpeg+ format string
29
+ # +(HH:MM:SS.ms)+.
30
+ #
31
+ # @return [Integer]
32
+ attr_reader :duration_ff
33
+
34
+ # The options to pass to +ffmpeg+. Any occurrences of the string +\_INPUT\_+
35
+ # will be converted to the value of the {#input} attribute.
36
+ # @return [String]
37
+ attr_accessor :options
38
+
39
+ # The theme for the progress bar. See {Theme::DEFAULT_THEME} for details.
40
+ # @return [Hash]
41
+ attr_accessor :theme
42
+
43
+ # The PID of the last spawned +ffmpeg+ process, or +nil+ if none spawned
44
+ # yet.
45
+ # @return [Integer]
46
+ attr_reader :pid
47
+
48
+ # Creates a new {FFmpeg} instance.
49
+ #
50
+ # @param [String] input_file the source file name.
51
+ # @param [Hash] option_hash
52
+ # @option option_hash [String] :output the output file to write to.
53
+ # @option option_hash [String] :options the options to pass to +ffmpeg+.
54
+ # See {#options} for details and {Presets} for examples.
55
+ # @option option_hash [Hash, Theme] :theme either a theme hash or a {Theme}
56
+ # object.
57
+ # @option option_hash [Boolean] :bell whether to play a bell sound upon
58
+ # finishing the task. (See {Utils} for details.)
59
+ # @return [FFmpeg]
60
+ def initialize(input_file, option_hash = {})
61
+ self.input = input_file
62
+
63
+ @options = option_hash.fetch :options, DEFAULT_OPTIONS
64
+ @theme = Theme.from(option_hash.fetch :theme, Theme.new)
65
+ @bell = option_hash.fetch :bell, false
66
+
67
+ @output = option_hash.fetch :output, @output
68
+ @duration_ff ||= nil
69
+ @duration ||= nil
70
+
71
+ @block = nil
72
+
73
+ @pid = nil
74
+
75
+ @last_bar = nil
76
+ end
77
+
78
+ # Set the input file.
79
+ #
80
+ # @param [String] string
81
+ # @return [String]
82
+ def input=(string)
83
+ @input = string
84
+
85
+ return @input unless @input
86
+
87
+ @duration_ff = `ffmpeg -i \'#{@input}\' 2>&1`.scan(/..:..:..\.../).first
88
+ @duration = parse_ffmpeg_time(@duration_ff)
89
+
90
+ @output ||= "#{DEFAULT_DIR}#{@input.rpartition('.').first}.#{DEFAULT_EXT}"
91
+
92
+ @input
93
+ end
94
+
95
+ # Returns the current +ffmpeg+ command that will be executed by {#run}.
96
+ #
97
+ # @return [String]
98
+ def command
99
+ "ffmpeg -i \'#{@input}\' #{@options} \'#{@output}\' &> #{LOG_FILE}"
100
+ .gsub('_INPUT_', "'#{@input}'")
101
+ end
102
+
103
+ # Run +ffmpeg+ while printing a progress bar to the terminal.
104
+ # If a block is passed, its return value will be attached to the
105
+ # progress bar. The current {FFmpeg} instance will be passed to
106
+ # the block.
107
+ #
108
+ # @return [0]
109
+ def run(&block)
110
+ File.delete(LOG_FILE) if File.exist?(LOG_FILE)
111
+
112
+ FileUtils.mkpath(@output.rpartition('/').first) if output.include?('/')
113
+
114
+ @pid = spawn command
115
+ @block = block
116
+
117
+ sleep 1 until current_time
118
+ monitor_progress
119
+
120
+ cleanup
121
+
122
+ 0
123
+ end
124
+
125
+ private
126
+
127
+ # Display the progress bar while waiting for +ffmpeg+ to finish.
128
+ def monitor_progress
129
+ until Process.waitpid(@pid, Process::WNOHANG)
130
+ print "\r#{progress_bar}"
131
+ sleep 1
132
+ end
133
+
134
+ puts "\r#{progress_bar(@duration_ff, finished: true)}"
135
+ bell if @bell
136
+ rescue Interrupt, IRB::Abort
137
+ puts "\r#{progress_bar(current_time, interrupted: true)}"
138
+ end
139
+
140
+ # Display the progress bar.
141
+ def progress_bar(time = current_time, option_hash = {})
142
+ position = parse_ffmpeg_time(time).to_f / @duration
143
+ option_hash.merge!(block_output: (@block ? @block.call(self) : nil))
144
+
145
+ @theme.bar(position, time, option_hash)
146
+ end
147
+
148
+ # Get the current time (in +ffmpeg+ format) from the log file.
149
+ # Returns +nil+ if the actual transcoding process has not started yet.
150
+ #
151
+ # @return [String]
152
+ def current_time
153
+ return nil unless File.exist?(LOG_FILE)
154
+ match = File.read(LOG_FILE).scan(/time=..:..:..\.../).last
155
+
156
+ return '00:00:00.00' unless match
157
+ match.delete('time=')
158
+ end
159
+
160
+ # Kill +ffmpeg+ if still running, and delete the log file.
161
+ def cleanup
162
+ Process.detach(@pid)
163
+
164
+ ffmpeg_alive =
165
+ begin
166
+ Process.kill(0, @pid)
167
+ true
168
+ rescue Errno::ESRCH
169
+ false
170
+ end
171
+
172
+ Process.kill('TERM', @pid) if ffmpeg_alive
173
+
174
+ File.delete(LOG_FILE) if File.exist?(LOG_FILE)
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,11 @@
1
+ # {include:file:README.rdoc}
2
+ module FFmpegProgress
3
+ # The current software version.
4
+ VERSION = '1.1.0'
5
+
6
+ # The date of the current version.
7
+ DATE = '2014-10-31'
8
+
9
+ # A short description of the software.
10
+ ABOUT = 'A fancy progress bar for ffmpeg.'
11
+ end
@@ -0,0 +1,27 @@
1
+ module FFmpegProgress
2
+ # Contains various examples of +ffmpeg+ options.
3
+ module Presets
4
+ # The default options. Convert the video to x264 and audio to AAC.
5
+ DEFAULT_OPTIONS = '-y -threads 0 -strict -2 -c:a aac -b:a 96k ' \
6
+ '-c:v libx264 -preset fast -tune fastdecode -crf 22 '
7
+
8
+ # Read the default subtitles embedded in an input MKV file and hardcode
9
+ # them into the video. Any embedded fonts must be extracted and installed
10
+ # first to preserve them in the output, for example:
11
+ # mkvextract attachments input.mkv {1..10}
12
+ # cp *.ttf *.ttc *.otf ~/.fonts
13
+ BURN_MKV_SUBS = '-y -threads 0 -strict -2 -sn -c:a copy ' \
14
+ '-c:v libx264 -preset fast -tune fastdecode -crf 22 ' \
15
+ '-vf "subtitles=_INPUT_"'
16
+
17
+ # Like {BURN_MKV_SUBS}, but also downscale the video to 800px width.
18
+ BURN_800 = '-y -threads 0 -strict -2 -sn -c:a aac -b:a 96k ' \
19
+ '-c:v libx264 -preset fast -tune fastdecode -crf 22 ' \
20
+ '-vf "[in]scale=800:-2[tmp];[tmp]subtitles=_INPUT_[out]"'
21
+
22
+ # Scale to 800px width.
23
+ WIDTH_800 = '-y -threads 0 -strict -2 -sn -c:a aac -b:a 96k ' \
24
+ '-c:v libx264 -preset fast -tune fastdecode -crf 22 ' \
25
+ '-vf scale=800:-2'
26
+ end
27
+ end
@@ -0,0 +1,177 @@
1
+ module FFmpegProgress
2
+ # This is the class that handles progress bar themes and converting them into
3
+ # visual elements. {FFmpegProgress} expects a 256-color capable terminal.
4
+ class Theme
5
+ # The default theme.
6
+ DEFAULT_THEME = {
7
+ bars: 63, head_chr: '[', full_chr: '=', empty_chr: '-', tail_chr: ']',
8
+ end_fg: 202, full_fg: 214, empty_fg: 202,
9
+ time_fg: 214, block_fg: 214, finish_fg: 40, cancel_fg: 1
10
+ }
11
+
12
+ # The length of the progress bar.
13
+ #
14
+ # @return [Integer]
15
+ attr_accessor :bars
16
+
17
+ # The string to be used as the start of a section.
18
+ #
19
+ # @return [String]
20
+ attr_accessor :head_chr
21
+
22
+ # The string to be used to draw the filled part of the bar.
23
+ #
24
+ # @return [String]
25
+ attr_accessor :full_chr
26
+
27
+ # The string to be used to draw the remaining part of the bar.
28
+ #
29
+ # @return [String]
30
+ attr_accessor :empty_chr
31
+
32
+ # The string to be used as the end of a section.
33
+ #
34
+ # @return [String]
35
+ attr_accessor :tail_chr
36
+
37
+ # The color of the beginning and end of a section.
38
+ #
39
+ # @return [Integer]
40
+ attr_accessor :end_fg
41
+
42
+ # The color of the filled part of the bar.
43
+ #
44
+ # @return [Integer]
45
+ attr_accessor :full_fg
46
+
47
+ # The color of the remaining part of the bar.
48
+ #
49
+ # @return [Integer]
50
+ attr_accessor :empty_fg
51
+
52
+ # The color of the current time position info.
53
+ #
54
+ # @return [Integer]
55
+ attr_accessor :time_fg
56
+
57
+ # The color of the optional block return value.
58
+ #
59
+ # @return [Integer]
60
+ attr_accessor :block_fg
61
+
62
+ # The color of the time position info on a completed task.
63
+ #
64
+ # @return [Integer]
65
+ attr_accessor :finish_fg
66
+
67
+ # The color of the time position info on an interrupted task.
68
+ #
69
+ # @return [Integer]
70
+ attr_accessor :cancel_fg
71
+
72
+ # Generates a new instance of Theme. Returns the argument if the argument
73
+ # is a Theme.
74
+ #
75
+ # @param [Hash, Theme] object
76
+ # @return [Theme]
77
+ def self.from(object)
78
+ return object if object.is_a? Theme
79
+ return new(object) if object.is_a? Hash
80
+ fail 'Argument must be either Theme or Hash.'
81
+ end
82
+
83
+ # Generates a new instance of Theme from a theme hash.
84
+ #
85
+ # @param [Hash] theme_hash
86
+ # @return [Theme]
87
+ def initialize(theme_hash = DEFAULT_THEME)
88
+ DEFAULT_THEME.merge(theme_hash).each_pair do |key, value|
89
+ method("#{key}=").call(value) if DEFAULT_THEME.key? key
90
+ end
91
+ end
92
+
93
+ # Returns a progress bar given a fractional position, a time string,
94
+ # and an optional block output string.
95
+ #
96
+ # @param [Float] position
97
+ # @param [String] time_string
98
+ # @param [Hash] option_hash
99
+ # @option option_hash [String] :block_output the output from the optional
100
+ # block passed to {FFmpeg#run}, if any.
101
+ # @option option_hash [Boolean] :interrupted has the task been canceled?
102
+ # @option option_hash [Boolean] :finished has the task finished?
103
+ # @return [String]
104
+ def bar(position, time_string = '00:00:00.00', option_hash = {})
105
+ output = "#{head}#{full(position)}#{empty(position)}#{tail}" \
106
+ "#{head}#{time(time_string, option_hash)}#{tail}"
107
+
108
+ if option_hash.fetch :block_output, false
109
+ output << "#{head}#{colorize(option_hash[:block_output], @block_fg)}" \
110
+ "#{tail}"
111
+ end
112
+
113
+ output
114
+ end
115
+
116
+ private
117
+
118
+ # Returns the contents of the time section.
119
+ #
120
+ # @param [String] time_string
121
+ # @param [Hash] option_hash
122
+ # @option option_hash [Boolean] :interrupted has the task been canceled?
123
+ # @option option_hash [Boolean] :finished has the task finished?
124
+ # @return [String]
125
+ def time(time_string, option_hash = {})
126
+ fg_color =
127
+ if option_hash.fetch :interrupted, false
128
+ @cancel_fg
129
+ elsif option_hash.fetch :finished, false
130
+ @finish_fg
131
+ else
132
+ @time_fg
133
+ end
134
+
135
+ colorize(time_string, fg_color)
136
+ end
137
+
138
+ # Returns the filled part of the progress bar given a fractional position.
139
+ #
140
+ # @param [Float] position
141
+ # @return [String]
142
+ def full(position)
143
+ colorize(@full_chr * (position * @bars).round, @full_fg)
144
+ end
145
+
146
+ # Returns the empty part of the progress bar given a fractional position.
147
+ #
148
+ # @param [Float] position
149
+ # @return [String]
150
+ def empty(position)
151
+ colorize(@empty_chr * (@bars - (position * @bars).round), @empty_fg)
152
+ end
153
+
154
+ # Returns a colorized section start element.
155
+ #
156
+ # @return [String]
157
+ def head
158
+ colorize(@head_chr, @end_fg)
159
+ end
160
+
161
+ # Returns a colorized section end element.
162
+ #
163
+ # @return [String]
164
+ def tail
165
+ colorize(@tail_chr, @end_fg)
166
+ end
167
+
168
+ # Colorize a string for the terminal (256-color mode).
169
+ #
170
+ # @param [String] string
171
+ # @param [Integer] color
172
+ # @return [String]
173
+ def colorize(string, color)
174
+ "\x1b[38;5;#{color}m#{string}\x1b[0m"
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,29 @@
1
+ module FFmpegProgress
2
+ # Helper methods.
3
+ module Utils
4
+ # The bell file to be used to signal completion.
5
+ BELL = "#{File.expand_path('../../..', __FILE__)}/" \
6
+ 'assets/LucasGonze_StLouisWaltz.ogg'
7
+
8
+ # Play the bell sound.
9
+ #
10
+ # @return [Integer] the PID of the bell process.
11
+ def bell
12
+ ffplay =
13
+ spawn "ffplay -nodisp -autoexit '#{BELL}' &> /dev/null"
14
+ Process.detach(ffplay)
15
+ ffplay
16
+ end
17
+
18
+ # Parse a time string in the +ffmpeg+ +HH:MM:SS.ms+ format and
19
+ # return seconds.
20
+ #
21
+ # @param [String] time_string
22
+ # @return [Integer]
23
+ def parse_ffmpeg_time(time_string)
24
+ array = time_string.rpartition('.').first.split(':').map(&:to_i)
25
+
26
+ array[0] * 3600 + array[1] * 60 + array[2]
27
+ end
28
+ end
29
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffmpeg_progress
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Winterbraid
@@ -19,7 +19,13 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - LICENSE.txt
21
21
  - README.rdoc
22
+ - assets/LucasGonze_StLouisWaltz.ogg
22
23
  - lib/ffmpeg_progress.rb
24
+ - lib/ffmpeg_progress/ffmpeg.rb
25
+ - lib/ffmpeg_progress/metadata.rb
26
+ - lib/ffmpeg_progress/presets.rb
27
+ - lib/ffmpeg_progress/theme.rb
28
+ - lib/ffmpeg_progress/utils.rb
23
29
  homepage: https://github.com/Winterbraid/ffmpeg_progress
24
30
  licenses:
25
31
  - MIT