+sub tags_file {
+ my $fn = shift;
+ my $trackcount = shift;
+ my $various = shift;
+ my $artist = shift;
+ my $album = shift;
+ my $release_dates = shift;
+ my $fh;
+ my $i;
+ my $track;
+ my $name;
+
+ open($fh, '>', $fn) or die("open('>$fn'): $!");
+ print($fh 'ARTIST=', (defined($artist) and $artist or ''), "\n");
+ print($fh 'ALBUM=', (defined($album) and $album or ''), "\n");
+
+ if (defined($release_dates) and %$release_dates) {
+ while (my ($country, $date) = each(%$release_dates)) {
+ print($fh "DATE[$country]=$date\n");
+ }
+ } else {
+ print($fh "DATE=\n");
+ }
+
+ for $i (1 .. $trackcount) {
+ $various and print($fh "ARTIST[$i]=\n");
+ if ($track = shift(@_)) {
+ $name = $track->get_name;
+ } else {
+ $name = '';
+ }
+ print($fh "TITLE[$i]=$name\n");
+ }
+
+ close($fh) or die("close(>$fn): $!");
+}
+
+sub tags {
+ my $device = shift;
+ my $trackcount = shift;
+ my $no_mb = shift;
+ my $mb;
+ my @results;
+ my $album;
+ my $i;
+ my $various;
+ my $seen_various;
+
+ tags_file('candidate-tags-0', $trackcount, 0);
+
+ defined($no_mb) and $no_mb and return;
+
+ if (defined($device)) {
+ $mb = new MusicBrainz::Client::Simple (device=>$device);
+ } else {
+ $mb = new MusicBrainz::Client::Simple;
+ }
+
+ @results = $mb->lookup_cd;
+ if (not $mb->success) {
+ die($mb->get_error);
+ }
+
+ for $album (@results) {
+ $i++;
+
+ if ($various = $album->has_various_artists) {
+ if (not $seen_various) {
+ $seen_various = 1;
+ tags_file('candidate-tags-0v', $trackcount, 1);
+ }
+ }
+
+ my %dates = $album->get_release_dates;
+ tags_file("candidate-tags-$i", $trackcount, $various,
+ $album->get_artist->get_name, $album->get_name,
+ \%dates, $album->get_tracks);
+ }
+}
+
+sub rip {
+ my $device = shift;
+ my $trackcount = shift;
+
+ $device ||= '/dev/cdrom';
+
+ exec('cdparanoia', '-Bd', $device);
+ # exec prints its own error message so just
+ die;
+}
+
+sub make_post_processor {
+ my $command = shift;
+
+ defined($command) or return;
+
+ sysopen(F, 'post-processor', O_CREAT | O_WRONLY, 0555)
+ or die("sysopen(post-processor, O_CREAT | O_WRONLY, 0555): $!");
+ print(F $command, ' "$@"', "\n");
+ close(F) or die("close(post-processor, O_CREAT | O_WRONLY, 0555): $!");
+}
+
+MAIN: {
+ my $no_mb;
+ my $post_processor;
+ my $trackcount = 99;
+ my $help;
+ my $tempdir;
+
+ GetOptions(
+ 'device|d=s' => \$CDDEV,
+ 'no-musicbrainz|m' => \$no_mb,
+ 'post-processor|p=s', \$post_processor,
+ 'tracks|t=i' => \$trackcount,
+ 'help|h|?' => \$help,
+ ) or pod2usage();
+ $help and pod2usage(-exitstatus=>0, -verbose=>1);
+
+ # File::Temp::tempdir calls die on error.
+ $tempdir = File::Temp::tempdir('flac-archive.XXXXXXXXXX');
+ chdir($tempdir) or die("chdir($tempdir): $!");
+
+ make_post_processor($post_processor);
+ $trackcount = mkcue($CDDEV, $trackcount);
+ tags($CDDEV, $trackcount, $no_mb);
+ rip($CDDEV, $trackcount);
+}
+
+\f
+__END__
+
+=head1 DESCRIPTION
+
+B<fa-rip> creates a temporary directory for storage of its
+intermediate files, uses MusicBrainz to create the "cue" file and
+candidate tags files, and runs C<cdparanoia(1)> to rip the CD to the
+"wav" file.
+
+In order for this CD to be processed by B<fa-flacd>, you must create a
+"tags" file. This is usually done by renaming one of the
+candidate-tags files and deleting the others. Don't forget to fill in
+the DATE tag in the selected candidate before renaming it. If
+B<fa-rip> could not find any tag information from MusicBrainz, you'll
+have to fill out the candidate-tags-0 template.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-d> [B<--device>] I<device>
+
+Use I<device> as the CD-ROM device, instead of the default
+"/dev/cdrom" or the environment variable CDDEV.
+
+=item B<-p> [B<--post-processor>] I<post-processor>
+
+Create a "post-processor" file in the temporary directory containing
+the line 'I<post-processor> "$@"'. See B<fa-flacd>'s man page for
+information about this hook.
+
+=item B<-t> [B<--tracks>] I<track-count>
+
+Archive only the first I<track-count> tracks. This is handy for
+ignoring data tracks.
+
+=back
+
+=head1 ENVIRONMENT
+
+=over 4
+
+=item CDDEV
+
+B<fa-rip> uses this to rip audio and save the cuesheet for a CD.
+MusicBrainz::Client can usually figure this out automatically.
+
+=back
+
+=head1 AUTHORS