]>
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>]
21 use POSIX
':sys_wait_h';
27 while ((my $pid = waitpid(-1, WNOHANG
)) > 0) {
28 push(@finished, [$pid, $?]);
31 # XXX if $pid == -1 handle errors?
33 $SIG{CHLD
} = \
&reaper
;
41 $SIG{CHLD
} = \
&reaper
;
42 if (not $o{'debug'}) {
44 if (not defined($pid)) {
49 if ($o{'debug'} or $pid == 0) {
50 $SIG{CHLD
} = 'DEFAULT';
63 my ($pid, $status) = @{$finished[$i]};
65 for ($j = 0; $j <= $#jobs; $j++) {
66 $pid == $jobs[$j] and splice(@jobs, $j, 1) and last;
69 splice(@finished, $i, 1);
71 return ($pid, $status);
75 ################################################################################
82 use Getopt
::Long
qw(:config gnu_getopt no_ignore_case);
83 use POSIX
':sys_wait_h';
90 $verbose and print(STDERR
$_) for @_;
93 # Return the ARTIST, ALBUM, and DATE followed by a list of all the
94 # lines in the file FN.
104 verbose
("Opening tags file $fn\n");
105 open(TAGS
, $fn) or die("open($fn): $!");
110 ($tag, $value) = split(/=/, $_, 2);
114 verbose
("ARTIST $artist from $fn\n");
115 } elsif (/^ALBUM=/i) {
117 verbose
("ALBUM $album from $fn\n"); # cperl-mode sucks "
118 } elsif (/^DISCNUMBER=/i) {
119 $discnum = int($value);
120 verbose
("DISCNUMBER $discnum from $fn\n");
123 close(TAGS
) or die("close($fn): $!");
125 return ($artist, $album, $discnum, @tags);
132 while (my ($key, $vall) = each(%$h)) {
133 for my $val (@$vall) {
134 push(@result, "$key=$val")
148 @cue = ('--cuesheet', $cue);
151 verbose
("Running flac\n");
152 my $status = system('flac', '-o', "$outfile.flac-tmp",
153 '--delete-input-file', '-V', '--no-padding', '--best',
155 map({ ('-T', $_) } @_),
157 if (WIFEXITED
($status)) {
158 if (($status = WEXITSTATUS
($status)) != 0) {
159 die("flac exited with status $status");
161 } elsif (WIFSIGNALED
($status)) {
162 die("flac killed with signal ", WTERMSIG
($status));
163 } elsif (WIFSTOPPED
($status)) {
164 die("flac stopped with signal ", WSTOPSIG
($status));
166 die("Major horkage on system(flac): \$? = $? \$! = $!");
169 rename("$outfile.flac-tmp", "$outfile.flac")
170 or die("rename($outfile.flac-tmp, $outfile.flac): $!");
173 # Process the fa-rip output in the directory DIR.
180 my $single_file = -e
"$dir/wav";
186 verbose
("Renaming $dir/tags\n");
187 rename("$dir/tags", "$dir/using-tags")
188 or die("rename($dir/tags, $dir/using-tags): $!");
190 ($artist, $album, $discnum, @tags) = get_tags
("$dir/using-tags");
191 for ($artist, $album) {
195 verbose
("mkdir($artist)\n");
196 -d
$artist or mkdir($artist) or die("mkdir($artist): $!");
198 if (not $single_file) {
199 $outdir = "$artist/$album";
200 verbose
("mkdir($outdir)\n");
201 -d
"$outdir" or mkdir("$outdir") or die("mkdir($outdir): $!");
204 verbose
("chdir($dir)\n");
205 chdir($dir) or die("chdir($dir): $!");
209 defined($discnum) and $outfile .= " (disc $discnum)";
210 run_flac
('wav', 'cue', "../$artist/$outfile", @tags);
211 $outlog = "../$artist/$outfile.log";
212 @files = ("$artist/$outfile.flac");
214 # Go over @tags, store all [n] tags in a list keyed by n in
215 # %tracks_to_tags, store all ARTIST (not ARTIST[n]) tags in
216 # @disc_artist, and leave the rest in @tags.
220 for my $tag (@tags) {
221 if ($tag =~ /^([^[]+)\[(\d+)]=(.*)/) {
222 push(@{$tracks_to_tags{$2}->{$1}}, $3);
223 } elsif ($tag =~ /^ARTIST=/) {
224 push(@disc_artist, $tag);
231 for my $tracknum (sort(map(int, keys(%tracks_to_tags)))) {
232 my $title = join(' ', map(split, @{$tracks_to_tags{$tracknum}->{'TITLE'}}));
238 ? sprintf('%02d', $discnum)
240 sprintf('%02d', $tracknum),
243 # If we have ARTIST[n] tags for this track, set
244 # @track_artist to the empty list; they will go in along
245 # with the other [n] tags.
247 if (exists($tracks_to_tags{$tracknum}->{'ARTIST'})) {
250 @track_artist = @disc_artist;
253 run_flac
(sprintf('track%02d.cdda.wav', $tracknum), undef,
257 "TRACKNUMBER=$tracknum",
258 track_tags
($tracks_to_tags{$tracknum}));
259 push(@files, "$outfile.flac");
261 $outlog = "../$outdir/log";
264 verbose
("Cleaning up $dir\n");
265 unlink('using-tags') or die("unlink(using-tags): $!");
266 unlink('cue') or die("unlink(cue): $!");
267 rename('log', $outlog)
268 or die("rename(log, $outlog): $!");
269 chdir('..') or die("chdir(..): $!");
271 if (-x
"$dir/post-processor") {
272 verbose
(join(' ', "Running ./$dir/post-processor", @files), "\n");
273 system("./$dir/post-processor", @files);
274 unlink("$dir/post-processor") or die("unlink($dir/post-processor): $!");
277 rmdir($dir) or die("rmdir($dir): $!");
287 $SIG{CHLD
} = \
&reaper
;
289 if (scalar(@jobs) <= $MAXJOBS) {
290 foreach $i (glob('*/tags')) {
291 my $dir = dirname
($i);
294 open(STDERR
, ">$dir/log")
295 or die("open(STDERR, >$dir/log): $!");
298 verbose
("new job $pid for $dir\n");
299 @Jobs::jobs
<= $MAXJOBS or last;
303 for ($i = 0; $i <= $#finished; $i++) {
304 my ($pid, $status) = Jobs
::deljob
($i);
305 verbose
("$pid finished (");
306 if (WIFEXITED
($status)) {
307 verbose
('exited with status ', WEXITSTATUS
($status));
308 } elsif (WIFSIGNALED
($status)) {
309 verbose
('killed with signal ', WTERMSIG
($status));
310 } elsif (WIFSTOPPED
($status)) {
311 verbose
('stopped with signal ', WSTOPSIG
($status));
316 verbose
(scalar(@jobs), " jobs\n");
327 'debug|X' => \
$debug,
328 'jobs|j=i' => \
$jobs,
329 'verbose|v' => \
$verbose,
330 'help|h|?' => \
$help,
332 $help and pod2usage
(-exitstatus
=>0, -verbose
=>1);
342 B<fa-flacd> and B<fa-rip> together comprise B<flac-archive>, a system
343 for archiving audio CDs to single FLAC files. B<fa-flacd> is the guts
344 of the system. It runs in the directory where the audio archives are
345 stored, scanning for new ripped CDs to encode and rename; it never
346 exits. B<fa-rip> generates the inputs for B<fa-flacd>: the ripped WAV
347 file, Vorbis tags, and a cuesheet.
349 Both programs expect to be run from the same directory. They use that
350 directory to manage directories named by artist. Intermediate files
351 are written to temporary directories here. B<fa-flacd> processes the
352 temporary directories into per-album files in the artist directories.
354 Every 5 seconds, B<fa-flacd> scans its current directory for
355 directories with a file called "tags" and creates a processing job for
356 each one. The number of jobs B<fa-flacd> attempts to run is
357 controlled by the B<-j> option and defaults to 4. B<fa-flacd> will
358 print diagnostic output when the B<-v> option is given.
360 A processing job first renames the directory's "tags" file to
361 "using-tags" so that B<ra-flacd> will not try to start another job for
362 this directory. This file is left as is when an error is encountered,
363 so a new job will not be started until the user corrects the error
364 condition and renames "using-tags" back to "tags". Next, it encodes
365 the "wav" file to a FLAC file, using the "cue" file for the cuesheet
366 and "using-tags" for Vorbis tags. Any diagnostic output is saved in
367 the "log" file. Finally, B<fa-flacd> moves the "cue" and "log" files
368 to the artist directory (named by album) and removes the temporary
371 If the temporary directory contains an executable file named
372 "post-processor", B<fa-flacd> executes that file with the relative
373 path to the output FLAC file as an argument. The output files are in
374 their final location when "post-processor" starts. Possible uses are
375 running B<flac2mp3>, moving the output files to a different location,
376 removing the lock file, or adding to a database. The standard input,
377 output, and error streams are inherited from B<fa-flacd>, so they may
378 be connected to anything from a tty to /dev/null. This means that you
379 may want to redirect these streams, if you want to save them or do any
386 =item B<-j> [B<--jobs>] I<jobs>
388 Run up to I<jobs> jobs instead of the default 4.
390 =item B<-v> [B<--verbose>]
392 Print diagnostic information.
398 Written by Eric Gillespie <epg@pretzelnet.org>.
400 flac-archive is free software; you may redistribute it and/or modify
401 it under the same terms as Perl itself.
406 # cperl-indent-level: 4
407 # perl-indent-level: 4
408 # indent-tabs-mode: nil
411 # vi: set tabstop=4 expandtab: