#!/usr/bin/perl
#
#    rip -- A command line based, audio Compact Disc (CD) track to
#    Motion Picture Experts Group Layer 3 (MP3) file, ripper.
#
#    Copyright (C) 2001 Gregory Smethells
#
#
#    License:
#    This program 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 2
#    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, write to the Free Software
#    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#    02111-1307, USA.
#
#
#    Disclaimer:
#    YOU SHOULD ONLY CREATE MP3s FROM AUDIO CDs THAT YOU OWN LEGALLY.
#    ANY OTHER USE OF THIS SCRIPT IS NOT LEGAL, IS NOT CONDONED BY THIS
#    AUTHOR, NOR IS SUCH ACTION IN THE SPIRIT OF THIS SCRIPT. THE AUTHOR
#    TAKES NO RESPONSIBILITY FOR MIS-USE OF THIS SOFTWARE OR RELATED
#    SOFTWARE.
#
#
#    Author's Notes:
#    This script requires a CD ripper such as cdparanoia and an MP3
#    application that can code MP3s from WAVs such as bladeenc. These
#    tools need to be on your path, since it is essentially only a
#    wrapper. All major functionality is found in those programs.
#    However, I believe that the abstract functionality provided by
#    this script is quite useful and makes these tools all the more
#    powerful.
#
#    In the future, I hope to add more CD rippers and, mostly, more
#    MP3 encoders that work with this script. The essential changes
#    would be to add more $PATH searches for different tools of these
#    types and then set the flags available for that tool.
#




##########################################################################
#                                                                        #
# DECLARATIONS                                                           #
#                                                                        #
##########################################################################

use Cwd;
use Time::localtime;


$time            = localtime(time());
$now             = $time->mon . $time->mday . $time->hour . $time->min . $time->sec;

$date            = "2001-2-21";     # Date of last modification
$version         = "0.76";          # Version number for this script

$tempFile        = "/tmp/rip-$now"; # Default temp file
$DEFAULT_DIR     = getcwd;          # Default "default directory"
$cwd             = $DEFAULT_DIR;    # For convience
$outputDir       = $DEFAULT_DIR;    # Default directory where output is placed
$wavONLY         = "false";         # Default is to encode CD tracks to MP3

@PATH            = split( ":", $ENV{PATH});
$numPaths        = @PATH;

@supportedRippers  = ( "cdparanoia" );
@existantRipper    = ( ""           );
@supportedEncoders = ( "bladeenc", "BladeEnc", "lame", "gogo"  );
@existantEncoder   = ( "",         "",         "",     ""      );
@supportedCDDBs    = ( "cddb.pl" );  # Unused currently
@existantCDDB      = ( ""        );  # Just does `cddb.pl`

$numSupportedRippers  = @supportedRippers;
$numSupportedEncoders = @supportedEncoders;
$numSupportedCDDBs    = @supportedCDDBs;


##########################################################################
#                                                                        #
# TOOLS AVAILABLE                                                        #
#                                                                        #
##########################################################################

# Make sure we have our tools available on the $PATH first...
$cdRipperFOUND   = "";
$mp3EncoderFOUND = "";
$cddbScriptFOUND = "";


# Find valid CD ripper tools
for( $i = 0 ; $i < $numSupportedRippers ; $i++ ) {
  $FOUND = "";

  for( $j = 0 ; ($j < $numPaths && !($FOUND)) ; $j++ ) {
    if( -e ("$PATH[$j]/$supportedRippers[$i]") ) {
      $FOUND         = "true";
      $cdRipperFOUND = "true";
      $existantRipper[$i] = "$PATH[$j]/$supportedRippers[$i]";
      $cdRipperPath  = "$PATH[$j]/$supportedRippers[$i]";
    }
  }
}


# Find valid MP3 Encoder tools
for( $i = 0 ; $i < $numSupportedEncoders ; $i++ ) {
  $FOUND = "";

  for( $j = 0 ; ($j < $numPaths && !($FOUND)) ; $j++ ) {
    if( -e ("$PATH[$j]/$supportedEncoders[$i]") ) {
      $FOUND           = "true";
      $mp3EncoderFOUND = "true";
      $existantEncoder[$i] = "$PATH[$j]/$supportedEncoders[$i]";
      $mp3EncoderPath  = "$PATH[$j]/$supportedEncoders[$i]";
    }
  }
}


# Make sure the MP3 encoder is set if there are multiple ones
# and the user didn't flag which one to use within @ARGV
#
# Note: We default to GOGO if both BladeEnc and GOGO are on the $PATH
# Reason: Speed
#
if( ($existantEncoder[0] && $existantEncoder[3]) ||
    ($existantEncoder[1] && $existantEncoder[3])     ) {

  $mp3EncoderPath = $existantEncoder[3];

}
#
# Note: We default to LAME if both BladeEnc and LAME are on the $PATH
# Reason: Speed
#
elsif( ($existantEncoder[0] && $existantEncoder[2]) ||
       ($existantEncoder[1] && $existantEncoder[2])     ) {

  $mp3EncoderPath = $existantEncoder[2];

}
#
# Note: We default to GOGO if both LAME and GOGO are on the $PATH
# Reason: Speed
#
elsif( ($existantEncoder[2] && $existantEncoder[3]) ) {

  $mp3EncoderPath = $existantEncoder[3];

}


