X-Git-Url: https://diplodocus.org/git/flac-archive/blobdiff_plain/956d1d13564d3a830d2ec2e817d95c521857ce3d..d177db7bfada46340eadca2a3d66155ad7cd9b6a:/fa-flacd diff --git a/fa-flacd b/fa-flacd index 69566ac..b10e0c9 100755 --- a/fa-flacd +++ b/fa-flacd @@ -13,6 +13,68 @@ B [B<-j> I] [B<-v>] =cut +package Jobs; + +use strict; +use warnings; + +use POSIX ':sys_wait_h'; + +our @jobs; +our @finished; + +sub reaper { + while ((my $pid = waitpid(-1, WNOHANG)) > 0) { + push(@finished, [$pid, $?]); + } + + # XXX if $pid == -1 handle errors? + + $SIG{CHLD} = \&reaper; +} + +sub newjob { + my $f = shift; + my %o = @_; + my $pid; + + $SIG{CHLD} = \&reaper; + if (not $o{'debug'}) { + $pid = fork(); + if (not defined($pid)) { + die("fork: $!"); + } + } + + if ($o{'debug'} or $pid == 0) { + $SIG{CHLD} = 'DEFAULT'; + exit($f->()); + } + + push(@jobs, $pid); + + return $pid; +} + +sub deljob { + my $i = shift; + my $j; + + my ($pid, $status) = @{$finished[$i]}; + + for ($j = 0; $j <= $#jobs; $j++) { + $pid == $jobs[$j] and splice(@jobs, $j, 1) and last; + } + + splice(@finished, $i, 1); + + return ($pid, $status); +} + + +################################################################################ +package main; + use strict; use warnings; @@ -23,8 +85,6 @@ use Pod::Usage; my $debug; my $verbose; -my @jobs; -my @finished; sub verbose { $verbose and print(STDERR $_) for @_; @@ -65,17 +125,7 @@ sub get_tags { return ($artist, $album, $discnum, @tags); } -# Hash of Hashes of Lists Push -sub hhlp { - my $hash = shift; - my $key1 = shift; - my $key2 = shift; - my $val = shift; - - return push(@{$hash->{$key1}->{$key2}}, $val); -} - -sub bork_tags { +sub track_tags { my $h = shift; my @result; @@ -88,6 +138,38 @@ sub bork_tags { return @result; } +sub run_flac { + my $infile = shift; + my $cue = shift; + my $outfile = shift; + + my @cue; + if (defined($cue)) { + @cue = ('--cuesheet', $cue); + } + + verbose("Running flac\n"); + my $status = system('flac', '-o', "$outfile.flac-tmp", + '--delete-input-file', '-V', '--no-padding', '--best', + @cue, + map({ ('-T', $_) } @_), + $infile); + if (WIFEXITED($status)) { + if (($status = WEXITSTATUS($status)) != 0) { + die("flac exited with status $status"); + } + } elsif (WIFSIGNALED($status)) { + die("flac killed with signal ", WTERMSIG($status)); + } elsif (WIFSTOPPED($status)) { + die("flac stopped with signal ", WSTOPSIG($status)); + } else { + die("Major horkage on system(flac): \$? = $? \$! = $!"); + } + + rename("$outfile.flac-tmp", "$outfile.flac") + or die("rename($outfile.flac-tmp, $outfile.flac): $!"); +} + # Process the fa-rip output in the directory DIR. sub flac { my $dir = shift; @@ -95,8 +177,11 @@ sub flac { my $album; my $discnum; my @tags; + my $single_file = -e "$dir/wav"; + my $outdir; my $outfile; - my $status; + my $outlog; + my @files; verbose("Renaming $dir/tags\n"); rename("$dir/tags", "$dir/using-tags") @@ -110,80 +195,81 @@ sub flac { verbose("mkdir($artist)\n"); -d $artist or mkdir($artist) or die("mkdir($artist): $!"); - my $outdir = "$artist/$album"; - verbose("mkdir($outdir)\n"); - -d "$outdir" or mkdir("$outdir") or die("mkdir($outdir): $!"); + if (not $single_file) { + $outdir = "$artist/$album"; + verbose("mkdir($outdir)\n"); + -d "$outdir" or mkdir("$outdir") or die("mkdir($outdir): $!"); + } verbose("chdir($dir)\n"); chdir($dir) or die("chdir($dir): $!"); - my @artist; - my %things; - my (@bork, @titles); - for my $tag (@tags) { - if ($tag =~ /^([^[]+)\[(\d+)]=(.*)/) { - hhlp(\%things, $2, $1, $3); - } elsif ($tag =~ /^ARTIST=/) { - push(@artist, $tag); - } else { - push(@bork, $tag); - } - } - @tags = @bork; - - my @files; - for my $tracknum (sort(map(int, keys(%things)))) { - my $title = join(' ', map(split, @{$things{$tracknum}->{'TITLE'}})); - $title =~ s|/|_|g; - $outfile = join(' ', - (defined($discnum) - ? sprintf('%02d', $discnum) - : ()), - sprintf('%02d', $tracknum), - $title); - push(@files, "$outdir/$outfile.flac"); - $outfile = "../$outdir/$outfile"; - - my @lartist; - if (exists($things{$tracknum}->{'ARTIST'})) { - @lartist = (); - } else { - @lartist = @artist; - } - - verbose("Running flac\n"); - $status = system('flac', '-o', "$outfile.flac-tmp", - '--delete-input-file', '-V', '--no-padding', '--best', - map({ ('-T', $_) } - @lartist, - grep({ $_ !~ /^ARTIST=/ } @tags), - bork_tags($things{$tracknum})), - sprintf('track%02d.cdda.wav', $tracknum)); - if (WIFEXITED($status)) { - if (($status = WEXITSTATUS($status)) != 0) { - die("flac exited with status $status"); + if ($single_file) { + $outfile = $album; + defined($discnum) and $outfile .= " (disc $discnum)"; + run_flac('wav', 'cue', "../$artist/$outfile", @tags); + $outlog = "../$artist/$outfile.log"; + @files = ("$artist/$outfile.flac"); + } else { + # Go over @tags, store all [n] tags in a list keyed by n in + # %tracks_to_tags, store all ARTIST (not ARTIST[n]) tags in + # @disc_artist, and leave the rest in @tags. + my %tracks_to_tags; + my @disc_artist; + my @tmp; + for my $tag (@tags) { + if ($tag =~ /^([^[]+)\[(\d+)]=(.*)/) { + push(@{$tracks_to_tags{$2}->{$1}}, $3); + } elsif ($tag =~ /^ARTIST=/) { + push(@disc_artist, $tag); + } else { + push(@tmp, $tag); } - } elsif (WIFSIGNALED($status)) { - die("flac killed with signal ", WTERMSIG($status)); - } elsif (WIFSTOPPED($status)) { - die("flac stopped with signal ", WSTOPSIG($status)); - } else { - die("Major horkage on system(flac): \$? = $? \$! = $!"); } + @tags = @tmp; + + for my $tracknum (sort(map(int, keys(%tracks_to_tags)))) { + my $title = join(' ', map(split, @{$tracks_to_tags{$tracknum}->{'TITLE'}})); + $title =~ s|/|_|g; + $outfile = join('/', + $outdir, + join(' ', + (defined($discnum) + ? sprintf('%02d', $discnum) + : ()), + sprintf('%02d', $tracknum), + $title)); + + # If we have ARTIST[n] tags for this track, set + # @track_artist to the empty list; they will go in along + # with the other [n] tags. + my @track_artist; + if (exists($tracks_to_tags{$tracknum}->{'ARTIST'})) { + @track_artist = (); + } else { + @track_artist = @disc_artist; + } - rename("$outfile.flac-tmp", "$outfile.flac") - or die("rename($outfile.flac-tmp, $outfile.flac): $!"); + run_flac(sprintf('track%02d.cdda.wav', $tracknum), undef, + "../$outfile", + @track_artist, + @tags, + "TRACKNUMBER=$tracknum", + track_tags($tracks_to_tags{$tracknum})); + push(@files, "$outfile.flac"); + } + $outlog = "../$outdir/log"; } verbose("Cleaning up $dir\n"); unlink('using-tags') or die("unlink(using-tags): $!"); unlink('cue') or die("unlink(cue): $!"); - rename('log', "../$outdir/log") - or die("rename(log, ../$outdir/log): $!"); + rename('log', $outlog) + or die("rename(log, $outlog): $!"); chdir('..') or die("chdir(..): $!"); if (-x "$dir/post-processor") { - verbose("Running './$dir/post-processor'\n"); + verbose(join(' ', "Running ./$dir/post-processor", @files), "\n"); system("./$dir/post-processor", @files); unlink("$dir/post-processor") or die("unlink($dir/post-processor): $!"); } @@ -193,79 +279,38 @@ sub flac { return 0; } -sub reaper { - my $pid; - - while (($pid = waitpid(-1, WNOHANG)) > 0) { - push(@finished, [$pid, $?]); - } - - $SIG{CHLD} = \&reaper; -} - -sub newjob { - my $dir = shift; - my $pid; - - if (not $debug) { - $pid = fork(); - if (not defined($pid)) { - die("fork: $!"); - } - } - - if ($debug or $pid == 0) { - $SIG{CHLD} = 'DEFAULT'; - open(STDERR, ">$dir/log") or die("open(STDERR, >$dir/log): $!"); - exit(flac($dir)); - } - - verbose("new job $pid for $dir\n"); - return $pid; -} - -sub deljob { - my $i = shift; - my $j; - my $pid; - my $status; - - $pid = $finished[$i][0]; - $status = $finished[$i][1]; - - verbose("$pid finished ("); - if (WIFEXITED($status)) { - verbose('exited with status ', WEXITSTATUS($status)); - } elsif (WIFSIGNALED($status)) { - verbose('killed with signal ', WTERMSIG($status)); - } elsif (WIFSTOPPED($status)) { - verbose('stopped with signal ', WSTOPSIG($status)); - } - verbose(")\n"); - - for ($j = 0; $j <= $#jobs; $j++) { - $pid == $jobs[$j] and splice(@jobs, $j, 1) and last; - } - - splice(@finished, $i, 1); -} - sub flacloop { my $MAXJOBS = shift; my $i; - my $j; $SIG{CHLD} = \&reaper; while (1) { if (scalar(@jobs) <= $MAXJOBS) { foreach $i (glob('*/tags')) { - push(@jobs, newjob(dirname($i))) <= $MAXJOBS or last; + my $dir = dirname($i); + my $pid = + Jobs::newjob(sub { + open(STDERR, ">$dir/log") + or die("open(STDERR, >$dir/log): $!"); + return flac($dir); + }, 'debug'=>$debug); + verbose("new job $pid for $dir\n"); + @Jobs::jobs <= $MAXJOBS or last; } } for ($i = 0; $i <= $#finished; $i++) { - deljob($i); + my ($pid, $status) = Jobs::deljob($i); + verbose("$pid finished ("); + if (WIFEXITED($status)) { + verbose('exited with status ', WEXITSTATUS($status)); + } elsif (WIFSIGNALED($status)) { + verbose('killed with signal ', WTERMSIG($status)); + } elsif (WIFSTOPPED($status)) { + verbose('stopped with signal ', WSTOPSIG($status)); + } + verbose(")\n"); } verbose(scalar(@jobs), " jobs\n");