class Vollbremsung::Brake

Public Class Methods

new() click to toggle source
# File lib/vollbremsung.rb, line 29
def initialize
  @logger = Logger.new STDOUT
  @logger.level = Logger::INFO
  #@logger.datetime_format = '%Y-%m-%d %H:%M:%S '
  @logger.formatter = proc do |severity, datetime, progname, msg|
    date_format = datetime.strftime("%Y-%m-%d %H:%M:%S")
    if severity == "INFO" or severity == "WARN"
        "[#{date_format}] #{severity}  : #{msg}\n"
    else        
        "[#{date_format}] #{severity} : #{msg}\n"
    end
  end # proc do
end

Public Instance Methods

process(targets, options) click to toggle source
# File lib/vollbremsung.rb, line 196
def process(targets, options)

  matches = match_files(ARGV, options[:match_extensions], options[:recursive])

  if options[:list_only]
    exit 0
  end

  if matches.empty?
    @logger.info "no files found to process"
    exit 0
  else

    matches.each do |infile, target_dir|

      metadata = ffprobe(infile)
      if metadata.nil?
        @logger.error "could not retrieve metadata -- skipping file"
        next
      end

      astreams, sstreams = parse_metadata(metadata)
      outfile, infile_relative_path = full_outpath(infile, target_dir, options[:extension])

      @logger.info "processing: #{infile_relative_path}"

      success = run_handbrake(infile, outfile, astreams, sstreams, options)

      if success
        @logger.info "compression ratio: " + ratio(infile, outfile)

        if options[:title]
          write_mp4_title(infile, outfile)
        end # if options[:title]

        if options[:move]
          @logger.info "moving source file to *.old"
          File.rename(infile, "#{infile}.old") rescue @logger.error "could not rename source file"
        elsif options[:delete]
          @logger.info "deleting source file"
          File.delete(infile) rescue @logger.error "could not delete source file"
        end

      end # if success
    end # target_files.each

    @logger.info "🚗  finally came to a halt"
  end # items.empty?
end

Private Instance Methods

escape_glob(s) click to toggle source

square brackets have a special meaning in the context of shell globbing –> escape them in order to find files in directories with [, ], {, } symbols in their path

# File lib/vollbremsung.rb, line 47
def escape_glob(s)
  s.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x }
end
ffprobe(file) click to toggle source
# File lib/vollbremsung.rb, line 52
def ffprobe(file)
  stdout,stderr,status = Open3.capture3("ffprobe #{Vollbremsung::FFPROBE_OPTIONS} \"#{file}\"")
  if status.success?
    return JSON.parse(stdout)
  else
    STDERR.puts stderr
    return nil
  end
end
full_outpath(infile, target_dir, extension) click to toggle source
# File lib/vollbremsung.rb, line 114
def full_outpath(infile, target_dir, extension)
  infile_basename = File.basename(infile)
  infile_basename_noext = File.basename(infile, File.extname(infile)) # without ext
  infile_dirname = File.dirname(infile)
  infile_path_noext = File.join(infile_dirname, infile_basename_noext)
  infile_relative_path = #File.directory?(TARGET_PATH) ? infile[TARGET_PATH.length+1..-1] : File.basename(TARGET_PATH)
    if File.directory?(target_dir)
      File.absolute_path(infile)[File.absolute_path(target_dir).length+1..-1]
    else
      File.basename(target_dir)
    end

  outfile = "#{infile_path_noext}.#{extension}"

  return outfile, infile_relative_path
end
match_files(targets, match_extensions, recursive) click to toggle source
# File lib/vollbremsung.rb, line 63
def match_files(targets, match_extensions, recursive)
  scope = recursive ? "/**/*" : "/*"
  matches = []
  targets.each do |target|

    if File.directory?(target)

      @logger.info "probing for target files in #{File.absolute_path(target) + scope}"
      @logger.info "files found:"

      Dir[escape_glob(File.absolute_path(target)) + scope].sort.each do |file|
        unless File.directory?(file)
          if match_extensions.include?(File.extname(file).downcase[1..-1])
            puts "* " + File.absolute_path(file)[File.absolute_path(target).length+1..-1]
            matches << [file,target] # file and provided target_dir
          end
        end
      end

    else
      puts "* " + target
      matches << [File.absolute_path(target),File.absolute_path(target)]
    end

  end

  return matches
end
parse_metadata(metadata) click to toggle source
# File lib/vollbremsung.rb, line 93
def parse_metadata(metadata)
  astreams = Vollbremsung::StreamDescr.new
  sstreams = Vollbremsung::StreamDescr.new

  metadata['streams'].each do |stream|
    case stream['codec_type']
    when 'audio'
      astreams.count += 1
      astreams.names << stream['tags']['title'] unless stream['tags'].nil? || stream['tags']['title'].nil?
    when 'subtitle'
      sstreams.count += 1
      sstreams.names << stream['tags']['title'] unless stream['tags'].nil? || stream['tags']['title'].nil?
    else
      # this is attachment stuff, like typefonts --> ignore
    end
  end

  return astreams, sstreams
end
ratio(infile, outfile) click to toggle source
# File lib/vollbremsung.rb, line 187
def ratio(infile, outfile)
  begin
    return "%.2f" % (File.size(outfile).to_f / File.size(infile).to_f)
  rescue
    return "UNAVAILABLE"
  end
end
run_handbrake(infile, outfile, astreams, sstreams, options) click to toggle source
# File lib/vollbremsung.rb, line 132
def run_handbrake(infile, outfile, astreams, sstreams, options)

  handbrake_cmd = 
    %{ #{HANDBRAKE_CLI} \
      --encoder #{options[:encoder]} \
      --encoder-preset #{options[:preset]} \
      --quality #{options[:quality]} \
      --aencoder faac \
      --audio-copy-mask aac \
      --audio-fallback ffac3 \
      --loose-anamorphic --modulus 2 \
      --audio #{(1..astreams.count).to_a.join(',')} \
      --aname #{astreams.names.join(',')} \
      --subtitle #{(1..sstreams.count).to_a.join(',')} \
      -i \"#{infile}\" -o \"#{outfile}\" 2>&1 
    }
  #@logger.info "running handbrake cmd: #{handbrake_cmd}"
  %x( #{handbrake_cmd} )

  if $?.exitstatus == 0
    @logger.info "encoding done"
    return true
  else
    @logger.error "Handbrake exited with error code #{$?.exitstatus}"
    return false
  end 
end
write_mp4_title(infile, outfile) click to toggle source
# File lib/vollbremsung.rb, line 161
def write_mp4_title(infile, outfile)

  @logger.info "setting MP4 title"

  infile_noext = File.join( File.dirname(infile), File.basename(infile,File.extname(infile)))
  tmpfile = infile_noext + ".tmp.mp4"

  # TODO check if ffmpeg does exists (should if ffprobe is availabe, but just to make sure)

  %x( ffmpeg -i \"#{outfile}\" -metadata title=\"#{infile_basename_noext}\" #{Vollbremsung::FFMPEG_OPTIONS} \"#{tmpfile}\" 2>&1 )

  # if successful, either delete old file and replace with new, or delete the broken tempfile if it exists
  if $?.exitstatus == 0
    begin
      File.delete outfile
      File.rename tmpfile, outfile
    rescue
      @logger.error "moving #{tmpfile} to #{outfile}"
    end
  else
    @logger.error "MP4 title could not be changed"
    File.delete tmpfile
  end
end