# Determine which MP3 encoder user likes better
# based on whether a flags is set in @ARGV
for( $i = 0 ; $i < @ARGV ; $i++ ) {
  if( $ARGV[0] =~ /B/ && !($ARGV[0] =~ /=/) || $ARGV[0] =~ /--bladeenc/ ) {
    if( $existantEncoder[0] ) {
      $mp3EncoderPath = $existantEncoder[0];
    }
    else {
      $mp3EncoderPath = $existantEncoder[1];
    }

    $setEncoder = "true";

    if( $ARGV[0] =~ /--bladeenc/ || $ARGV[0] =~ /^-B$/ ) {
      shift( @ARGV );
    }
    else {
      $_ = $ARGV[0];
      s/B//g;
      $ARGV[0] = $_;
    }
  }

  if( $ARGV[0] =~ /L/ && !($ARGV[0] =~ /=/) || $ARGV[0] =~ /--lame/ ) {
    $mp3EncoderPath = $existantEncoder[2];
    $setEncoder = "true";

    if( $ARGV[0] =~ /--lame/ || $ARGV[0] =~ /^-L$/ ) {
      shift( @ARGV );
    }
    else {
      $_ = $ARGV[0];
      s/L//g;
      $ARGV[0] = $_;
    }
  }

  if( $ARGV[0] =~ /G/ && !($ARGV[0] =~ /=/) || $ARGV[0] =~ /--gogo/ ) {
    $mp3EncoderPath = $existantEncoder[3];
    $setEncoder = "true";

    if( $ARGV[0] =~ /--gogo/ || $ARGV[0] =~ /^-G$/ ) {
      shift( @ARGV );
    }
    else {
      $_ = $ARGV[0];
      s/G//g;
      $ARGV[0] = $_;
    }
  }

  $temp = shift( @ARGV );
  @ARGV = ( @ARGV, $temp );
}


##########################################################################
#                                                                        #
# FLAGS AVAILABLE                                                        #
#                                                                        #
##########################################################################

# The flags should be set dependent on which tool was found
# on the $PATH for either the purpose of being a CD ripper
# or being a MP3 encoder.


# Set the cdRipper flags
if( $cdRipperPath =~ /cdparanoia/ ) {
  $outputWAV           = " *.cdda.wav ";
  $cdRipperFlags       = " -B ";            # Default cdparanoia flags

  $cdRipperVERBOSE     = " -v ";
  $cdRipperQUIET       = " --quiet ";
  $cdRipperSPEED       = " --force-read-speed ";
  $cdRipperDEVICE      = " --force-cdrom-device ";
  $cdRipperPARANOIA    = " --never-skip ";
  $cdRipperQUERY       = "cdparanoia -sQ";
  $cdRipperVERSION     = " --version ";
}


# Set the mp3Encoder flags
if( $mp3EncoderPath =~ /bladeenc/ || $mp3EncoderPath =~ /BladeEnc/ ) {
  $mp3EncoderFlags     = " -del ";          # Default BladeEnc flags

  $mp3EncoderQUIET     = " -quiet ";
  $mp3EncoderBITRATE   = " -br ";
  $mp3EncoderPARANOIA  = " -br 160 ";
  $mp3EncoderVERSION   = " --version ";
}
elsif( $mp3EncoderPath =~ /gogo/ ) {
  $mp3EncoderFlags     = " -delete ";       # Default GOGO flags

  $mp3EncoderQUIET     = " -silent ";
  $mp3EncoderBITRATE   = " -b ";
  $mp3EncoderPARANOIA  = " -b 160 ";
  $mp3EncoderVERSION   = " --version ";
}
elsif( $mp3EncoderPath =~ /lame/ ) {
  $mp3EncoderFlags     = "  ";              # Default LAME flags

  $mp3EncoderQUIET     = " -S ";
  $mp3EncoderBITRATE   = " -b ";
  $mp3EncoderPARANOIA  = " -b 160 ";
  $mp3EncoderVERSION   = "  ";
}


##########################################################################
#                                                                        #
# MAIN SCRIPT                                                            #
#                                                                        #
##########################################################################

if( !($cdRipperFOUND) || !($mp3EncoderFOUND) ) {
  # Prints an error message and exits the script NOW
  &abort;
}
else {
  $case =   ( $ARGV[0] =~ /^-/  )  &&  "flag"     # Either there're flags
         || ( $ARGV[0] =~ /\d/	)  &&  "encode"   # or just digits
         ||                            "default"; # or no args at all

  # Prints a usage message and exits the script IFF there are NO args
  if( $case eq "default" ) {
    &usage;
  }

  # Parse any flags if they exist in the argument list (sets GLOBAL vars)
  if( $case eq "flag" ) {
    &parseFlags;
    &handleFlags;
  }

  # Encode the requested tracks accordingly
  &parseTracks;
  &setNames;
  &encode;
}

exit( 0 );



