]> diplodocus.org Git - flac-archive/blobdiff - flac2mp3
Fix a job in Jobs::run (@jobs < $maxjobs, not <=) and icky icky
[flac-archive] / flac2mp3
index ab83c9f9528110eeab7a55fcc6b7ba8e10d63ea0..842f0cd8041e9e61eb554258c0c50bc44ef13a6e 100755 (executable)
--- a/flac2mp3
+++ b/flac2mp3
@@ -8,7 +8,7 @@ B<flac2mp3> - transcode FLAC file to MP3 files
 
 =head1 SYNOPSIS
 
-B<flac2mp3> [B<--lame-options> I<lame-options>] [B<-q>] [B<-v>] I<file> [...]
+B<flac2mp3> [B<--lame-options> I<lame-options>] [B<-j> I<jobs>] [B<-q>] [B<-v>] I<file> [...]
 
 =head1 DESCRIPTION
 
@@ -19,6 +19,107 @@ ARTIST, ALBUM, and DATE tags.
 
 =cut
 
+package Jobs;
+
+use strict;
+use warnings;
+
+use Errno;
+use POSIX ':sys_wait_h';
+
+sub newjob {
+    my $f = shift;
+    my $jobs = shift;
+    my $debug = shift;
+    my $pid;
+
+    if (not $debug) {
+        $pid = fork();
+        if (not defined($pid)) {
+            die("fork: $!");
+        }
+    }
+
+    if ($debug or $pid == 0) {
+        exit($f->());
+    }
+
+    if ($pid == 0) {
+        exit($f->());
+    }
+
+    push(@$jobs, $pid);
+
+    return $pid;
+}
+
+sub deljob {
+    my $pid = shift;
+    my $status = shift;
+    my $jobs = shift;
+
+    for (my $i = 0; $i <= $#$jobs; $i++) {
+        if ($pid == $jobs->[$i]) {
+            splice(@$jobs, $i, 1);
+            last;
+        }
+    }
+
+    return ($pid, $status);
+}
+
+sub run {
+    my %o = @_;
+    my $maxjobs = $o{'max-jobs'};
+    my $get_job = $o{'get-job'};
+    my $notify_start = $o{'notify-start'};
+    my $notify_finish = $o{'notify-finish'};
+    my @jobs;
+    my $pid;
+
+    # Call notifier function if given.
+    sub call {
+        my $f = shift or return;
+        ref($f) eq 'CODE' or return;
+        $f->(@_);
+    }
+
+    while (1) {
+        if (@jobs < $maxjobs) {
+            my $job;
+            while (defined($job = $get_job->())) {
+                $pid = newjob($job, \@jobs, $o{'debug'});
+                call($notify_start, $pid, @jobs);
+                @jobs < $maxjobs or last;
+            }
+
+            # No jobs running and get-job returned undef; we're finished.
+            if (@jobs == 0 and not defined($job)) {
+                return;
+            }
+        }
+
+        # Now running as many jobs as we can, block waiting for one to die.
+        do {
+            $pid = waitpid(-1, 0);
+        } while ($pid == 0
+                 or ($pid == -1 and ($!{ECHILD} or $!{EINTR})));
+        $pid == -1 and die("waitpid(-1): $!");
+
+        # Before starting more, see if any others have finished.
+        do {
+            call($notify_finish, deljob($pid, $?, \@jobs), @jobs);
+        } while (($pid = waitpid(-1, WNOHANG)) > 0);
+        if ($pid == -1) {
+            $!{ECHILD} or $!{EINTR} or die("waitpid(-1): $!");
+        }
+    }
+}
+
+\f
+################################################################################
+package main;
+
 use strict;
 use warnings;
 
@@ -135,6 +236,11 @@ sub get_tags {
     }
     close(TAGS) or die("close(metaflac --export-vc-to=- $fn): $?");
 
+    # If no TITLEs, stick a dummy in here.
+    if (@$titles == 0) {
+        push(@$titles, undef);
+    }
+
     return ($artist, $album, $date, $discnum, $track);
 }
 
@@ -151,10 +257,10 @@ sub arg {
 
 sub flac2mp3 {
     my $fn = shift;
-    my $title = shift;
-    my $artist = shift;
-    my $album = shift;
-    my $date = shift;
+    my $title = (shift or 'unknown');
+    my $artist = (shift or 'unknown');
+    my $album = (shift or 'unknown');
+    my $date = (shift or 'unknown');
     my $track = int(shift);
     my $skip_arg = shift;
     my $until_arg = shift;
@@ -201,7 +307,11 @@ sub flac2mp3 {
 
 MAIN: {
     my $help;
+    my $debug;
+    my $maxjobs = 1;
     GetOptions(
+               'debug|X' => \$debug,
+               'jobs|j=i' => \$maxjobs,
                'lame-options=s', \$lame_options,
                'quiet|q' => \$quiet,
                'verbose|v' => \$verbose,
@@ -209,7 +319,9 @@ MAIN: {
               ) or pod2usage();
     $help and pod2usage(-exitstatus=>0, -verbose=>1);
 
-    @ARGV or pod2usage();
+    @ARGV > 0 or pod2usage();
+
+    my @jobs;
     for my $fn (@ARGV) {
         my @args = get_decode_args($fn);
         my (@artists, @titles);
@@ -226,11 +338,18 @@ MAIN: {
         $track ||= 1;
 
         for my $i (0..$#titles) {
-            flac2mp3($fn, $titles[$i], ($artists[$i] or $artist), $album, $date,
-                     $track, @{$args[$i]});
+            push(@jobs, [$fn, $titles[$i], ($artists[$i] or $artist), $album,
+                         $date, $track, @{$args[$i]}]);
             $track = $i + 2;
         }
     }
+
+    Jobs::run('max-jobs'=>$maxjobs,
+              'debug'=>$debug,
+              'get-job'=>sub {
+                  my $job = shift(@jobs) or return;
+                  return sub { flac2mp3(@$job) }
+              });
 }
 
 \f
@@ -246,6 +365,10 @@ Pass I<lame-options> to B<lame>.  This ends up being passed to the
 shell, so feel free to take advantage of that.  You'll almost
 certainly have to put I<lame-options> in single quotes.
 
+=item B<-j> [B<--jobs>] I<jobs>
+
+Run up to I<jobs> jobs instead of the default 1.
+
 =item B<-q> [B<--quiet>]
 
 Suppress status information.  This option is passed along to B<flac>