]>
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 "TRACKNUMBER=$tracknum",
198 track_tags
($tracks_to_tags{$tracknum}));
199 push(@files, "$outfile.flac");
201 $outlog = "../$outdir/log";
204 verbose
("Cleaning up $dir\n");
205 unlink('using-tags') or die("unlink(using-tags): $!");
206 unlink('cue') or die("unlink(cue): $!");
207 rename('log', $outlog)
208 or die("rename(log, $outlog): $!");
209 chdir('..') or die("chdir(..): $!");
211 if (-x
"$dir/post-processor") {
212 verbose
(join(' ', "Running ./$dir/post-processor", @files), "\n");
213 system("./$dir/post-processor", @files);
214 unlink("$dir/post-processor") or die("unlink($dir/post-processor): $!");
217 rmdir($dir) or die("rmdir($dir): $!");
225 while (($pid = waitpid(-1, WNOHANG
)) > 0) {
226 push(@finished, [$pid, $?]);
229 $SIG{CHLD
} = \
&reaper
;
238 if (not defined($pid)) {
243 if ($debug or $pid == 0) {
244 $SIG{CHLD
} = 'DEFAULT';
245 open(STDERR
, ">$dir/log") or die("open(STDERR, >$dir/log): $!");
249 verbose
("new job $pid for $dir\n");
259 $pid = $finished[$i][0];
260 $status = $finished[$i][1];
262 verbose
("$pid finished (");
263 if (WIFEXITED
($status)) {
264 verbose
('exited with status ', WEXITSTATUS
($status));
265 } elsif (WIFSIGNALED
($status)) {
266 verbose
('killed with signal ', WTERMSIG
($status));
267 } elsif (WIFSTOPPED
($status)) {
268 verbose
('stopped with signal ', WSTOPSIG
($status));
272 for ($j = 0; $j <= $#jobs; $j++) {
273 $pid == $jobs[$j] and splice(@jobs, $j, 1) and last;
276 splice(@finished, $i, 1);
285 $SIG{CHLD
} = \
&reaper
;
287 if (scalar(@jobs) <= $MAXJOBS) {
288 foreach $i (glob('*/tags')) {
289 push(@jobs, newjob
(dirname
($i))) <= $MAXJOBS or last;
293 for ($i = 0; $i <= $#finished; $i++) {
297 verbose
(scalar(@jobs), " jobs\n");
308 'debug|X' => \
$debug,
309 'jobs|j=i' => \
$jobs,
310 'verbose|v' => \
$verbose,
311 'help|h|?' => \
$help,
313 $help and pod2usage
(-exitstatus
=>0, -verbose
=>1);
323 B<fa-flacd> and B<fa-rip> together comprise B<flac-archive>, a system
324 for archiving audio CDs to single FLAC files. B<fa-flacd> is the guts
325 of the system. It runs in the directory where the audio archives are
326 stored, scanning for new ripped CDs to encode and rename; it never
327 exits. B<fa-rip> generates the inputs for B<fa-flacd>: the ripped WAV
328 file, Vorbis tags, and a cuesheet.
330 Both programs expect to be run from the same directory. They use that
331 directory to manage directories named by artist. Intermediate files
332 are written to temporary directories here. B<fa-flacd> processes the
333 temporary directories into per-album files in the artist directories.
335 Every 5 seconds, B<fa-flacd> scans its current directory for
336 directories with a file called "tags" and creates a processing job for
337 each one. The number of jobs B<fa-flacd> attempts to run is
338 controlled by the B<-j> option and defaults to 4. B<fa-flacd> will
339 print diagnostic output when the B<-v> option is given.
341 A processing job first renames the directory's "tags" file to
342 "using-tags" so that B<ra-flacd> will not try to start another job for
343 this directory. This file is left as is when an error is encountered,
344 so a new job will not be started until the user corrects the error
345 condition and renames "using-tags" back to "tags". Next, it encodes
346 the "wav" file to a FLAC file, using the "cue" file for the cuesheet
347 and "using-tags" for Vorbis tags. Any diagnostic output is saved in
348 the "log" file. Finally, B<fa-flacd> moves the "cue" and "log" files
349 to the artist directory (named by album) and removes the temporary
352 If the temporary directory contains an executable file named
353 "post-processor", B<fa-flacd> executes that file with the relative
354 path to the output FLAC file as an argument. The output files are in
355 their final location when "post-processor" starts. Possible uses are
356 running B<flac2mp3>, moving the output files to a different location,
357 removing the lock file, or adding to a database. The standard input,
358 output, and error streams are inherited from B<fa-flacd>, so they may
359 be connected to anything from a tty to /dev/null. This means that you
360 may want to redirect these streams, if you want to save them or do any
367 =item B<-j> [B<--jobs>] I<jobs>
369 Run up to I<jobs> jobs instead of the default 4.
371 =item B<-v> [B<--verbose>]
373 Print diagnostic information.
379 Written by Eric Gillespie <epg@pretzelnet.org>.
381 flac-archive is free software; you may redistribute it and/or modify
382 it under the same terms as Perl itself.
387 # cperl-indent-level: 4
388 # perl-indent-level: 4
389 # indent-tabs-mode: nil
392 # vi: set tabstop=4 expandtab: