#!/usr/bin/env ruby # # # = Jeeves CLI # # == Command Line Interpreter for various Jeeves tasks # # Author:: Robert Sharp # Copyright:: Copyright (c) 2013 Robert Sharp # License:: Open Software Licence v3.0 # # This software is licensed for use under the Open Software Licence v. 3.0 # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php # and in the file copyright.txt. Under the terms of this licence, all derivative works # must themselves be licensed under the Open Software Licence v. 3.0 # # Based on Optparse # require 'rubygems' require 'optparse' require 'jeeves' require 'jeeves/config' require 'jeeves/store' require 'jeeves/partition' require 'jeeves/video' require 'jeckyl/errors' require 'jellog/proxy' require 'jeni/io' require 'colored' include Jeni::IO local_options = Hash.new debug = false verbose = false config_file = nil suppress = true commands = Hash.new # define global options actions = OptionParser.new do |opts| opts.banner = "Usage: jeeves [actions] [options]" opts.separator "" opts.separator "where actions are listed below" opts.separator "and options can be global or specific to actions as shown below" opts.separator "" opts.separator "Actions can be one of the following:" opts.separator "" opts.separator " store - manage where jeeves stores videos" opts.separator " listings - manage programme listings" opts.separator " videos - do things with specific videos" opts.separator "" opts.separator "Global Options" opts.separator "" opts.on('-c', '--config [FILE]', String, 'use the given config file') do |c| config_file = c end opts.separator "" opts.separator "Actions for 'jeeves store':" opts.separator "" opts.separator " init - create a key file to initialise a store" opts.separator " status - list specified stores and their status" opts.separator " clean - clean up the file list" opts.separator "" opts.separator "Options for 'store init' command" opts.separator "" opts.on('-f', '--force', 'force an init regardless of existing keys') do |c| local_options[:force_init] = c end opts.on('-a', '--all', 'clean all files in the list regardless of status') do |c| local_options[:clean_all] = c end opts.on('-d', '--delete', 'delete all possible file list data - use with care') do |c| local_options[:delete] = c end opts.on('-E', '--errors [TYPE]', [:ignore, :warn, :fatal], 'set the way store errors are treated') do |c| local_options[:store_errors] = c end opts.separator "" opts.separator "Actions for 'jeeves listings':" opts.separator "" opts.separator " update - download the lastest listings info and update Jeeves TV" opts.separator " clean - clean out old listings" opts.separator "" opts.separator "Options for 'listings' command" opts.separator "" opts.on('-u', '--update', 'update listings data regardless of when it was last downloaded') do |c| local_options[:update_listings] = c end opts.on('-R', '--reset', 'reset listings data, clearing out all old listings') do |c| local_options[:reset_listings] = c end opts.separator "" opts.separator "General options" opts.separator "" opts.on_tail('-D', '--debug', 'show debug information') do debug = true verbose = false suppress = false end opts.on_tail('-V', '--verbose', 'show verbose information') do verbose = true debug = false suppress = false end opts.on_tail('-h', '--help', 'you are looking at it') do puts opts #show_help(commands) exit 0 end end.permute(ARGV) # start a logger logger = Jellog::ProxyLogger.new("Jeeves", :suppress=>suppress) if debug then logger.log_level = :debug logger.debug "Setting logger to debug" end if verbose then logger.log_level = :verbose logger.verbose "Setting logger to verbose" puts "Running under ruby #{RUBY_VERSION}" end begin options = Jeeves.get_config(config_file) rescue Jeckyl::ConfigFileMissing logger.error "No config file #{config_file ? config_file : '/etc/jerbil/jeeves.rb'}" exit 1 end options = options.merge(local_options) begin raise ArgumentError, "You must specify an action" unless actions.length > 0 case act = actions.shift.to_sym when :store actions.unshift('status') unless actions.length > 0 case act = actions.shift.to_sym when :init # jeeves store init path/to/store # # expect next argument to be a file if actions.length > 0 then new_store = actions.shift else logger.error "need to provide a path to initialise" exit 1 end unless FileTest.exists?(new_store) logger.error "Error: path #{new_store} does not exist" exit 1 end forced = options[:force_init] # fudge due to bug in jeckyl(?) force = options[:force_init] part = Jeeves::Partition.new(new_store) puts "Forcing a new key" if force key = part.create_key(force) puts "# add this line to your config file" puts "add_store :path=>'#{part.path}', :key=>'#{key}'" when :status parts = Array.new devices = Array.new options[:add_partition].each_pair do |path, params| part = Jeeves::Partition.new(path, params) # if mounted = FileTest.exists?(path) ? 'OK' : 'No'then # key_ok = Jeeves::Store.key_ok?(path, params[:key]) ? 'OK' : 'Error' # end if part.ready? then if devices.include?(part.device) then part.duplicate = true else devices << part.device end end parts << part.to_a end Jeeves.tabulate(%w{* 4c 4c 4c 8r 8r}, %w{Path Mnt Rdy Dup Total Free}, parts ) when :list # jeeves store list jestore = Jeeves::Store.new(logger, options) if jestore.files <= 0 then puts "There are no files to show" else entries = Array.new jestore.each_file do |path, params| entries << [path, Jeeves.human_size(params[:space]), params[:date].strftime("%Y-%b-%d %H:%M")] end Jeeves.tabulate(%w{* *r *c}, %w{Path Space Date}, entries) end when :clean jestore = Jeeves::Store.new(logger, options) if local_options[:delete] then # going to get rid of all file lists if ask('Delete all file lists?', :no, 'yn') == :yes then jestore.each_partition do |part| if FileTest.exists?(part.file_list) then puts "Deleting #{part.file_list}" FileUtils.rm_f(part.file_list) end end else puts "OK, no files have been deleted" end exit 0 end # jeeves store clear if jestore.files == 0 then puts "The file list is already empty, nothing to clean" exit 0 end if local_options[:clean_all] then if ask('Clean all files from the list?', :no, 'yn') != :no then jestore.clean(true) puts "The file list is now empty" end else if ask('Clean files from the list?', :no, 'yn') != :no then jestore.clean puts "Cleaned file list" end end else #case raise ArgumentError, "Unknown action for store: #{act}" end # case store actions # # listings actions # when :listings # update actions.unshift('status') unless actions.length > 0 case act = actions.shift.to_sym when :update begin Jeeves.update_listings(options) rescue Jeeves::ListingError => e puts "Error while updating listings: #{e}" exit 1 end else raise ArgumentError, "Unknown action for listings: #{act}" end # case # # video actions # when :videos # load, cleanup etc actions.unshift('status') unless actions.length > 0 case act = actions.shift.to_sym when :wrangle begin unless file = actions.shift logger.error "Need to provide a file to wrangle" exit 1 end unless FileTest.exists?(file) logger.error "File does not exist: #{file}" exit 1 end unless pid = actions.shift logger.error "Need to provide a programme id to wrangle" exit 1 end vid = Jeeves::Video.new(file, logger, options) vid.tag_from_prog_id(pid) response = vid.upload_to_jeeves vid.clean_up if response.to_i == 0 then puts "Jeeves OK" else logger.error "Jeeves returned unexpected response: #{response}" end rescue Jeeves::JeevesError => e puts "Error while wrangling video: #{e}" end when :load begin unless file = actions.shift logger.error "Need to provide a file to load" exit 1 end unless FileTest.exists?(file) logger.error "File does not exist: #{file}" exit 1 end vid = Jeeves::Video.new(file, logger, options) response = vid.upload_to_jeeves vid.clean_up if response.to_i == 0 then puts "Jeeves OK" else logger.error "Jeeves returned unexpected response: #{response}" end rescue Jeeves::JeevesError => e puts "Error while wrangling video: #{e}" end when :show unless file = actions.shift logger.error "Need to provide a file to load" exit 1 end unless FileTest.exists?(file) logger.error "File does not exist: #{file}" exit 1 end vid = Jeeves::Video.new(file, logger, options) tags = vid.get_tags tags.each_tag do |key, value| puts "#{key}: #{value}" end end # case for video subcommands else puts "Error: Unknown action #{act}" exit 1 end # first level action rescue ArgumentError => err logger.error err if debug then err.backtrace.each do |bt| puts bt end end rescue Jeeves::JeevesError => err logger.error "#{err}" if debug then err.backtrace.each do |bt| puts bt end end ensure puts options if debug end