6 # Advanced CD-ROM ripping program
8 # Copyright 2010, Colin McCabe
13 require 'optparse/time'
16 #-----------------------------------------------------------------
18 #-----------------------------------------------------------------
19 $cd_dev = "/dev/cdrom"
21 #-----------------------------------------------------------------
23 #-----------------------------------------------------------------
26 system(cmd) unless $opts.dry_run
27 ($?.exitstatus == 0) or raise "#{cmd} failed"
30 def die_unless_installed(cmd)
31 system("which #{cmd} > /dev/null")
32 ($?.exitstatus == 0) or raise "you need to install the #{cmd} program"
35 def get_number_of_tracks_on_cd
36 look_for_tracks = false
37 IO.popen("cdda2wav -v summary -J dev=#{$cd_dev}", "r") do |io|
39 if (line =~ /^AUDIOtrack/) then
40 look_for_tracks = true
41 elsif (look_for_tracks == true) then
42 look_for_tracks = false
43 line =~ /[ \t]*1-([1234567890][1234567890]*)[^1234567890]/ \
44 or raise "couldn't understand cdda2wav output!"
48 raise "couldn't find what we were looking for in cdda2wav output!"
51 def audiorip(track, number)
53 my_system("nice -1 cdparanoia -w -d #{$cd_dev} #{number}")
55 raise "failed to rip track #{number} (#{track.name})"
57 # cdparanoia always outputs to cdda.wav
58 FileUtils.mv("cdda.wav", track.wav_file_name, $fu_args)
60 # TODO: spawn a thread to do this stuff in the background
61 FileUtils.mkdir_p(track.flac_dir, $fu_args)
62 my_system("flac -c #{track.wav_file_name} > #{track.flac_file_name}")
63 my_system("lame -q 1 -b 192 #{track.wav_file_name} > #{track.mp3_file_name}")
64 FileUtils.rm_f(track.wav_file, $fu_args)
67 #-----------------------------------------------------------------
69 #-----------------------------------------------------------------
74 $fu_args = { :verbose => true }
77 parser = OptionParser.new do |myparser|
78 myparser.banner = "Usage: #{ File.basename($0) } [opts]"
79 myparser.separator("Specific options:")
80 myparser.on("--dry-run", "-d",
81 "Show what would be done, without doing it.") do |a|
83 $fu_args = { :verbose => true, :noop => true }
85 myparser.on("--tracklist", "-t",
86 "Provide a list of tracks to use.") do |file|
87 opts.manifest_file = file
90 myparser.on("--partial-tracklist", "-T",
91 "Provide a partial list of tracks to use.") do |file|
92 opts.manifest_file = file
98 raise "you must provide a tracklist" unless opts.manifest_file != nil
104 attr_accessor :name, :flac_dir, :flac_file_name, :mp3_dir, :mp3_file_name
106 if name =~ /\[LL\]/ then
107 raise "you can't include [LL] in a track name"
109 if name =~ /\.mp3/ then
110 raise "don't include .mp3 in the track name; that will be added"
112 if name =~ /\.flac/ then
113 raise "don't include .flac in the track name; that will be added"
115 (name =~ /([^\/][^\/]*)\/([^\/]*[^\/])/) or \
116 raise "track name must be of the form 'foo/bar'"
118 @flac_dir = "#{1} [LL]"
119 @flac_file_name = "#{@flac_dir}/#{2}.flac"
121 @mp3_file_name = "#{@mp3_dir}/#{2}.mp3"
122 @wav_file_name = "#{1}__#{2}.wav"
127 def initialize(filename)
130 @tracks.each do |key, val|
131 @tracks[key] = Track.new(val)
135 def validate(num_tracks)
136 if (@tracks.empty?) then
137 raise "you must define some tracks"
139 @tracks.each { |t| t.validate }
140 if (not $opts.partial) then
141 (1..num_tracks).each do |t|
142 if not @tracks[t].defined?
143 raise "don't know what to do with track #{t}"
150 (1..num_tracks).each do |t|
151 next unless @tracks.defined?(t)
157 #-----------------------------------------------------------------
159 #-----------------------------------------------------------------
163 $opts = MyOptions.parse(ARGV)
164 rescue ArgumentError => msg
165 $stderr.puts("#{msg} Type --help to see usage information.\n")
170 die_unless_installed("lame")
171 die_unless_installed("flac")
172 die_unless_installed("cdparanoia")
173 die_unless_installed("cdda2wav")
175 manifest = Manifest.new($opts.manifest_file)
176 num_tracks = get_number_of_tracks_on_cd()
177 puts "num_tracks = #{num_tracks}"
178 #manifest.rip(num_tracks)