#!/usr/bin/ruby -w # Rip an entire CD into a collection of FLAC files. # Input is an album description text file. Example: # Genre=Classical # Artist=Prokofiev # Album=Violin Sonata No. 1 in F minor / Gidon Kremer & Martha Argerich # Date=1992 # track_01.flac=1. Andante assai # track_02.flac=2. Allegro brusco # track_03.flac=3. Andante # track_04.flac=4. Allegrissimo - Andante assai, come prima require 'fileutils' require 'getoptlong' # Option values $dryrun = false $debug = false $temperature = nil $force = false $eject = false $movealbum = false $flac_track = 1 $cd_track = 1 tags = {} files = [] titles = {} def usage puts "usage: ripalbum [options] album.txt" puts "-v Enable debug messages" puts "-n Dry run, don't run commands" puts "-f Force overwrite of existing files" puts "-e Eject CD after ripping" puts "-m Move album file to rip directory after ripping" puts "-t temp Wait for CPU temperature to drop to this level before each file" puts "-f n Initial FLAC track number" puts "-c n Initial CD track number" exit 1 end opts = GetoptLong.new( [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ], [ "--dryrun", "-n", GetoptLong::NO_ARGUMENT ], [ "--force", "-f", GetoptLong::NO_ARGUMENT ], [ "--eject", "-e", GetoptLong::NO_ARGUMENT ], [ "--movealbum", "-m", GetoptLong::NO_ARGUMENT ], [ "--temperature", "-t", GetoptLong::REQUIRED_ARGUMENT ], [ "--flactrack", "-i", GetoptLong::REQUIRED_ARGUMENT ], [ "--cdtrack", "-c", GetoptLong::REQUIRED_ARGUMENT ] ) opts.each do |opt, arg| case opt when '--verbose' $debug = true when '--dryrun' $dryrun = true when '--force' $force = true when '--eject' $eject = true when '--movealbum' $movealbum = true when '--temperature' $temperature = arg when '--flactrack' $flac_track = arg.to_i when '--cdtrack' $cd_track = arg.to_i end end usage if ARGV.length != 1 # Put out a string if debugging is enabled. def dputs(string) puts(string) if $debug end # Escape quote characters in filename, and surround the result in # quotes, so that it can be embedded in a shell command. def escape(str) '"' + str.gsub('"', '\"') + '"' end # check_status -- # # Check the most recent child program status, and if it # was aborted with control-C, exit. def check_status if $?.signaled? signum = $?.termsig if Signal.list['INT'] == signum puts "\nChild process aborted by Control-C. Exiting." exit 1 end end end # Read the album file. Lines that are of the form "file.flac=title" # define the association between files and track titles. And other lines # of the form "tag=value" are tags that will be applied to every file. albumfile = ARGV[0] track = $flac_track IO.foreach(albumfile) do |line| line.rstrip! case line when /^(.+\.flac)=(.+)$/ files.push($1) titles[$1] = $2 track += 1 when /^(\w+)=(.+)$/ tags[$1] = $2 else # Assume it's a track title. Construct a filename by prepending # the track number and appending a file extension. title = sprintf("%02d - %s", track, line) filename = title + ".flac" files.push(filename) titles[filename] = title track += 1 end end # Make sure the required tags are present bad = false ['Artist', 'Genre', 'Date', 'Album'].each do |key| if !tags[key] puts "#{key} not defined in album file!" bad = true end end exit 1 if bad # Rip each track and pipe the data to the FLAC encoder. # Set the tags for each file, and also set the track numbers in ascending # order. track = $flac_track flacdir = nil files.each do |file| dputs "Ripping #{file}" flacfile = tags['Artist'] + '/' + tags['Album'] + '/' + file # Create output directory if it doesn't exist. flacdir = File.dirname(flacfile) unless File.directory?(flacdir) dputs "Creating directory #{flacdir}" if $dryrun puts("mkdir -p #{flacdir}") else FileUtils.mkdir_p(flacdir) end end if File.exist?(flacfile) # Don't rip the track if the FLAC file already exists. puts "File #{flacfile} already exists. Skipping." else # Construct the pipe command that will rip the track and encode it. tracktag = sprintf("%02d", track) cdtrack = track - $flac_track + $cd_track cmd = "cdparanoia -q #{cdtrack}-#{cdtrack} - |" + "flac --best -o " + escape(flacfile) + " --tag=Title=" + escape(titles[file]) + " --tag=Tracknumber=#{tracktag}" ['Artist', 'Genre', 'Date', 'Album'].each do |key| cmd += " --tag=#{key}=" + escape(tags[key]) end cmd += ' -f' if $force cmd += ' -' if $dryrun puts("waittemp #{$temperature}") if $temperature puts(cmd) else if $temperature system("waittemp", $temperature) check_status end system(cmd) check_status end end track += 1 end if ($dryrun) puts("mv #{albumfile} #{flacdir}") if $movealbum puts("eject /dev/cdrom") if $eject else FileUtils.mv(albumfile, flacdir, :verbose => $debug) if $movealbum system("eject /dev/cdrom") if $eject end