]>
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.
120 my $single_file = -e
"$dir/wav";
126 verbose
("Renaming $dir/tags\n");
127 rename("$dir/tags", "$dir/using-tags")
128 or die("rename($dir/tags, $dir/using-tags): $!");
130 ($artist, $album, $discnum, @tags) = get_tags
("$dir/using-tags");
131 for ($artist, $album) {
135 verbose
("mkdir($artist)\n");
136 -d
$artist or mkdir($artist) or die("mkdir($artist): $!");
138 if (not $single_file) {
139 $outdir = "$artist/$album";
140 verbose
("mkdir($outdir)\n");
141 -d
"$outdir" or mkdir("$outdir") or die("mkdir($outdir): $!");
144 verbose
("chdir($dir)\n");
145 chdir($dir) or die("chdir($dir): $!");
149 defined($discnum) and $outfile .= " (disc $discnum)";
150 run_flac
('wav', 'cue', "../$artist/$outfile", @tags);
151 $outlog = "../$artist/$outfile.log";
152 @files = ("$artist/$outfile.flac");
157 for my $tag (@tags) {
158 if ($tag =~ /^([^[]+)\[(\d+)]=(.*)/) {
159 push(@{$things{$2}->{$1}}, $3);
160 } elsif ($tag =~ /^ARTIST=/) {
168 for my $tracknum (sort(map(int, keys(%things)))) {
169 my $title = join(' ', map(split, @{$things{$tracknum}->{'TITLE'}}));
173 ? sprintf('%02d', $discnum)
175 sprintf('%02d', $tracknum),
177 push(@files, "$outdir/$outfile.flac");
178 $outfile = "../$outdir/$outfile";
181 if (exists($things{$tracknum}->{'ARTIST'})) {
187 run_flac
(sprintf('track%02d.cdda.wav', $tracknum), undef, $outfile,
189 grep({ $_ !~ /^ARTIST=/ } @tags),
190 bork_tags
($things{$tracknum}));
191 $outlog = "../$outdir/log";
195 verbose
("Cleaning up $dir\n");
196 unlink('using-tags') or die("unlink(using-tags): $!");
197 unlink('cue') or die("unlink(cue): $!");
198 rename('log', $outlog)
199 or die("rename(log, $outlog): $!");
200 chdir('..') or die("chdir(..): $!");
202 if (-x
"$dir/post-processor") {
203 verbose
(join(' ', 'Running', @files), "\n");
204 system("./$dir/post-processor", @files);
205 unlink("$dir/post-processor") or die("unlink($dir/post-processor): $!");
208 rmdir($dir) or die("rmdir($dir): $!");
216 while (($pid = waitpid(-1, WNOHANG
)) > 0) {
217 push(@finished, [$pid, $?]);
220 $SIG{CHLD
} = \
&reaper
;
229 if (not defined($pid)) {
234 if ($debug or $pid == 0) {
235 $SIG{CHLD
} = 'DEFAULT';
236 open(STDERR
, ">$dir/log") or die("open(STDERR, >$dir/log): $!");
240 verbose
("new job $pid for $dir\n");
250 $pid = $finished[$i][0];
251 $status = $finished[$i][1];
253 verbose
("$pid finished (");
254 if (WIFEXITED
($status)) {
255 verbose
('exited with status ', WEXITSTATUS
($status));
256 } elsif (WIFSIGNALED
($status)) {
257 verbose
('killed with signal ', WTERMSIG
($status));
258 } elsif (WIFSTOPPED
($status)) {
259 verbose
('stopped with signal ', WSTOPSIG
($status));
263 for ($j = 0; $j <= $#jobs; $j++) {
264 $pid == $jobs[$j] and splice(@jobs, $j, 1) and last;
267 splice(@finished, $i, 1);
276 $SIG{CHLD
} = \
&reaper
;
278 if (scalar(@jobs) <= $MAXJOBS) {
279 foreach $i (glob('*/tags')) {
280 push(@jobs, newjob
(dirname
($i))) <= $MAXJOBS or last;
284 for ($i = 0; $i <= $#finished; $i++) {
288 verbose
(scalar(@jobs), " jobs\n");
299 'debug|X' => \
$debug,
300 'jobs|j=i' => \
$jobs,
301 'verbose|v' => \
$verbose,
302 'help|h|?' => \
$help,
304 $help and pod2usage
(-exitstatus
=>0, -verbose
=>1);
314 B<fa-flacd> and B<fa-rip> together comprise B<flac-archive>, a system
315 for archiving audio CDs to single FLAC files. B<fa-flacd> is the guts
316 of the system. It runs in the directory where the audio archives are
317 stored, scanning for new ripped CDs to encode and rename; it never
318 exits. B<fa-rip> generates the inputs for B<fa-flacd>: the ripped WAV
319 file, Vorbis tags, and a cuesheet.
321 Both programs expect to be run from the same directory. They use that
322 directory to manage directories named by artist. Intermediate files
323 are written to temporary directories here. B<fa-flacd> processes the
324 temporary directories into per-album files in the artist directories.
326 Every 5 seconds, B<fa-flacd> scans its current directory for
327 directories with a file called "tags" and creates a processing job for
328 each one. The number of jobs B<fa-flacd> attempts to run is
329 controlled by the B<-j> option and defaults to 4. B<fa-flacd> will
330 print diagnostic output when the B<-v> option is given.
332 A processing job first renames the directory's "tags" file to
333 "using-tags" so that B<ra-flacd> will not try to start another job for
334 this directory. This file is left as is when an error is encountered,
335 so a new job will not be started until the user corrects the error
336 condition and renames "using-tags" back to "tags". Next, it encodes
337 the "wav" file to a FLAC file, using the "cue" file for the cuesheet
338 and "using-tags" for Vorbis tags. Any diagnostic output is saved in
339 the "log" file. Finally, B<fa-flacd> moves the "cue" and "log" files
340 to the artist directory (named by album) and removes the temporary
343 If the temporary directory contains an executable file named
344 "post-processor", B<fa-flacd> executes that file with the relative
345 path to the output FLAC file as an argument. The output files are in
346 their final location when "post-processor" starts. Possible uses are
347 running B<flac2mp3>, moving the output files to a different location,
348 removing the lock file, or adding to a database. The standard input,
349 output, and error streams are inherited from B<fa-flacd>, so they may
350 be connected to anything from a tty to /dev/null. This means that you
351 may want to redirect these streams, if you want to save them or do any
358 =item B<-j> [B<--jobs>] I<jobs>
360 Run up to I<jobs> jobs instead of the default 4.
362 =item B<-v> [B<--verbose>]
364 Print diagnostic information.
370 Written by Eric Gillespie <epg@pretzelnet.org>.
372 flac-archive is free software; you may redistribute it and/or modify
373 it under the same terms as Perl itself.
378 # cperl-indent-level: 4
379 # perl-indent-level: 4
380 # indent-tabs-mode: nil
383 # vi: set tabstop=4 expandtab: