]>
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): $!");
288 if (scalar(@jobs) <= $MAXJOBS) {
289 foreach $i (glob('*/tags')) {
290 my $dir = dirname
($i);
293 open(STDERR
, ">$dir/log")
294 or die("open(STDERR, >$dir/log): $!");
297 verbose
("new job $pid for $dir\n");
298 @Jobs::jobs
<= $MAXJOBS or last;
302 for ($i = 0; $i <= $#finished; $i++) {
303 my ($pid, $status) = Jobs
::deljob
($i);
304 verbose
("$pid finished (");
305 if (WIFEXITED
($status)) {
306 verbose
('exited with status ', WEXITSTATUS
($status));
307 } elsif (WIFSIGNALED
($status)) {
308 verbose
('killed with signal ', WTERMSIG
($status));
309 } elsif (WIFSTOPPED
($status)) {
310 verbose
('stopped with signal ', WSTOPSIG
($status));
315 verbose
(scalar(@jobs), " jobs\n");
326 'debug|X' => \
$debug,
327 'jobs|j=i' => \
$jobs,
328 'verbose|v' => \
$verbose,
329 'help|h|?' => \
$help,
331 $help and pod2usage
(-exitstatus
=>0, -verbose
=>1);
341 B<fa-flacd> and B<fa-rip> together comprise B<flac-archive>, a system
342 for archiving audio CDs to single FLAC files. B<fa-flacd> is the guts
343 of the system. It runs in the directory where the audio archives are
344 stored, scanning for new ripped CDs to encode and rename; it never
345 exits. B<fa-rip> generates the inputs for B<fa-flacd>: the ripped WAV
346 file, Vorbis tags, and a cuesheet.
348 Both programs expect to be run from the same directory. They use that
349 directory to manage directories named by artist. Intermediate files
350 are written to temporary directories here. B<fa-flacd> processes the
351 temporary directories into per-album files in the artist directories.
353 Every 5 seconds, B<fa-flacd> scans its current directory for
354 directories with a file called "tags" and creates a processing job for
355 each one. The number of jobs B<fa-flacd> attempts to run is
356 controlled by the B<-j> option and defaults to 4. B<fa-flacd> will
357 print diagnostic output when the B<-v> option is given.
359 A processing job first renames the directory's "tags" file to
360 "using-tags" so that B<ra-flacd> will not try to start another job for
361 this directory. This file is left as is when an error is encountered,
362 so a new job will not be started until the user corrects the error
363 condition and renames "using-tags" back to "tags". Next, it encodes
364 the "wav" file to a FLAC file, using the "cue" file for the cuesheet
365 and "using-tags" for Vorbis tags. Any diagnostic output is saved in
366 the "log" file. Finally, B<fa-flacd> moves the "cue" and "log" files
367 to the artist directory (named by album) and removes the temporary
370 If the temporary directory contains an executable file named
371 "post-processor", B<fa-flacd> executes that file with the relative
372 path to the output FLAC file as an argument. The output files are in
373 their final location when "post-processor" starts. Possible uses are
374 running B<flac2mp3>, moving the output files to a different location,
375 removing the lock file, or adding to a database. The standard input,
376 output, and error streams are inherited from B<fa-flacd>, so they may
377 be connected to anything from a tty to /dev/null. This means that you
378 may want to redirect these streams, if you want to save them or do any
385 =item B<-j> [B<--jobs>] I<jobs>
387 Run up to I<jobs> jobs instead of the default 4.
389 =item B<-v> [B<--verbose>]
391 Print diagnostic information.
397 Written by Eric Gillespie <epg@pretzelnet.org>.
399 flac-archive is free software; you may redistribute it and/or modify
400 it under the same terms as Perl itself.
405 # cperl-indent-level: 4
406 # perl-indent-level: 4
407 # indent-tabs-mode: nil
410 # vi: set tabstop=4 expandtab: