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'