##########################################################################
#                                                                        #
# SUBROUTINES                                                            #
#                                                                        #
##########################################################################


##########################################################################
#                                                                        #
# SUB: parseFlags                                                        #
#                                                                        #
#   Parses arguments (flags) given to rip. Once it is determined what    #
#   flags have been given, either GLOBAL variables that the subroutine   #
#   "encode" looks at are set or functions such as cdparanoia's query    #
#   are run.                                                             #
#                                                                        #
##########################################################################

sub parseFlags {
  while( $ARGV[0] =~ /^-/ ) {

    # Parse flags of the form:  --query

    $item =   ( $ARGV[0] =~ /^--help/      )  &&  "help"
           || ( $ARGV[0] =~ /^--query/     )  &&  "query"
           || ( $ARGV[0] =~ /^--version/   )  &&  "version"
           || ( $ARGV[0] =~ /^--verbose/   )  &&  "verbose"
           || ( $ARGV[0] =~ /^--paranoia/  )  &&  "paranoia"
           || ( $ARGV[0] =~ /^--quiet/     )  &&  "quiet"
           || ( $ARGV[0] =~ /^--rename/    )  &&  "rename"
           || ( $ARGV[0] =~ /^--bitrate=/  )  &&  "bitrate"
           || ( $ARGV[0] =~ /^-b=/         )  &&  "bitrate"
           || ( $ARGV[0] =~ /^--move=/     )  &&  "move"
           || ( $ARGV[0] =~ /^-m=/         )  &&  "move"
           || ( $ARGV[0] =~ /^--wav/       )  &&  "wav"
           || ( $ARGV[0] =~ /^-s=/         )  &&  "cdspeed"
           || ( $ARGV[0] =~ /^--speed=/    )  &&  "cdspeed"
           || ( $ARGV[0] =~ /^-d=/         )  &&  "device"
           || ( $ARGV[0] =~ /^--dev=/      )  &&  "device"
           || ( $ARGV[0] =~ /^--cddb/      )  &&  "cddb"
           || ( $ARGV[0] =~ /^--lazy/      )  &&  "lazy"
           || ( $ARGV[0] =~ /^--superlazy/ )  &&  "superlazy"
           || ( $ARGV[0] =~ /^--number/    )  &&  "number"
           || ( $ARGV[0] =~ /^--gogo/      )  &&  "gogo"
           || ( $ARGV[0] =~ /^--bladeenc/  )  &&  "bladeenc"
           || ( $ARGV[0] =~ /^--lame/      )  &&  "lame"
           || ( $ARGV[0] =~ /^--eject/     )  &&  "eject"
           || ( $ARGV[0] =~ /^--trayclose/ )  &&  "trayclose"
           ||                                     "unknown";


    # Parse out value given to bitrate=, move=, speed=,
    # or dev= when argument is in one of those formats.

    $revArg = reverse( $ARGV[0] );
    chop($revArg); chop($revArg); chop($revArg);

    if( $ARGV[0] =~ /^--bitrate=/ ) {
      chop($revArg); chop($revArg); chop($revArg); chop($revArg);
      chop($revArg); chop($revArg); chop($revArg);
      $kbps = reverse( $revArg );
    }
    elsif( $ARGV[0] =~ /^-b=/ ) {
      $kbps = reverse( $revArg );
    }
    elsif( $ARGV[0] =~ /^--move=/ ) {
      chop($revArg); chop($revArg); chop($revArg); chop($revArg);
      $outputDir = reverse( $revArg );
      $outputDir = $outputDir . "\/";
    }
    elsif( $ARGV[0] =~ /^-m=/ ) {
      $outputDir = reverse( $revArg );
      $outputDir = $outputDir . "\/";
    }
    elsif( $ARGV[0] =~ /^--speed=/ ) {
      chop($revArg); chop($revArg); chop($revArg);
      chop($revArg); chop($revArg);
      $speed = reverse( $revArg );
    }
    elsif( $ARGV[0] =~ /^-s=/ ) {
      $speed = reverse( $revArg );
    }
    elsif( $ARGV[0] =~ /^--dev=/ ) {
      chop($revArg); chop($revArg); chop($revArg);
      $dev = reverse( $revArg );
    }
    elsif( $ARGV[0] =~ /^-d=/ ) {
      $dev = reverse( $revArg );
    }


    if( $item ne "unknown" ) {
      # Accumulate the $item to be dealt with later or...

      @flagsToCheck = ( $item, @flagsToCheck );
    }
    else {
      # ...parse flags of the form:  -Qpr

      while( $ARGV[0] =~ /^-\w/ ) {
        $check = chop( $ARGV[0] );

        $item =   ( $check =~ /h/  )  &&  "help"
               || ( $check =~ /q/  )  &&  "query"
               || ( $check =~ /V/  )  &&  "version"
               || ( $check =~ /v/  )  &&  "verbose"
               || ( $check =~ /p/  )  &&  "paranoia"
               || ( $check =~ /Q/  )  &&  "quiet"
               || ( $check =~ /r/  )  &&  "rename"
               || ( $check =~ /w/  )  &&  "wav"
               || ( $check =~ /c/  )  &&  "cddb"
               || ( $check =~ /l/  )  &&  "lazy"
               || ( $check =~ /S/  )  &&  "superlazy"
               || ( $check =~ /n/  )  &&  "number"
               || ( $check =~ /G/  )  &&  "gogo"
               || ( $check =~ /B/  )  &&  "bladeenc"
               || ( $check =~ /L/  )  &&  "lame"
               || ( $check =~ /e/  )  &&  "eject"
               || ( $check =~ /t/  )  &&  "trayclose"
               ||                         "default";

        @flagsToCheck = ( $item, @flagsToCheck );
      }
    }

    shift( @ARGV );
  }
}



