]>
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");
154 # Go over @tags, store all [n] tags in a list keyed by n in
155 # %tracks_to_tags, store all ARTIST (not ARTIST[n]) tags in
156 # @disc_artist, and leave the rest in @tags.
160 for my $tag (@tags) {
161 if ($tag =~ /^([^[]+)\[(\d+)]=(.*)/) {
162 push(@{$tracks_to_tags{$2}->{$1}}, $3);
163 } elsif ($tag =~ /^ARTIST=/) {
164 push(@disc_artist, $tag);
171 for my $tracknum (sort(map(int, keys(%tracks_to_tags)))) {
172 my $title = join(' ', map(split, @{$tracks_to_tags{$tracknum}->{'TITLE'}}));
178 ? sprintf('%02d', $discnum)
180 sprintf('%02d', $tracknum),
183 # If we have ARTIST[n] tags for this track, set
184 # @track_artist to the empty list; they will go in along
185 # with the other [n] tags.
187 if (exists($tracks_to_tags{$tracknum}->{'ARTIST'})) {
190 @track_artist = @disc_artist;
193 run_flac
(sprintf('track%02d.cdda.wav', $tracknum), undef,
197 track_tags
($tracks_to_tags{$tracknum}));
198 push(@files, "$outfile.flac");
200 $outlog = "../$outdir/log";
203 verbose
("Cleaning up $dir\n");
204 unlink('using-tags') or die("unlink(using-tags): $!");
205 unlink('cue') or die("unlink(cue): $!");
206 rename('log', $outlog)
207 or die("rename(log, $outlog): $!");
208 chdir('..') or die("chdir(..): $!");
210 if (-x
"$dir/post-processor") {
211 verbose
(join(' ', "Running ./$dir/post-processor", @files), "\n");
212 system("./$dir/post-processor", @files);
213 unlink("$dir/post-processor") or die("unlink($dir/post-processor): $!");
216 rmdir($dir) or die("rmdir($dir): $!");
224 while (($pid = waitpid(-1, WNOHANG
)) > 0) {
225 push(@finished, [$pid, $?]);
228 $SIG{CHLD
} = \
&reaper
;
237 if (not defined($pid)) {
242 if ($debug or $pid == 0) {
243 $SIG{CHLD
} = 'DEFAULT';
244 open(STDERR
, ">$dir/log") or die("open(STDERR, >$dir/log): $!");
248 verbose
("new job $pid for $dir\n");
258 $pid = $finished[$i][0];
259 $status = $finished[$i][1];
261 verbose
("$pid finished (");
262 if (WIFEXITED
($status)) {
263 verbose
('exited with status ', WEXITSTATUS
($status));
264 } elsif (WIFSIGNALED
($status)) {
265 verbose
('killed with signal ', WTERMSIG
($status));
266 } elsif (WIFSTOPPED
($status)) {
267 verbose
('stopped with signal ', WSTOPSIG
($status));
271 for ($j = 0; $j <= $#jobs; $j++) {
272 $pid == $jobs[$j] and splice(@jobs, $j, 1) and last;
275 splice(@finished, $i, 1);
284 $SIG{CHLD
} = \
&reaper
;
286 if (scalar(@jobs) <= $MAXJOBS) {
287 foreach $i (glob('*/tags')) {
288 push(@jobs, newjob
(dirname
($i))) <= $MAXJOBS or last;
292 for ($i = 0; $i <= $#finished; $i++) {
296 verbose
(scalar(@jobs), " jobs\n");
307 'debug|X' => \
$debug,
308 'jobs|j=i' => \
$jobs,
309 'verbose|v' => \
$verbose,
310 'help|h|?' => \
$help,
312 $help and pod2usage
(-exitstatus
=>0, -verbose
=>1);
322 B<fa-flacd> and B<fa-rip> together comprise B<flac-archive>, a system
323 for archiving audio CDs to single FLAC files. B<fa-flacd> is the guts
324 of the system. It runs in the directory where the audio archives are
325 stored, scanning for new ripped CDs to encode and rename; it never
326 exits. B<fa-rip> generates the inputs for B<fa-flacd>: the ripped WAV
327 file, Vorbis tags, and a cuesheet.
329 Both programs expect to be run from the same directory. They use that
330 directory to manage directories named by artist. Intermediate files
331 are written to temporary directories here. B<fa-flacd> processes the
332 temporary directories into per-album files in the artist directories.
334 Every 5 seconds, B<fa-flacd> scans its current directory for
335 directories with a file called "tags" and creates a processing job for
336 each one. The number of jobs B<fa-flacd> attempts to run is
337 controlled by the B<-j> option and defaults to 4. B<fa-flacd> will
338 print diagnostic output when the B<-v> option is given.
340 A processing job first renames the directory's "tags" file to
341 "using-tags" so that B<ra-flacd> will not try to start another job for
342 this directory. This file is left as is when an error is encountered,
343 so a new job will not be started until the user corrects the error
344 condition and renames "using-tags" back to "tags". Next, it encodes
345 the "wav" file to a FLAC file, using the "cue" file for the cuesheet
346 and "using-tags" for Vorbis tags. Any diagnostic output is saved in
347 the "log" file. Finally, B<fa-flacd> moves the "cue" and "log" files
348 to the artist directory (named by album) and removes the temporary
351 If the temporary directory contains an executable file named
352 "post-processor", B<fa-flacd> executes that file with the relative
353 path to the output FLAC file as an argument. The output files are in
354 their final location when "post-processor" starts. Possible uses are
355 running B<flac2mp3>, moving the output files to a different location,
356 removing the lock file, or adding to a database. The standard input,
357 output, and error streams are inherited from B<fa-flacd>, so they may
358 be connected to anything from a tty to /dev/null. This means that you
359 may want to redirect these streams, if you want to save them or do any
366 =item B<-j> [B<--jobs>] I<jobs>
368 Run up to I<jobs> jobs instead of the default 4.
370 =item B<-v> [B<--verbose>]
372 Print diagnostic information.
378 Written by Eric Gillespie <epg@pretzelnet.org>.
380 flac-archive is free software; you may redistribute it and/or modify
381 it under the same terms as Perl itself.
386 # cperl-indent-level: 4
387 # perl-indent-level: 4
388 # indent-tabs-mode: nil
391 # vi: set tabstop=4 expandtab: