]>
diplodocus.org Git - flac-archive/blob - fa-flacd
8 B<fa-flacd> - archive CDs to single FLAC files
12 B<fa-flacd> [B<-j> I<jobs>] [B<-v>]
20 use Getopt
::Long
qw(:config gnu_getopt no_ignore_case);
21 use POSIX
':sys_wait_h';
30 $verbose and print(STDERR
$_) for @_;
33 # Return the ARTIST, ALBUM, and DATE followed by a list of all the
34 # lines in the file FN.
44 verbose
("Opening tags file $fn\n");
45 open(TAGS
, $fn) or die("open($fn): $!");
50 ($tag, $value) = split(/=/, $_, 2);
54 verbose
("ARTIST $artist from $fn\n");
55 } elsif (/^ALBUM=/i) {
57 verbose
("ALBUM $album from $fn\n"); # cperl-mode sucks "
58 } elsif (/^DISCNUMBER=/i) {
59 $discnum = int($value);
60 verbose
("DISCNUMBER $discnum from $fn\n");
63 close(TAGS
) or die("close($fn): $!");
65 return ($artist, $album, $discnum, @tags);
72 while (my ($key, $vall) = each(%$h)) {
73 for my $val (@$vall) {
74 push(@result, "$key=$val")
88 @cue = ('--cuesheet', $cue);
91 verbose
("Running flac\n");
92 my $status = system('flac', '-o', "$outfile.flac-tmp",
93 '--delete-input-file', '-V', '--no-padding', '--best',
95 map({ ('-T', $_) } @_),
97 if (WIFEXITED
($status)) {
98 if (($status = WEXITSTATUS
($status)) != 0) {
99 die("flac exited with status $status");
101 } elsif (WIFSIGNALED
($status)) {
102 die("flac killed with signal ", WTERMSIG
($status));
103 } elsif (WIFSTOPPED
($status)) {
104 die("flac stopped with signal ", WSTOPSIG
($status));
106 die("Major horkage on system(flac): \$? = $? \$! = $!");
109 rename("$outfile.flac-tmp", "$outfile.flac")
110 or die("rename($outfile.flac-tmp, $outfile.flac): $!");
113 # Process the fa-rip output in the directory DIR.
122 verbose
("Renaming $dir/tags\n");
123 rename("$dir/tags", "$dir/using-tags")
124 or die("rename($dir/tags, $dir/using-tags): $!");
126 ($artist, $album, $discnum, @tags) = get_tags
("$dir/using-tags");
127 for ($artist, $album) {
131 verbose
("mkdir($artist)\n");
132 -d
$artist or mkdir($artist) or die("mkdir($artist): $!");
134 my $outdir = "$artist/$album";
135 verbose
("mkdir($outdir)\n");
136 -d
"$outdir" or mkdir("$outdir") or die("mkdir($outdir): $!");
138 verbose
("chdir($dir)\n");
139 chdir($dir) or die("chdir($dir): $!");
144 for my $tag (@tags) {
145 if ($tag =~ /^([^[]+)\[(\d+)]=(.*)/) {
146 push(@{$things{$2}->{$1}}, $3);
147 } elsif ($tag =~ /^ARTIST=/) {
156 for my $tracknum (sort(map(int, keys(%things)))) {
157 my $title = join(' ', map(split, @{$things{$tracknum}->{'TITLE'}}));
161 ? sprintf('%02d', $discnum)
163 sprintf('%02d', $tracknum),
165 push(@files, "$outdir/$outfile.flac");
166 $outfile = "../$outdir/$outfile";
169 if (exists($things{$tracknum}->{'ARTIST'})) {
175 run_flac
(sprintf('track%02d.cdda.wav', $tracknum), undef, $outfile,
177 grep({ $_ !~ /^ARTIST=/ } @tags),
178 bork_tags
($things{$tracknum}));
181 verbose
("Cleaning up $dir\n");
182 unlink('using-tags') or die("unlink(using-tags): $!");
183 unlink('cue') or die("unlink(cue): $!");
184 rename('log', "../$outdir/log")
185 or die("rename(log, ../$outdir/log): $!");
186 chdir('..') or die("chdir(..): $!");
188 if (-x
"$dir/post-processor") {
189 verbose
("Running './$dir/post-processor'\n");
190 system("./$dir/post-processor", @files);
191 unlink("$dir/post-processor") or die("unlink($dir/post-processor): $!");
194 rmdir($dir) or die("rmdir($dir): $!");
202 while (($pid = waitpid(-1, WNOHANG
)) > 0) {
203 push(@finished, [$pid, $?]);
206 $SIG{CHLD
} = \
&reaper
;
215 if (not defined($pid)) {
220 if ($debug or $pid == 0) {
221 $SIG{CHLD
} = 'DEFAULT';
222 open(STDERR
, ">$dir/log") or die("open(STDERR, >$dir/log): $!");
226 verbose
("new job $pid for $dir\n");
236 $pid = $finished[$i][0];
237 $status = $finished[$i][1];
239 verbose
("$pid finished (");
240 if (WIFEXITED
($status)) {
241 verbose
('exited with status ', WEXITSTATUS
($status));
242 } elsif (WIFSIGNALED
($status)) {
243 verbose
('killed with signal ', WTERMSIG
($status));
244 } elsif (WIFSTOPPED
($status)) {
245 verbose
('stopped with signal ', WSTOPSIG
($status));
249 for ($j = 0; $j <= $#jobs; $j++) {
250 $pid == $jobs[$j] and splice(@jobs, $j, 1) and last;
253 splice(@finished, $i, 1);
262 $SIG{CHLD
} = \
&reaper
;
264 if (scalar(@jobs) <= $MAXJOBS) {
265 foreach $i (glob('*/tags')) {
266 push(@jobs, newjob
(dirname
($i))) <= $MAXJOBS or last;
270 for ($i = 0; $i <= $#finished; $i++) {
274 verbose
(scalar(@jobs), " jobs\n");
285 'debug|X' => \
$debug,
286 'jobs|j=i' => \
$jobs,
287 'verbose|v' => \
$verbose,
288 'help|h|?' => \
$help,
290 $help and pod2usage
(-exitstatus
=>0, -verbose
=>1);
300 B<fa-flacd> and B<fa-rip> together comprise B<flac-archive>, a system
301 for archiving audio CDs to single FLAC files. B<fa-flacd> is the guts
302 of the system. It runs in the directory where the audio archives are
303 stored, scanning for new ripped CDs to encode and rename; it never
304 exits. B<fa-rip> generates the inputs for B<fa-flacd>: the ripped WAV
305 file, Vorbis tags, and a cuesheet.
307 Both programs expect to be run from the same directory. They use that
308 directory to manage directories named by artist. Intermediate files
309 are written to temporary directories here. B<fa-flacd> processes the
310 temporary directories into per-album files in the artist directories.
312 Every 5 seconds, B<fa-flacd> scans its current directory for
313 directories with a file called "tags" and creates a processing job for
314 each one. The number of jobs B<fa-flacd> attempts to run is
315 controlled by the B<-j> option and defaults to 4. B<fa-flacd> will
316 print diagnostic output when the B<-v> option is given.
318 A processing job first renames the directory's "tags" file to
319 "using-tags" so that B<ra-flacd> will not try to start another job for
320 this directory. This file is left as is when an error is encountered,
321 so a new job will not be started until the user corrects the error
322 condition and renames "using-tags" back to "tags". Next, it encodes
323 the "wav" file to a FLAC file, using the "cue" file for the cuesheet
324 and "using-tags" for Vorbis tags. Any diagnostic output is saved in
325 the "log" file. Finally, B<fa-flacd> moves the "cue" and "log" files
326 to the artist directory (named by album) and removes the temporary
329 If the temporary directory contains an executable file named
330 "post-processor", B<fa-flacd> executes that file with the relative
331 path to the output FLAC file as an argument. The output files are in
332 their final location when "post-processor" starts. Possible uses are
333 running B<flac2mp3>, moving the output files to a different location,
334 removing the lock file, or adding to a database. The standard input,
335 output, and error streams are inherited from B<fa-flacd>, so they may
336 be connected to anything from a tty to /dev/null. This means that you
337 may want to redirect these streams, if you want to save them or do any
344 =item B<-j> [B<--jobs>] I<jobs>
346 Run up to I<jobs> jobs instead of the default 4.
348 =item B<-v> [B<--verbose>]
350 Print diagnostic information.
356 Written by Eric Gillespie <epg@pretzelnet.org>.
358 flac-archive is free software; you may redistribute it and/or modify
359 it under the same terms as Perl itself.
364 # cperl-indent-level: 4
365 # perl-indent-level: 4
366 # indent-tabs-mode: nil
369 # vi: set tabstop=4 expandtab: