]> diplodocus.org Git - flac-archive/blob - fa-rip
Add support for a post-processor hook.
[flac-archive] / fa-rip
1 #! /usr/bin/env perl
2
3 # $Id$
4 # $URL$
5
6 =head1 NAME
7
8 B<fa-rip> - rip a CD for B<fa-flacd>
9
10 =head1 SYNOPSIS
11
12 B<fa-rip> [B<-d> I<device>] [B<-p> I<post-processor> [B<-t> I<track-count>]
13
14 =cut
15
16 use strict;
17 use warnings;
18
19 use Env qw(
20 CDDEV
21 );
22
23 use Fcntl qw(O_CREAT O_WRONLY);
24 use File::Temp;
25 use Getopt::Long qw(:config gnu_getopt no_ignore_case);
26 use POSIX ':sys_wait_h';
27 use Pod::Usage;
28
29 use MusicBrainz::Client::Simple;
30
31 sub run_or_die {
32 my $command = shift;
33 my $status;
34
35 $status = system($command);
36
37 if (WIFEXITED($status)) {
38 if (($status = WEXITSTATUS($status)) != 0) {
39 die("$command exited with status $status");
40 }
41 } elsif (WIFSIGNALED($status)) {
42 die("$command killed with signal ", WTERMSIG($status));
43 } elsif (WIFSTOPPED($status)) {
44 die("$command stopped with signal ", WSTOPSIG($status));
45 } else {
46 die("Major horkage on system($command): \$? = $? \$! = $!");
47 }
48 }
49
50 sub mkcue {
51 my $device = shift;
52 my $trackcount = shift;
53 my @command;
54
55 push(@command, 'mkcue');
56
57 if (defined($trackcount)) {
58 push(@command, "-t $trackcount");
59 }
60
61 if (defined($device)) {
62 push(@command, $device);
63 }
64
65 push(@command, '> cue');
66 run_or_die(join(' ', @command));
67
68 if (not defined($trackcount)) {
69 open(F, 'cue') or die("open(cue): $!");
70 $trackcount = grep(/TRACK.*AUDIO/, <F>);
71 close(F);
72 }
73
74 return $trackcount;
75 }
76
77 sub tags {
78 my $device = shift;
79 my $trackcount = shift;
80 my $mb;
81 my @results;
82 my $album;
83 my $i;
84 my @tracks;
85 my $name;
86 my $track;
87 my $j;
88
89 if (defined($device)) {
90 $mb = new MusicBrainz::Client::Simple (device=>$device);
91 } else {
92 $mb = new MusicBrainz::Client::Simple;
93 }
94
95 @results = $mb->lookup_cd;
96 if (not $mb->success) {
97 die($mb->get_error);
98 }
99
100 open(F, '>candidate-tags-0') or die("open('>candidate-tags-0'): $!");
101 print(F "$_=\n") for ('ARTIST', 'ALBUM', 'DATE');
102 print(F "TITLE=\n") for 1 .. $trackcount;
103 close(F) or die("close('>candidate-tags-0'): $!");
104
105 for $album (@results) {
106 $i++;
107 open(F, ">candidate-tags-$i") or die("open(>candidate-tags-$i): $!");
108
109 print(F 'ARTIST=', $album->get_artist->get_name, "\n");
110 print(F 'ALBUM=', $album->get_name, "\n");
111
112 # MusicBrainz doesn't have dates yet; these are usually wrong anyway.
113 print(F "DATE=\n");
114
115 @tracks = $album->get_tracks;
116 for $j (1 .. $trackcount) {
117 if ($track = shift(@tracks)) {
118 $name = $track->get_name;
119 } else {
120 $name = '';
121 }
122 print(F "TITLE=$name\n");
123 }
124
125 close(F) or die("close(>candidate-tags-$i): $!");
126 }
127 }
128
129 sub rip {
130 my $device = shift;
131 my $trackcount = shift;
132
133 $device ||= '/dev/cdrom';
134
135 exec('cdparanoia', '-d', $device, "1-$trackcount", 'wav');
136 # exec prints its own error message so just
137 die;
138 }
139
140 sub make_post_processor {
141 my $command = shift;
142
143 defined($command) or return;
144
145 sysopen(F, 'post-processor', O_CREAT | O_WRONLY, 0555)
146 or die("sysopen(post-processor, O_CREAT | O_WRONLY, 0555): $!");
147 print(F $command, ' "$@"', "\n");
148 close(F) or die("close(post-processor, O_CREAT | O_WRONLY, 0555): $!");
149 }
150
151 MAIN: {
152 my $post_processor;
153 my $trackcount;
154 my $help;
155 my $tempdir;
156
157 GetOptions(
158 'device|d=s' => \$CDDEV,
159 'post-processor|p=s', \$post_processor,
160 'tracks|t=i' => \$trackcount,
161 'help|h|?' => \$help,
162 ) or pod2usage();
163 $help and pod2usage(-exitstatus=>0, -verbose=>1);
164
165 # File::Temp::tempdir calls die on error.
166 $tempdir = File::Temp::tempdir('flac-archive.XXXXXXXXXX');
167 chdir($tempdir) or die("chdir($tempdir): $!");
168
169 make_post_processor($post_processor);
170 $trackcount = mkcue($CDDEV, $trackcount);
171 tags($CDDEV, $trackcount);
172 rip($CDDEV, $trackcount);
173 }
174
175 \f
176 __END__
177
178 =head1 DESCRIPTION
179
180 B<fa-rip> creates a temporary directory for storage of its
181 intermediate files, runs C<mkcue(1)> to create the "cue" file, uses
182 MusicBrainz to generate candidate tags files, and runs
183 C<cdparanoia(1)> to rip the CD to the "wav" file.
184
185 In order for this CD to be processed by B<fa-flacd>, you must create a
186 "tags" file. This is usually done by renaming one of the
187 candidate-tags files and deleting the others. Don't forget to fill in
188 the DATE tag in the selected candidate before renaming it. If
189 B<fa-rip> could not find any tag information from MusicBrainz, you'll
190 have to fill out the candidate-tags-0 template.
191
192 =head1 OPTIONS
193
194 =over 4
195
196 =item B<-d> [B<--device>] I<device>
197
198 Use I<device> as the CD-ROM device, instead of the default
199 "/dev/cdrom" or the environment variable CDDEV.
200
201 =item B<-p> [B<--post-processor>] I<post-processor>
202
203 Create a "post-processor" file in the temporary directory containing
204 the line 'I<post-processor> "$@"'. See B<fa-flacd>'s man page for
205 information about this hook.
206
207 =item B<-t> [B<--tracks>] I<track-count>
208
209 Archive only the first I<track-count> tracks. This is handy for
210 ignoring data tracks.
211
212 =back
213
214 =head1 ENVIRONMENT
215
216 =over 4
217
218 =item CDDEV
219
220 B<fa-rip> uses this to rip audio and save the cuesheet for a CD. It
221 makes some effort to check some common device names for FreeBSD,
222 Linux, and NetBSD by default.
223
224 =back
225
226 =head1 AUTHORS
227
228 Written by Eric Gillespie <epg@pretzelnet.org>.
229
230 flac-archive is free software; you may redistribute it and/or modify
231 it under the same terms as Perl itself.
232
233 =cut
234
235 # Local variables:
236 # cperl-indent-level: 4
237 # perl-indent-level: 4
238 # indent-tabs-mode: nil
239 # End:
240
241 # vi: set tabstop=4 expandtab: