#!/usr/bin/env ruby # == Synopsis # # Translate a 2D track file from a single format to many others # # == Usage # # tracksperanto -w 1920 -h 1080 /Films/Blockbuster/Shots/001/script.shk --only flamestabilizer # # == Author # Julik require File.dirname(__FILE__) + '/../lib/tracksperanto' unless defined?(Tracksperanto) require 'optparse' require 'rubygems' require 'progressbar' require "update_hints" def disclaimer "Please consider a small donation to keep Tracksperanto going: http://guerilla-di.org/source-and-license/\n"+ "For information and support please contact info#{64.chr}guerilla-di.org" end options = {} $middlewares = [] writer_class_name = nil readers = Tracksperanto.importer_names writers = Tracksperanto.exporter_names op = OptionParser.new def mw(*name_option_and_default_value) Proc.new do |value| name, option, default_value = name_option_and_default_value v = value.nil? ? default_value : value mw_tuple = [name] mw_tuple.push({option => v}) if option $middlewares.push(mw_tuple) end end def mwd(name) Tracksperanto.get_middleware(name).action_description end def list_exporters(dest = $stderr) dest.puts "The following export modules are available:" Tracksperanto.exporters.each do | exporter | dest.puts "\t#{exporter.const_name.downcase.inspect} - #{exporter.human_name}" end end def list_importers(dest = $stderr) dest.puts "The following import modules are available:" Tracksperanto.importers.each do | importer | dest.puts "\t#{importer.const_name.downcase.inspect} - #{importer.human_name}" end end class CodeLoaded < RuntimeError; end op.banner = "Usage: tracksperanto -f ShakeScript -w 1920 -h 1080 /Films/Blockbuster/Shots/001/script.shk" op.on("--code PATH_TO_SCRIPT", String, "Load custom Ruby code into tracksperanto") do |c| unless $code require(c) raise CodeLoaded end end op.on(" -f", "--from TRANSLATOR", String, "Use the specific import translator") { |f| options[:importer] = f } op.on(" -w", "--width WIDTH_IN_PIXELS", Integer, "Absolute input comp width in pixels (will try to autodetect)") { |w| options[:width] = w } op.on(" -h", "--height HEIGHT_IN_PIXELS", Integer, "Absolute input comp height in pixels (will try to autodetect)") {|w| options[:height] = w } op.on(" -o", "--only EXPORTER_NAME", String, "Only export the selected format, format must be one of #{writers.join(", ")}") { |f| writer_class_name = f } op.on(" -xs", "--xscale X_SCALING_FACTOR", Float, mwd("Scaler"), &mw("Scaler", :x_factor)) op.on(" -pad", "--pad PAD_FRACTION_VALUES_COMMA_SEPARATED", String, mwd("Pad")) do | pads| left, right, top, bottom = pads.split(",").map{|e| e.to_f } $middlewares.push(["Pad", {"left_pad" => left, "right_pad"=> right, "top_pad" => top, "bottom_pad" => bottom}]) end op.on(" -crop", "--crop CROP_VALUES_COMMA_SEPARATED", String, mwd("Crop")) do | pads| left, right, top, bottom = pads.split(",").map{|e| e.to_i } $middlewares.push(["Crop", {"left" => left, "right"=> right, "top" => top, "bottom" => bottom}]) end op.on(" -ys", "--yscale Y_SCALING_FACTOR", Float, mwd("Scaler"), &mw("Scaler", :y_factor)) op.on(" -t", "--trim", Float, mwd("StartTrim"), &mw("StartTrim")) # Before slip! op.on(" -s", "--slip FRAMES", Integer, mwd("Slipper"), &mw("Slipper", :slip)) op.on(" -g", "--golden", mwd("Golden"), &mw("Golden")) op.on(" -m", "--min-length LENGTH_IN_FRAMES", Integer, mwd("LengthCutoff"), &mw("LengthCutoff", :min_length)) op.on(" -p", "--prefix PREFIX", String, mwd("Prefix"), &mw("Prefix", :prefix)) op.on("--lerp", mwd("Lerp"), &mw("Lerp")) op.on("--flip", mwd("Flip"), &mw("Flip")) op.on("--flop", mwd("Flop"), &mw("Flop")) # TODO - multiparameters op.on(" -rx", "--reformat-x NEW_PIX_WIDTH", Integer, "Reformat the comp to this width and scale all tracks to it", &mw("Reformat", :width)) op.on(" -ry", "--reformat-y NEW_PIX_HEIGHT", Integer, "Reformat the comp to this height and scale all tracks to it", &mw("Reformat", :height)) op.on(" -xm", "--xshift X_IN_PIXELS", Float, "Move the points left or right", &mw("Shift", :x_shift)) op.on(" -ym", "--yshift Y_IN_PIXELS", Float, "Move the points up or down", &mw("Shift", :y_shift)) op.on("--list-exporters", "Show available export modules") do list_exporters; exit(0) end op.on("--list-importers", "Show available import modules") do list_importers; exit(0) end op.on("--version", "Show the version and exit") do |v| puts "Tracksperanto v.#{Tracksperanto::VERSION} running on Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}" puts "Copyright 2008-#{Time.now.year} by Guerilla-DI (Julik Tarkhanov and contributors)" puts disclaimer UpdateHints.version_check("tracksperanto", Tracksperanto::VERSION, STDOUT) exit(0) end op.on("--trace", "Output LOTS of info during conversion") { $debug = true } begin op.parse! rescue CodeLoaded $code = true retry end input_file = ARGV.pop fail "No input file provided - should be the last argument. Also use the --help option." unless input_file fail "Input file #{input_file} does not exist" unless File.exist?(input_file) pbar = ProgressBar.new("Converting", 100, $stdout) progress = lambda do |percent,message| pbar.set(percent) if $debug STDOUT.puts("[%d -> %s]" % [percent, message]) STDOUT.flush end end begin pipe = Tracksperanto::Pipeline::Base.new(:progress_block => progress, :middleware_tuples => $middlewares) pipe.exporters = [Tracksperanto.get_exporter(writer_class_name)] if writer_class_name pipe.run(input_file, options) pbar.finish $stdout.puts "Found and converted %d trackers with %d keyframes." % [pipe.converted_points, pipe.converted_keyframes] rescue Tracksperanto::UnknownExporterError => damn $stderr.puts damn.message list_exporters($stderr) fail "Unknown exporter" rescue Tracksperanto::UnknownImporterError => damn $stderr.puts damn.message list_importers($stderr) fail "Unknown importer" rescue Tracksperanto::UnknownMiddlewareError => damn $stderr.puts damn.message fail "This is a bug, please report it" rescue Exception => damn fail damn.message end puts disclaimer UpdateHints.version_check("tracksperanto", Tracksperanto::VERSION, STDOUT)