lib/ffmprb/util.rb in ffmprb-0.9.6 vs lib/ffmprb/util.rb in ffmprb-0.10.0
- old
+ new
@@ -1,53 +1,53 @@
-# require 'ffmprb/util/synchro'
-require 'ffmprb/util/thread'
-require 'ffmprb/util/threaded_io_buffer'
-
require 'open3'
module Ffmprb
+ class Error < StandardError; end
+
module Util
class TimeLimitError < Error; end
class << self
- attr_accessor :ffmpeg_cmd, :ffprobe_cmd
+ attr_accessor :ffmpeg_cmd, :ffmpeg_inputs_max, :ffprobe_cmd
attr_accessor :cmd_timeout
def ffprobe(*args, limit: nil, timeout: cmd_timeout)
sh *ffprobe_cmd, *args, limit: limit, timeout: timeout
end
- def ffmpeg(*args, limit: nil, timeout: cmd_timeout, ignore_broken_pipe: false)
- args = ['-loglevel', 'debug'] + args if Ffmprb.debug
- sh *ffmpeg_cmd, *args, output: :stderr, limit: limit, timeout: timeout, ignore_broken_pipe: ignore_broken_pipe
+ def ffmpeg(*args, limit: nil, timeout: cmd_timeout, ignore_broken_pipes: true)
+ args = ['-loglevel', 'debug'] + args if Ffmprb.ffmpeg_debug
+ sh *ffmpeg_cmd, *args, output: :stderr, limit: limit, timeout: timeout, ignore_broken_pipes: ignore_broken_pipes
end
- def sh(*cmd, output: :stdout, log: :stderr, limit: nil, timeout: cmd_timeout, ignore_broken_pipe: false)
+ def sh(*cmd, input: nil, output: :stdout, limit: nil, timeout: cmd_timeout, ignore_broken_pipes: false)
cmd = cmd.map &:to_s unless cmd.size == 1
- cmd_str = cmd.size != 1 ? cmd.map{|c| "\"#{c}\""}.join(' ') : cmd.first
+ cmd_str = cmd.size != 1 ? cmd.map{|c| sh_escape c}.join(' ') : cmd.first
timeout = [timeout, limit].compact.min
thr = Thread.new "`#{cmd_str}`" do
Ffmprb.logger.info "Popening `#{cmd_str}`..."
Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thr|
begin
+ stdin.write input if input
stdin.close
- log_cmd = cmd.first.upcase if log
- stdout_r = Reader.new(stdout, output == :stdout, log == :stdout && log_cmd)
- stderr_r = Reader.new(stderr, true, log == :stderr && log_cmd)
+ log_cmd = cmd.first.upcase
+ stdout_r = Reader.new(stdout, store: output == :stdout, log_with: log_cmd)
+ stderr_r = Reader.new(stderr, store: true, log_with: log_cmd, log_as: output == :stderr && Logger::DEBUG || Logger::INFO)
Thread.timeout_or_live(limit, log: "while waiting for `#{cmd_str}`", timeout: timeout) do |time|
value = wait_thr.value
status = value.exitstatus # NOTE blocking
if status != 0
- if ignore_broken_pipe && value.signaled? && value.termsig == Signal.list['PIPE']
- Ffmprb.logger.debug "Ignoring broken pipe: #{cmd_str}"
+ if ignore_broken_pipes && value.signaled? && value.termsig == Signal.list['PIPE']
+ Ffmprb.logger.info "Ignoring broken pipe: #{cmd_str}"
else
- fail Error, "#{cmd_str} (#{status || "sig##{value.termsig}"}):\n#{stderr_r.read}"
+ status ||= "sig##{value.termsig}"
+ fail Error, "#{cmd_str} (#{status}):\n#{stderr_r.read}"
end
end
end
Ffmprb.logger.debug "FINISHED: #{cmd_str}"
@@ -63,10 +63,19 @@
thr.value
end
protected
+ # NOTE a best guess kinda method
+ def sh_escape(str)
+ if str !~ /^[a-z0-9\/.:_-]*$/i && str !~ /"/
+ "\"#{str}\""
+ else
+ str
+ end
+ end
+
def process_dead!(wait_thr, cmd_str, limit)
grace = limit ? limit/4 : 1
return unless wait_thr.alive?
# NOTE a simplistic attempt to gracefully terminate a child process
@@ -95,17 +104,17 @@
end
class Reader < Thread
- def initialize(input, store=false, log=nil)
+ def initialize(input, store: false, log_with: nil, log_as: Logger::DEBUG)
@output = ''
@queue = Queue.new
super "reader" do
begin
while s = input.gets
- Ffmprb.logger.debug "#{log}: #{s.chomp}" if log
+ Ffmprb.logger.log log_as, "#{log_with}: #{s.chomp}" if log_with
@output << s if store
end
@queue.enq @output
rescue Exception
@queue.enq Error.new("Exception in a reader thread")
@@ -127,5 +136,10 @@
end
end
end
+
+# require 'ffmprb/util/synchro'
+require_relative 'util/proc_vis'
+require_relative 'util/thread'
+require_relative 'util/threaded_io_buffer'