##########################################################################
#                                                                        #
# SUB: handleFlags                                                       #
#                                                                        #
#   Once parseFlags sets up the @flagsToCheck array, handleFlags may     #
#   run through the array, taking appropriate action. These actions can  #
#   include, and are not limited to: setting cdparaoniaFlags or setting  #
#   mp3EncoderFlags, querying & exiting, calling usage & exiting, and    #
#   printing out version info. Many switches only set flags and do not   #
#   exit the script.                                                     #
#                                                                        #
##########################################################################

sub handleFlags {
  # Finished parsing the ARGV array. Now determine how to
  # flag cdparanoia and bladeenc; possibly reacting, calling
  # subroutines abort or usage, even exit.

  foreach $flag ( @flagsToCheck ) {
    if( $flag eq "query" ) {
        `$cdRipperQUERY`;
        exit( 0 );
    }
    elsif( $flag eq "version" ) {
        print( "\n" );
        print( `$cdRipperPath $cdRipperVERSION` );
        print( "\n\n" );
        print( `$mp3EncoderPath $mp3EncoderVERSION` );
        print( "\n\n\n\n"                                                    );
        print( "########################################################\n"  );
        print( "#     rip  --  Created by Greg Smethells (c) 2001      #\n"  );
        print( "########################################################\n"  );
        print( "# Current version: $version was last modified on $date #\n"  );
        print( "# Please report bugs and/or email your suggestions to  #\n"  );
        print( "#             gsmethells\@linuxfreak.com                #\n" );
        print( "#    http://www.linuxfreak.com/~gsmethells/rip.html    #\n"  );
        print( "########################################################\n"  );
        print( "\n\n"                                                        );

        exit( 0 );
    }
    elsif( $flag eq "paranoia" ) {
        $cdRipperFlags   = $cdRipperFlags   . $cdRipperPARANOIA;
        $mp3EncoderFlags = $mp3EncoderFlags . $mp3EncoderPARANOIA;
    }
    elsif( $flag eq "bitrate" ) {
        if( !($kbps =~ /\d{1,4}/) ) {
          die( "Invalid bitrate ${kbps}. Aborting.\n" );
        }

        $mp3EncoderFlags = $mp3EncoderFlags . $mp3EncoderBITRATE . $kbps;
    }
    elsif( $flag eq "device" ) {
        if( $dev =~ /^\/dev\/\w+/ ) {
          $cdRipperFlags = $cdRipperFlags . $cdRipperDEVICE . $dev;
        }
        else {
          die( "Invalid device: ${dev}. Aborting.\n" ) ;
        }
    }
    elsif( $flag eq "eject" ) {
        $eject = "true";
    }
    elsif( $flag eq "trayclose" ) {
        `eject --trayclose`;
    }
    elsif( $flag eq "lame" ) {
        if( $existantEncoder[2] ) {
          $mp3EncoderPath = $existantEncoder[2];
        }
        else {
          print( "No \"lame\" on your \$PATH though a -L or a\n" );
          print( "--lame flag was specified. Aborting.\n" );
          &abort;
        }
    }
    elsif( $flag eq "gogo" ) {
        if( $existantEncoder[3] ) {
          $mp3EncoderPath = $existantEncoder[3];
        }
        else {
          print( "No \"gogo\" on your \$PATH though a -G or a\n" );
          print( "--gogo flag was specified. Aborting.\n" );
          &abort;
        }
    }
    elsif( $flag eq "bladeenc" ) {
        if( $existantEncoder[0] ) {
          $mp3EncoderPath = $existantEncoder[0];
        }
        elsif( $existantEncoder[1] ) {
          $mp3EncoderPath = $existantEncoder[1];
        }
        else {
          print( "No \"bladeenc\" and no \"BladeEnc\" on your \$PATH\n" );
          print( "though a -B or a --bladeenc flag was specified. Aborting.\n" );
          &abort;
        }
    }
    elsif( $flag eq "number" ) {
        $numberThem = "true";
    }
    elsif( $flag eq "cddb" ) {
        $checkDatabase = "true";
    }
    elsif( $flag eq "lazy" ) {
        @flagsToCheck = (@flagsToCheck, "paranoia" );
        @flagsToCheck = (@flagsToCheck, "cddb"     );
        @flagsToCheck = (@flagsToCheck, "quiet"    );
        @flagsToCheck = (@flagsToCheck, "trayclose");
        @flagsToCheck = (@flagsToCheck, "eject"    );
        $lazy         = "true";
    }
    elsif( $flag eq "superlazy" ) {
        @flagsToCheck = (@flagsToCheck, "paranoia" );
        @flagsToCheck = (@flagsToCheck, "cddb"     );
        @flagsToCheck = (@flagsToCheck, "quiet"    );
        @flagsToCheck = (@flagsToCheck, "trayclose");
        @flagsToCheck = (@flagsToCheck, "eject"    );
        $lazy         = "true";
        $superlazy    = "true";
    }
    elsif( $flag eq "cdspeed" ) {
        $okaySpeed = "true";

        if(  !( $speed =~ /^\d+$/ )  ) {
          die( "Invalid CD read speed: ${speed}. Aborting.\n" );
        }
        else {
          if( $speed > 50 || $speed < 0 ) {
            $okaySpeed = "false";

            print( "\nCD read speed should be ${speed}?\n" );
            print( "Are you sure (y/N)? " );
            chop( $answer = <STDIN> );

            if( $answer =~ /^[yY]/ ) {
              $okaySpeed = "true";
            }
          }
        }

        if( $okaySpeed eq "true" ) {
          $cdRipperFlags = $cdRipperFlags . $cdRipperSPEED . $speed;
        }
    }
    elsif( $flag eq "rename" ) {
        $renameTracks = "true";
    }
    elsif( $flag eq "move" ) {
        if(  !( -e $outputDir )  ) {
          `mkdir $outputDir`;

          if(  !( -e $outputDir ) || !( -w $outputDir )  ) {
            print( "Either the output directory $outputDir cannot be created\n" );
            $outputDir = $DEFAULT_DIR;
            print( "or it is not writable. Output will be to $outputDir instead.\n" );
          }
          else {
            $moveIt         = "true";
            $absPathMoveDir = $outputDir;
          }
        }
    }
    elsif( $flag eq "quiet"  ) {
        $cdRipperFlags   = $cdRipperFlags   . $cdRipperQUIET;
        $mp3EncoderFlags = $mp3EncoderFlags . $mp3EncoderQUIET;
    }
    elsif( $flag eq "wav" ) {
        $wavONLY = "true";
    }
    elsif( $flag eq "verbose" ) {
        $cdRipperFlags = $cdRipperFlags . $cdRipperVERBOSE;
    }
    elsif( $flag eq "help" || $flag eq "default" ) {
        &usage;
    }
    else {
        print( "rip:  Unknown flag \"${flag}\". It will be ignored.\n" );
    }
  }
}



