From: Colin Patrick McCabe Date: Sat, 3 Apr 2010 07:47:51 +0000 (-0700) Subject: Initial rev of superrip X-Git-Url: http://club.cc.cmu.edu/~cmccabe/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=25d81bfb0e6b35a7019f0825d879a8d265f87dbf;p=cmccabe-bin Initial rev of superrip --- diff --git a/superrip.rb b/superrip.rb new file mode 100755 index 0000000..0b1ef6f --- /dev/null +++ b/superrip.rb @@ -0,0 +1,179 @@ +#!/usr/bin/ruby -w + +# +# superrip.rb +# +# Advanced CD-ROM ripping program +# +# Copyright 2010, Colin McCabe +# + +require 'fileutils' +require 'optparse' +require 'optparse/time' +require 'ostruct' + +#----------------------------------------------------------------- +# constants +#----------------------------------------------------------------- +$cd_dev = "/dev/cdrom" + +#----------------------------------------------------------------- +# functions +#----------------------------------------------------------------- +def my_system(cmd) + puts cmd + system(cmd) unless $opts.dry_run + ($?.exitstatus == 0) or raise "#{cmd} failed" +end + +def die_unless_installed(cmd) + system("which #{cmd} > /dev/null") + ($?.exitstatus == 0) or raise "you need to install the #{cmd} program" +end + +def get_number_of_tracks_on_cd + look_for_tracks = false + IO.popen("cdda2wav -v summary -J dev=#{$cd_dev}", "r") do |io| + line = io.read.chomp + if (line =~ /^AUDIOtrack/) then + look_for_tracks = true + elsif (look_for_tracks == true) then + look_for_tracks = false + line =~ /[ \t]*1-([1234567890][1234567890]*)[^1234567890]/ \ + or raise "couldn't understand cdda2wav output!" + return $1.to_i + end + end + raise "couldn't find what we were looking for in cdda2wav output!" +end + +def audiorip(track, number) + begin + my_system("nice -1 cdparanoia -w -d #{$cd_dev} #{number}") + rescue + raise "failed to rip track #{number} (#{track.name})" + end + # cdparanoia always outputs to cdda.wav + FileUtils.mv("cdda.wav", track.wav_file_name, $fu_args) + + # TODO: spawn a thread to do this stuff in the background + FileUtils.mkdir_p(track.flac_dir, $fu_args) + my_system("flac -c #{track.wav_file_name} > #{track.flac_file_name}") + my_system("lame -q 1 -b 192 #{track.wav_file_name} > #{track.mp3_file_name}") + FileUtils.rm_f(track.wav_file, $fu_args) +end + +#----------------------------------------------------------------- +# classes +#----------------------------------------------------------------- +class MyOptions + def self.parse(args) + opts = OpenStruct.new + opts.dry_run = false + $fu_args = { :verbose => true } + + # Fill in opts values + parser = OptionParser.new do |myparser| + myparser.banner = "Usage: #{ File.basename($0) } [opts]" + myparser.separator("Specific options:") + myparser.on("--dry-run", "-d", + "Show what would be done, without doing it.") do |a| + opts.dry_run = true + $fu_args = { :verbose => true, :noop => true } + end + myparser.on("--tracklist", "-t", + "Provide a list of tracks to use.") do |file| + opts.manifest_file = file + opts.partial = false + end + myparser.on("--partial-tracklist", "-T", + "Provide a partial list of tracks to use.") do |file| + opts.manifest_file = file + opts.partial = true + end + end + parser.parse!(args) + + raise "you must provide a tracklist" unless opts.manifest_file != nil + return opts + end +end + +class Track + attr_accessor :name, :flac_dir, :flac_file_name, :mp3_dir, :mp3_file_name + def initialize(name) + if name =~ /\[LL\]/ then + raise "you can't include [LL] in a track name" + end + if name =~ /\.mp3/ then + raise "don't include .mp3 in the track name; that will be added" + end + if name =~ /\.flac/ then + raise "don't include .flac in the track name; that will be added" + end + (name =~ /([^\/][^\/]*)\/([^\/]*[^\/])/) or \ + raise "track name must be of the form 'foo/bar'" + @name = name + @flac_dir = "#{1} [LL]" + @flac_file_name = "#{@flac_dir}/#{2}.flac" + @mp3_dir = "#{1}" + @mp3_file_name = "#{@mp3_dir}/#{2}.mp3" + @wav_file_name = "#{1}__#{2}.wav" + end +end + +class Manifest + def initialize(filename) + @tracks = Hash.new + eval(filename) + @tracks.each do |key, val| + @tracks[key] = Track.new(val) + end + end + + def validate(num_tracks) + if (@tracks.empty?) then + raise "you must define some tracks" + end + @tracks.each { |t| t.validate } + if (not $opts.partial) then + (1..num_tracks).each do |t| + if not @tracks[t].defined? + raise "don't know what to do with track #{t}" + end + end + end + end + + def rip(num_tracks) + (1..num_tracks).each do |t| + next unless @tracks.defined?(t) + audiorip(t) + end + end +end + +#----------------------------------------------------------------- +# main +#----------------------------------------------------------------- +# Parse options. +begin + begin + $opts = MyOptions.parse(ARGV) + rescue ArgumentError => msg + $stderr.puts("#{msg} Type --help to see usage information.\n") + exit 1 + end +end + +die_unless_installed("lame") +die_unless_installed("flac") +die_unless_installed("cdparanoia") +die_unless_installed("cdda2wav") + +manifest = Manifest.new($opts.manifest_file) +num_tracks = get_number_of_tracks_on_cd() +puts "num_tracks = #{num_tracks}" +#manifest.rip(num_tracks) +exit 0