#!/usr/bin/env ruby # Rubyripper - A secure ripper for Linux/BSD/OSX # Copyright (C) 2007 Bouke Woudstra (rubyripperdev@gmail.com) # # This file is part of Rubyripper. Rubyripper is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see RUBYDIR=[ENV['PWD'], File.dirname(__FILE__), "/usr/local/lib/ruby/site_ruby/1.8"] found_rrlib = false RUBYDIR.each do |dir| if File.exists?(file = File.join(dir, '/rr_lib.rb')) require file; found_rrlib = true ; break end end if found_rrlib == false puts "The main program logic file 'rr_lib.rb' can't be found!" exit() end require 'optparse' require 'ostruct' class Gui_CLI def initialize() parse_options() @ripping_log = "" @ripping_progress = 0.0 @encoding_progress = 0.0 @finished = false @settings = $rr_defaultSettings if File.exist?(File.expand_path(@options.file)) load_configfile() if @options.configure: edit_settings() end else edit_settings() # Always set up settings if no config file exists. end get_cd_info() end def parse_options @options = OpenStruct.new @options.file = "~/.rubyripper/settings" @options.help = false @options.verbose = false @options.configure = false @options.all = false @options.help = false opts = OptionParser.new(banner = nil, width = 20, indent = ' ' * 2) do |opts| opts.on("-V", "--version", _("Show current version of rubyripper.")) do puts "Rubyripper version #{$rr_version}." exit() end opts.on("-f", "--file ", _("Load configuration settings from file .")) do |f| @options.file = f end opts.on("-v", "--verbose", _("Display verbose output.")) do |v| @options.verbose = v end opts.on("-c", "--configure", _("Change configuration settings.")) do |c| @options.configure = c end opts.on("-a", "--all", _("Rip all tracks. Skip any questions about track selection.")) do |a| @options.all = a end opts.on_tail("-h", "--help", _("Show this usage statement.")) do |h| puts opts @options.help = h end end begin opts.parse!(ARGV) rescue Exception => e puts e, "", opts exit end if @options.help: exit end puts _("Verbose output specified.") if @options.verbose puts _("Configure option specified.") if @options.configure puts _("Rip all tracks specified.") if @options.all puts _("Use config file ") + @options.file if @options.file end def show_settings # simple overview of current settings, with numbers so a user can choose what to change puts _("*** RUBYRIPPER SETTINGS ***") puts "" puts _("** ENCODING **") puts _("1) flac [%s] , settings: %s") % [@settings['flac'] ? '*' : ' ', @settings['flacsettings']] puts _("2) vorbis [%s] , settings: %s") % [@settings['vorbis'] ? '*' : ' ', @settings['vorbissettings']] puts _("3) mp3 [%s] , settings: %s") % [@settings['mp3'] ? '*' : ' ', @settings['mp3settings']] puts _("4) wav [%s]") % [@settings['wav'] ? '*' : ' '] puts _("5) other [%s], settings: %s") % [@settings['othersettings'], @settings['other'] ? '*' : ' '] puts _("6) playlist support [%s]") % [@settings['playlist'] ? '*' : ' '] puts _("7) encode while ripping [%s]") % [@settings['maxThreads']] puts "" puts _("** RIPPING **") puts _("8) cdrom : %s with offset %s") % [@settings['cdrom'], @settings['offset']] puts _("9) passing extra cdparanoia parameters : %s") % [@settings['rippersettings'] != '' ? @settings['rippersettings'] : 'none'] puts _("10) match all chunks : %s") % [@settings['req_matches_all']] puts _("11) match erroneous chunks : %s") % [@settings['req_matches_errors']] puts _("12) maximum trials : %s") % [@settings['max_tries'] == 0 ? "no maximum" : @settings['max_tries']] puts _("13) eject disc after ripping [%s]") % [@settings['eject'] ? '*' : ' '] puts "" puts _("** NAMING SCHEME **") puts _("14) base directory : %s") % [@settings['basedir']] puts _("15) standard scheme: %s") % [@settings['naming_normal']] puts _("16) various artist scheme: %s") % [@settings['naming_various']] puts "" puts _("** FREEDB **") puts _("17) fetch cd info with freedb [%s]") % [@settings['freedb'] ? '*' : ' '] puts _("18) always use first hit [%s]") % [@settings['first_hit'] ? '*' : ' '] puts _("19) site = %s") % [@settings['site']] puts _("20) username = %s") % [@settings['username']] puts _("21) hostname = %s") % [@settings['hostname']] puts _("22) edit freedb data before ripping [%s]") % [@settings['edit'] ? '*' : ' '] puts "" puts _("88) config file = %s") % [@options.file] puts _("99) Don't change any setting") puts "" end def edit_settings # depending on the input of the user as asked in show_settings(), change the setting show_settings() while (number = get_answer(_("Please type the number of the setting you wish to change : "), "number", 99)) != 99 if number == 1 : set_codec('flac') elsif number == 2 : set_codec('vorbis') elsif number == 3 : set_codec('mp3') elsif number == 4 : if @settings['wav'] : puts _("wav disabled") ; @settings['wav'] = false else puts _("wav enabled") ; @settings['wav'] = true end elsif number == 5 : set_codec('other') elsif number == 6 : if @settings['playlist'] : puts _("playlist disabled") ; @settings['playlist'] = false else puts _("playlist enabled") ; @settings['playlist'] = true end elsif number == 7 : @settings['maxThreads'] = get_answer(_("How many encoding threads would you like? : "), "number", 1) elsif number == 8 : set_cdrom() elsif number == 9 : @settings['rippersettings'] = get_answer(_("Which options to pass to cdparanoia? : "), "open", "") elsif number == 10 : @settings['req_matches_all'] = get_answer(_("How many times must all chunks be matched? : "), "number", 2) elsif number == 11 : @settings['req_matches_errors'] = get_answer(_("How many times must erroneous chunks be matched? : "), "number", 3) elsif number == 12 : @settings['max_tries'] = get_answer(_("What should be the maximum number of tries? : "), "number", 5) elsif number == 13 : if @settings['eject'] : puts _("eject disabled") ; @settings['eject'] = false else puts _("eject enabled") ; @settings['eject'] = true end elsif number == 14 : @settings['basedir'] = get_answer(_("Please enter your directory for your encodings: "), "open", "") elsif number == 15 : setdir('normal') elsif number == 16 : setdir('various') elsif number == 17 : if @settings['freedb'] : puts _("freedb disabled") ; @settings['freedb'] = false else puts _("freedb enabled") ; @settings['freedb'] = true end elsif number == 18 : if @settings['first_hit'] : puts _("first hit disabled") ; @settings['first_hit'] = false else puts _("first hit enabled") ; @settings['first_hit'] = true end elsif number == 19 : @settings['site'] = get_answer(_("Freedb mirror: "), "open", "freedb.org") elsif number == 20 : @settings['username'] = get_answer(_("Username : "), "open", "anonymous") elsif number == 21 : @settings['hostname'] = get_answer(_("Hostname : "), "open", "my_secret.com") elsif number == 22 : if @settings['edit'] : puts _("freedb editing disabled") ; @settings['edit'] = false else puts _("freedb editing enabled") ; @settings['edit'] = true end elsif number == 88 : @options.file = get_answer(_("Config file : "), "open", @options.file) else puts _("Number %s is not an option!\nPlease try again.") % [number] end puts "" show_settings() end save_configfile() unless get_answer(_("Do you want to continue with the program now? (y/n) : "), "yes", _("y")) : exit() end end def set_codec(codec) # little help function for edit_settings @settings[codec] = get_answer("Do you want to enable #{codec} encoding? (y/n) : ", "yes", _("y")) if @settings[codec] if get_answer(_("Do you want to change the encoding parameters for %s? (y/n) : ") % [codec], "yes", _("n")) if codec == 'other' : puts _("%a = artist, %b = album, %g = genre, %y = year, %t = trackname, %n = tracknumber, %i = inputfile, %o = outputfile (don't forget extension)") end @settings[codec + 'settings'] = get_answer(_("Encoding paramaters for %s : ") % [codec], "open", "") end end end def set_cdrom # little help function for edit_settings @settings['cdrom'] = get_answer(_("Cdrom device : "), "open", cdrom_drive()) @settings['offset'] = get_answer(_("Offset for drive : "), "number", 0) end def setdir(variable) if variable == 'normal' puts _("\nCurrent naming scheme: %s") % [@settings['naming_normal']] puts get_example_filename_normal(@settings['basedir'], @settings['naming_normal']) else puts _("\nCurrent naming scheme: %s") % [@settings['naming_various']] puts get_example_filename_various(@settings['basedir'], @settings['naming_various']) end puts _("\n%a = Artist \n%b = Album\n%g = Genre\n%y = Year\n%f = Codec\n%n = Tracknumber\n%t = Trackname\n%va = Various Artist\n") answer = get_answer(_("New %s naming scheme (q to quit) : ") % [variable], "open", "%f/%a (%y) %b/%n - %t") if answer !=_('q') if variable == 'normal' puts _("An example filename is now:\n\n\t%s") % [get_example_filename_normal(@settings['basedir'], answer)] @settings['naming_normal'] = answer else puts _("An example filename is now:\n\n\t%s") % [get_example_filename_various(@settings['basedir'], answer)] @settings['naming_various'] = answer end end end def load_configfile # load config file if File.exists?(File.expand_path(@options.file)) file = File.new(File.expand_path(@options.file),'r') while line = file.gets key, value = line.split('=', 2) value.rstrip! # remove the trailing newline character if value == "false" : value = false # replace the string with a bool elsif value == "true" : value = true # replace the string with a bool elsif value == "''" : value = '' #replace a string that contains two quotes with an empty string elsif value[0,1] == "#" : next #name of previous instances should not be loaded elsif value.to_i > 0 || value == '0' : value = value.to_i end @settings[key] = value end file.close() end end def save_configfile # save config file if not File.directory?(dirname = File.join(ENV['HOME'], '.rubyripper')) : Dir.mkdir(dirname) end file = File.new(File.expand_path(@options.file), 'w') @settings.each do |key, value| file.puts "#{key}=#{value}" end file.close() end def get_answer(question, answer, default) succes = false while !succes if answer == "yes" answers = [_("yes"), _("y"), _("no"), _("n")] STDOUT.print(question + " [#{default}] ") input = STDIN.gets.strip if input == '' : input = default end if answers.include?(input) if input == _('y') || input == _("yes") : return true else return false end else puts _("Please answer yes or no") end elsif answer == "open" STDOUT.print(question + " [#{default}] ") input = STDIN.gets.strip if input == '' : return default else return input end elsif answer == "number" STDOUT.print(question + " [#{default}] ") input = STDIN.gets.strip.to_i #convert the answer to an integer value, if input is a text it will be 0. if input == 0 : return default else return input end else puts _("We should never get here") ; puts _("answer = %s, question = %s (error)") % [answer, question] end end end def get_cd_info @settings['cd'] = Disc.new(@settings['cdrom'], @settings['freedb'], self, @settings['verbose']) # Analyze the TOC of disc in drive if @settings['cd'].audiotracks != 0 # a disc is found puts _("Audio-disc found, number of tracks : %s, total playlength : %s") % [@settings['cd'].audiotracks, @settings['cd'].cd_playtime] if @settings['freedb'] #freedb enabled? puts _("Fetching freedb info...") @settings['cd'].freedb(@settings, !@settings['first_hit']) else freedb_finished() end else puts _("No audio-disc found !"); edit_settings() end end def freedb_finished #got the signal that all the metadata info is ready for processing if @settings['edit'] : edit_freedb() else show_freedb() end tracklist() # Which tracks should be ripped? @rubyripper = Rubyripper.new(@settings, self) # starts some check if the settings are sane puts "Now here, class = #{@rubyripper.class}" if @rubyripper.settings_ok : start() end end def choose_freedb(choices) #callback function initiated by rr_lib.rb via our update function puts _("Freedb reported multiple possibilities.") choices.each_index{|index| puts "#{index + 1}) #{choices[index]}"} choice = get_answer(_("Please type the number of the one you prefer? : "), "number", 1) @settings['cd'].md.freedbChoice(choice - 1) end def show_freedb(edit = false) # leave the letters and numbers away if edit mode == false puts "" puts _("FREEDB INFO") puts _("#{"a) " if edit}Artist : %s") % [@settings['cd'].artist] puts _("#{"b) " if edit}Album : %s") % [@settings['cd'].album] puts _("#{"c) " if edit}Genre : %s") % [@settings['cd'].genre] puts _("#{"d) " if edit}Year : %s") % [@settings['cd'].year] puts "" @settings['cd'].audiotracks.times do |index| puts _("%sTrack %s : %s") % [if edit : "#{index + 1}) " end, index + 1, @settings['cd'].tracklist[index]] end puts "" end def edit_freedb show_freedb(edit = true) puts _("99) Don't change any setting") puts "" while (answer = get_answer(_("Please type the letter or number of the setting you wish to change : "), "open", "99")) != "99" if answer == "a" : @settings['cd'].artist = get_answer(_("Artist : "), "open", @settings['cd'].artist) elsif answer == "b" : @settings['cd'].album = get_answer(_("Album : "), "open", @settings['cd'].album) elsif answer == "c" : @settings['cd'].genre = get_answer(_("Genre : "), "open", @settings['cd'].genre) elsif answer == "d" : @settings['cd'].year = get_answer(_("Year : "), "open", @settings['cd'].year) else if answer.to_i > 0 && answer.to_i <= @settings['cd'].audiotracks #Check if tracknumber is in valid range @settings['cd'].tracklist[answer.to_i - 1] = get_answer("Track #{answer.to_i} : ", "open", @settings['cd'].tracklist[answer.to_i - 1]) else puts _("This is not a valid number. Try again") end end show_freedb(edit = true) end end def dir_exists puts _("The output directory already exists. What would you like to do?") puts "" puts _("1) Auto rename the output directory") puts _("2) Overwrite the existing directory") puts _("3) Cancel the rip") puts "" answer = get_answer(_("Please enter the number of your choice: "), "number", 1) if answer == 1: @rubyripper.postfix_dir() ; start() elsif answer == 2: @rubyripper.overwrite_dir() ; start() else puts _("Rip is canceled, exiting...") ; exit() end end def start @rubyripper.start_rip() while @finished == false sleep(1) #loop until the finished signal is given. This is not ideal, but apparently the program gets terminated otherwise. end end def update(modus, value=false) if modus == "ripping_progress" progress = "%.3g" % (value * 100) puts "Ripping progress (#{progress} %)" elsif modus == "encoding_progress" progress = "%.3g" % (value * 100) puts "Encoding progress (#{progress} %)" elsif modus == "log_change" print value elsif modus == "error" print value print "\n" if get_answer(_("Do you want to change your settings? (y/n) : "), "yes",_("y")) : edit_settings() end elsif modus == "cddb_hit" && value == false #final values freedb freedb_finished elsif modus == "cddb_hit" && value != false #multiple freedb hits choose_freedb(value) elsif modus == "dir_exists" dir_exists() elsif modus == "finished" @finished = true end end def tracklist # Fill @settings['tracksToRip'] puts "inside tracklist()" @settings['tracksToRip'] = Array.new @settings['cd'].audiotracks.times{|number| @settings['tracksToRip'] << number + 1} # Start with all tracks selected if @options.all || get_answer(_("\nShould all tracks be ripped ? (y/n) "), "yes", _('y')) puts _("Tracks to rip are %s") % [@settings['tracksToRip'].join(" ")] else succes = false while !succes puts _("Current selection of tracks : %s") % [@settings['tracksToRip'].join(' ')] number = get_answer(_("Enter 1 for entering the tracknumbers you want to remove.\nEnter 2 for entering the tracks you want to keep.\nYour choice: "), "number", 1) if number == 1 print _("Type the numbers of the tracks you want to remove and separate them with a space: ") answer = STDIN.gets.strip.split answer.each_index{|index| answer[index] = answer[index].to_i} #convert to integers @settings['tracksToRip'] -= answer # don't you just love ruby math? [1,2,3,4,5] - [3,4] = [1,2,5] elsif number == 2 print _("Type the numbers of the tracks you want to keep and separate them with a space: ") answer = STDIN.gets.strip.split answer.each_index{|index| answer[index] = answer[index].to_i} #convert to integers remove = @settings['tracksToRip'] - answer # remove is inverted result -> which tracks you want to remove? @settings['tracksToRip'] -= remove # remove these else puts _("%s is not a valid number! Please enter 1 or 2!\n") % [number] end if number == 1 || number == 2 puts _("Current selection of tracks : %s") % [@settings['tracksToRip'].join(' ')] succes = !get_answer(_("Do you want to make any changes? (y/n) : "), "yes", _("n")) end end end end end Gui_CLI.new()