##########################################################################
#                                                                        #
# SUB: parseTracks                                                       #
#                                                                        #
#   Parses the track list argument to rip. Then set @trackList to show   #
#   what tracks to handle.                                               #
#                                                                        #
##########################################################################

sub parseTracks {
  # Parse the tracks list given after the flags
  while( $ARGV[0] =~ /\d/ ) {
    # Determine if the user used a dash or spaced number format
    if( $ARGV[0] =~ /^\d{1,2}-\d{1,2}$/ ) {      # USED DASHED
      $char  = chop( $ARGV[0] );
      $start = "";
      $end   = "";

      while( $char ne '-' ) {
        $end  = $char . $end;
        $char = chop( $ARGV[0] );
      }

      $start = $ARGV[0];

      @trackList = ( @trackList, ${start}..${end} );
    }
    elsif( $ARGV[0] =~ /^\d{1,2}$/ ) {           # USED SPACED
      @trackList = ( @trackList, $ARGV[0] );
    }
    else {                                       # ELSE IGNORE
      print( "Sorry. Cannot rip \"$ARGV[0]\"\n" );
      $case = "default";
    }

    shift( @ARGV );
  }

  if( @trackList != 0 ) {
    print( "\n\nRipping the following track(s):  @trackList\n\n" );
  }
}



##########################################################################
#                                                                        #
# SUB: setNames                                                          #
#                                                                        #
#   If the track are to be renamed after encoding, then request or set   #
#   the names to be used for each track now.                             #
#                                                                        #
##########################################################################

sub setNames {
  if( $checkDatabase eq "true" && $renameTracks eq "true" ) {
    print( "\nOnly ONE of these flags can be used \n" );
    print( "at the same time: -c/--cddb, -r/--rename\n\n" );
    &abort;
  }

  if( $renameTracks eq "true" && $wavONLY eq "false" ) {
    &manualRename;
  }

  if( $checkDatabase eq "true" && $wavONLY eq "false" ) {
    &cddbRename;
  }

  if( $lazy eq "true" ) {
    &beLazy;
  }
}



##########################################################################
#                                                                        #
# SUB: manualRename                                                      #
#                                                                        #
#   Prompt the user, manually, to give the proper name for each track    #
#   which is to be ripped to MP3.                                        #
#                                                                        #
##########################################################################

sub manualRename {
  print( "\n****************************************************\n" );
  print( "We will now take the proper names for the mp3 files.\n" );
  print( "EXAMPLE:  Riders_On_The_Storm.mp3\n\n"                  );
  print( "You may remove the CD for a moment if you need it.\n\n" );

  $doItAgain = "true";

  while( $doItAgain eq "true" ) {
    @renameList = ();

    foreach $num (@trackList) {
      $newName = "";

      if( $num < 10 ) {
        print( "<Rename track $num >  " );
      }
      else {
        print( "<Rename track $num>  " );
      }

      chop( $newName = <STDIN> );
      @renameList = ( @renameList, $newName );
    }

    print( "\n\n\n" );

    $num = 1;
    foreach $item (@renameList) {
      if( $num < 10 ) {
        print( "track $num : $item\n" );
      }
      else {
        print( "track $num: $item\n" );
      }

      $num++;
    }

    print( "\nIs this right (Y/n)?  " );
    chop( $answer = <STDIN> );
    print( "\n" );

    if( $answer =~ /^[nN]/ ) {
      $doItAgain = "true";
    }
    else {
      $doItAgain = "false";
    }
  }

  print( "\nMake sure the CD is in the drive and hit <ENTER>." );
  <STDIN>;

  print( "****************************************************\n\n" );
}



##########################################################################
#                                                                        #
# SUB: cddbRename                                                        #
#                                                                        #
#   Setup the renameList variable by way of CDDB. If there are no track  #
#   arguments to rip, just print CDDB info to the screen.                #
#                                                                        #
##########################################################################

sub cddbRename {
  # Get the names to rename MP3s to, via CDDB, if so requested (flagged)
  chop( $cddbPath = `which cddb.pl` );
  $cddbFound = ( $cddbPath =~ /cddb.pl/ );

  if( !($cddbFound) ) {
    print( "The -c or --cddb flag you used requires CDDB/CDDB_get\n" );
    print( "You can find this on http://freshmeat.com by searching\n" );
    print( "on the keyword:  CDDB_get\n" );
  }
  else {
    if(  @trackList == 0 && !($lazy eq "true")  ) {
      print( "Connecting to freedb.freedb.org...\n\n" );
      `$cddbPath > /dev/tty`;
    }
    else {
      `rm -f /tmp/rip-*`;

      print( "Connecting to freedb.freedb.org... " );
      `echo 1 | $cddbPath > $tempFile`;
      print( "Done.\n" );

      open( INFILE, "<$tempFile") || die( "rip:  cannot open file: $!\n");

      if( eof INFILE ) {
        print( "rip:  Cannot connect to CDDB: Connection refused.\n" );
        print( "rip:  CDDB lookup has been aborted.\n" );

        if( $lazy eq "true" ) {
          print( "rip:  Cannot complete lazy rip. Exiting script.\n" );
          exit( 2 );
        }
      }
      else {
        # Read the artist name (used by subroutine "beLazy" if it's called)
        while(  !($_ =~ /artist/)  ) {
          $_ = <INFILE>;
        }

        chop( $_ );
        s/ //g;
        @line = split( ":", $_ );
        
        # May get "Choose: artist: SomeNameHere"
        # instead of "artist: SomeNameHere" if forced to chose
        # between CDDB entries (we chose first one no matter what).
        if( $line[1] =~ /artist/ ) {
          $artist = $line[2];
        }
        else {
          $artist = $line[1];
        }

        # Read the album name (used by subroutine "beLazy" if it's called)
        while(  !($_ =~ /title/)  ) {
          $_ = <INFILE>;
        }

        chop( $_ );
        s/ //g;
        s/[\(\)\[\]\{\}\'\"\`\.\?,]//g;
        @line = split( ":", $_ );
        $title = $line[1];

        # Read the num of tracks
        while(  !($_ =~ /trackno/)  ) {
          $_ = <INFILE>;
        }

        chop( $_ );
        s/ //g;
        @line = split( ":", $_ );
        $trackno = $line[1];

        for( $i = 0 ; $i < $trackno ; $i++ ) {
          $_ = <INFILE>;
          chop( $_ );
          s/ /_/g;
          s/[\(\)\[\]\{\}\'\"\`\.\?,]//g;
          @line = split( ":", $_);
          $newName = reverse( $line[1] );
          chop( $newName );
          $newName = reverse( $newName ) . ".mp3";
          @renameList = ( @renameList, $newName );
        }
      }
    }
  }

  close( INFILE );
}



##########################################################################
#                                                                        #
# SUB: beLazy                                                            #
#                                                                        #
#   Using cddb.pl, create a dir in the current working dir with the name #
#   given in the artist field, setup the trackList to rip the entire CD, #
#   and create a playlist that XMMS could use in the "artist's" dir.     #
#   This entire section of code assumes you have already called the      #
#   subroutine "cddbRename" prior to this point.                         #
#                                                                        #
##########################################################################

sub beLazy {
  # Setup the output dir based on the artist's name
  $outputDir = "$cwd/$artist/";

  if(  !( -e $outputDir ) ) {
    `mkdir $outputDir`;

    if(  !( -e $outputDir ) || !( -w $outputDir )  ) {
      print( "Either the output directory $outputDir cannot be created\n" );
      $outputDir = $DEFAULT_DIR;
      print( "or it is not writable. Output will be to $outputDir instead.\n" );
    }
  }

  # Setup the output dir based on the artist's name and album title
  # if we are being very lazy
  if( $superlazy eq "true" ) {
    $outputDir = "$cwd/$artist/$title/";

    if(  !( -e $outputDir ) ) {
      `mkdir $outputDir`;

      if(  !( -e $outputDir ) || !( -w $outputDir )  ) {
        print( "Either the output directory $outputDir cannot be created\n" );
        $outputDir = $DEFAULT_DIR;
        print( "or it is not writable. Output will be to $outputDir instead.\n" );
      }
    }
  }

  # Rip the ENTIRE CD
  @trackList = (1..$trackno);

  # Create play list for XMMS
  `rm -f $cwd/$title`;
  `touch $cwd/$title`;

  open( OUTFILE, ">$cwd/$title" ) || die( "rip:  cannot open file: $!\n" );

  for( $i = 0 ; $i < $trackno ; $i++ ) {
    $nextTrack = $renameList[$i];
    $value     = $i + 1;

    if( $numberThem eq "true" && $value < 10 ) {
      print OUTFILE ( "${outputDir}0${value}_${nextTrack}\n" );
    }
    elsif( $numberThem eq "true" ) {
      print OUTFILE ( "${outputDir}${value}_${nextTrack}\n" );
    }
    else {
      print OUTFILE ( "${outputDir}${nextTrack}\n" );
    }
  }

  close( OUTFILE );
}



##########################################################################
#                                                                        #
# SUB: encode                                                            #
#                                                                        #
#   Based upon what GLOBAL variables have been set, encode the given     #
#   tracks to MP3 or to WAV one at a time. This saves hard drive space   #
#   compared to ripping all tracks to WAV first and then encoding.       #
#                                                                        #
##########################################################################

sub encode {
  # If in verbose mode, output flags in use by cdparanoia and bladeenc
  if( $cdRipperFlags =~ /$cdRipperVERBOSE/ ) {
    print( "\nUsing CD Ripper: $cdRipperPath\n" );
    print( "Using CD Ripper flags: $cdRipperFlags\n" );
    print( "Using MP3 Encoder: $mp3EncoderPath\n" );
    print( "Using MP3 Encoder flags: $mp3EncoderFlags\n" );
    print( "Output directory is: $outputDir\n\n" );
  }


  if( $lazy eq "true" && $moveIt eq "true" ) {
    print( "rip:  Using -m/--move and -l/--lazy together not yet implemented.\n" );
  }

  # Rip all the way to MP3 unless the WAV only flags is set
  foreach $track ( @trackList ) {
    if( $track =~ /^\d{1,2}$/ ) {
      print( "\nNow ripping CD track $track...\n" );

      # Rip to WAV
      `$cdRipperPath $cdRipperFlags $track`;

      if( $wavONLY eq "false" ) {
        print( "Now encoding track $track to MP3.\n" );
        print( "Please wait, this may take some time...\n\n" );

        # Set the current track's WAV name (cdparanoia SPECIFIC!)
        if( $track < 10 ) {
          $outputWAV = "track0" . $track . ".cdda.wav";
        }
        else {
          $outputWAV = "track"  . $track . ".cdda.wav";
        }

        # LAME is lame and doesn't do *.wav => *.mp3 but
        # instead does *.wav => *.wav.mp3 which this fixes.
        if( $mp3EncoderPath =~ /lame/ ) {
          if( $track < 10 ) {
            $nextWAV = "track0" . $track . ".cdda";
          }
          else {
            $nextWAV = "track"  . $track . ".cdda";
          }

          `mv $outputWAV $nextWAV`;
          $outputWAV = $nextWAV;
        }

        # Encode to MP3
        `$mp3EncoderPath $mp3EncoderFlags $outputWAV`;
        `rm -f $outputWAV`;

        # Get the track's proper name
        if( $renameTracks eq "true" ) {
          $properName = shift( @renameList );
        }
        elsif( $checkDatabase eq "true" ) {
          $properName = $renameList[ $track - 1 ];
        }
        else {
          $properName = "track" . $track . ".mp3";
        }

        # Possibly add numbering to proper name
        if( $numberThem eq "true" && $track < 10 ) {
          $properName = "0" . $track . "_" . $properName;
        }
        elsif( $numberThem eq "true" ) {
          $properName = $track . "_" . $properName;
        }

        # Set the current track's MP3 name (cdparanoia SPECIFIC!)
        if( $track < 10 ) {
          $currentTrack = "track0" . $track . ".cdda.mp3";
        }
        else {
          $currentTrack = "track"  . $track . ".cdda.mp3";
        }

        # Rename and move the ripped and encoded MP3 file
        `mv $currentTrack ${outputDir}/${properName}`;
      }
    }
    else {
      print( "Track to rip argument was syntactically incorrect: $track\n\n" );
      print( "An example of legal syntax is:   rip 2-5 7 8 10-13 15\n"       );
      exit( 2 );
    }
  }


  if( $eject eq "true" ) {
    print( "Ejecting CD..." );
    `eject --cdrom`;
    print( "\n" );
  }
}



##########################################################################
#                                                                        #
# SUB: usage                                                             #
#                                                                        #
#   Prints a usage message for rip and exits.                            #
#                                                                        #
##########################################################################

sub usage {
  print( "\nUSAGE: rip [option(s)] <track(s)>\n\n"                                        );
  print( "OPTIONS:\n\n"                                                                   );
  print( "  -b=  --bitrate=NUM           set bitrate for encoding to NUM kbps\n"          );
  print( "  -B   --bladeenc              use BladeEnc for MP3 encoding\n"                 );
  print( "  -c   --cddb                  use CDDB to rename MP3 files (must be online)\n" );
  print( "  -d=  --dev=DEV               set input cdrom device to be DEV\n"              );
  print( "  -e   --eject                 eject CD after ripping is complete\n"            );
  print( "  -G   --gogo                  use GOGO for MP3 encoding\n"                     );
  print( "  -h   --help                  print this help to screen\n"                     );
  print( "  -l   --lazy                  rip entire CD, use CDDB to rename MP3 files,\n"  );
  print( "                               move to \$PWD/Artist/ after file is encoded\n"   );
  print( "                               uses -p, -c, and -Q flags while ripping\n"       );
  print( "  -L   --lame                  user LAME for MP3 \"encoding\"\n"  );
  print( "  -m=  --move=DIR              move MP3 files to DIR (absolute pathname)\n"     );
  print( "  -n   --number                append 01_, 02_, etc to start of MP3 filename\n" );
  print( "  -p   --paranoia              Use 160 kbps and don't accept skips on rip\n"    );
  print( "  -q   --query                 print track info for the CD in the drive\n"      );
  print( "  -Q   --quiet                 rip tracks quietly\n"                            );
  print( "  -r   --rename                prompt user for proper name of each track \n"    );
  print( "  -s=  --speed=NUM             set CD read speed to NUM during ripping \n"      );
  print( "  -S   --superlazy             rip entire CD, use CDDB to rename MP3 files,\n"  );
  print( "                               move to \$PWD/Artist/Album after file is encoded\n" );
  print( "                               uses -p, -c, and -Q flags while ripping\n"       );
  print( "  -t   --trayclose             close CD tray before doing anything else\n"      );
  print( "  -v   --verbose               rip tracks in a verbose manner\n"                );
  print( "  -V   --version               print version information\n"                     );
  print( "  -w   --wav                   only rip tracks to *.wav files.\n\n"             );
  print( "EXAMPLES:\n\n"                                                                  );
  print( "  rip 1 3-5 7 12 14            rip tracks 1, 3, 4, 5, 7, 12, and 14 to MP3\n"   );
  print( "  rip -c                       print track info from CDDB to screen\n"          );
  print( "  rip -c 6                     rip track 6 and rename MP3 file via CDDB\n"      );
  print( "  rip -cp 6                    simliar: also uses 160 kbps & accepts no skips\n" );
  print( "  rip -cp -m=/home/mp3 6       simliar: also moves MP3 file to /home/mp3\n"     );
  print( "  rip -Qp -s=32 6              quiet, paranoia, using a CD read speed of 32x\n" );
  print( "  rip -Vp -b=128 6             verbose, paranoia, using 128 kbps encoding\n"    );
  print( "  rip -d=/dev/cdrom 6          rip track 6 using /dev/cdrom as input device\n"  );
  print( "  rip -q                       print track info for CD in drive\n\n"            );
  print( "NORMAL USAGE:\n\n"                                                              );
  print( "  rip -Qpr 1-3 6 8-11          rip to MP3 with quiet paranoia and rename\n"     );
  print( "  rip -Qpc 1-3 6 8-11          similar: renames via CDDB (must be online)\n"    );
  print( "  rip -Qpc -m=/home/mp3 1-11   similar: also does a move (must be online)\n"    );
  print( "  rip -Set                     laziness at its best IMHO (must be online)\n\n"  );
  print( "\n  Note: the default output directory is the present working directory.\n"     );
  print( "  This goes for --lazy and --superlazy in that they output to a subdirectory\n" );
  print( "  \$PWD/ArtistName/ or \$PWD/ArtistName/AlbumTitle respectively.\n"             );
  print( "\n  To use the CDDB features of this script, you need the perl module\n"        );
  print( "  CDDB/CDDB_get installed. Available on http://freshmeat.net.\n\n"              );
  print( "            http://www.linuxfreak.com/~gsmethells/rip.html\n\n"                 );

  exit( 0 );
}



##########################################################################
#                                                                        #
# SUB: abort                                                             #
#                                                                        #
#   Prints error messages if either a CD Ripper or an MP3 Encoder is not #
#   present on the $PATH, then exits.                                    #
#                                                                        #
##########################################################################

sub abort {
  if( !($cdRipperFOUND) ) {
    print( "\nrip:  WARNING: A CD Ripper was *not* found on your \$PATH\n" );
    print ("rip:  you can download one from http://www.xiph.org/paranoia/\n\n" );
  }

  if( !($mp3EncoderFOUND) ) {
    print( "\nrip:  WARNING: An MP3 Encoder was *not* found on your \$PATH\n" );
    print ("rip:  you can download one from http://bladeenc.mp3.no\n\n" );
  }

  exit( 1 );
}
