diff --git a/handbrake-cluster-client.pl b/handbrake-cluster-client.pl index 853ee5c..88c47e7 100755 --- a/handbrake-cluster-client.pl +++ b/handbrake-cluster-client.pl @@ -121,12 +121,25 @@ my $client = Gearman::Client->new; $client->job_servers($options->{job_servers}); # Add new ripping task for each job to run +my @running_tasks; my $taskset = $client->new_task_set; foreach my $job (@jobs) { - $taskset->add_task("handbrake_rip", freeze($job), + $taskset->add_task('handbrake_rip', freeze($job), { + on_status => sub { + my $numerator = shift; + my $denominator = shift or die; + + $log->notice("Ripping task at ", ($numerator/$denominator), "% complete"); + }, on_complete => \&on_complete_handler, - on_fail => \&on_fail_handler, + on_retry => sub { + my $attempt = shift or die; + $log->warning("Retrying rip"); + }, + on_fail => sub { + $log->warning("Rip failed"); + }, } ); } @@ -164,10 +177,6 @@ sub on_complete_handler { $log->notice("Completed rip to $response->{real_output_filename}"); } -sub on_fail_handler { - $log->error("Failed to distribute job"); -} - # Reads configuration options from a config file, expands the internal references, and returns the expanded form. sub parse_config { my $config_file = shift; diff --git a/handbrake-cluster-worker.pl b/handbrake-cluster-worker.pl index cfe59d6..cbd67f9 100755 --- a/handbrake-cluster-worker.pl +++ b/handbrake-cluster-worker.pl @@ -6,7 +6,7 @@ use strict; use Data::Dumper; use Gearman::Worker; use Getopt::Long; -use IPC::Open2; +use IPC::Open3; use Log::Handler; use Pod::Usage; use String::Random qw/random_string/; @@ -70,70 +70,85 @@ $worker->work while 1; sub handbrake_rip { my $job = shift; - my %rip_options = %{ thaw($job->arg) }; + my $rip_options = thaw($job->arg); - my %response; + my $response = {}; - $log->notice("Beginning new rip to $rip_options{output_filename}"); + $log->notice("Beginning new rip to $rip_options->{output_filename}"); # Generate a unique filename based on the output filename to prevent clashes from previous runs my $uuid = random_string('cccccc'); - $rip_options{real_output_filename} = $rip_options{output_filename}; - $rip_options{real_output_filename} =~ s/\.([^\.]+)$/\.$uuid\.$1/; - $response{real_output_filename} = $rip_options{real_output_filename}; + $rip_options->{real_output_filename} = $rip_options->{output_filename}; + $rip_options->{real_output_filename} =~ s/\.([^\.]+)$/\.$uuid\.$1/; + $response->{real_output_filename} = $rip_options->{real_output_filename}; # Generate the command line for handbrake my @handbrake_cmd = ( # Reduce the priority of the ripping process, since it is very processor intensive - 'nice', '-n', $rip_options{nice}, + 'nice', '-n', $rip_options->{nice}, # Construct the handbrake command line $options{handbrake}, - get_options(\%rip_options, 'input_filename', '-i'), - get_options(\%rip_options, 'real_output_filename', '-o'), - get_options(\%rip_options, 'title'), - get_options(\%rip_options, 'format', '-f'), - get_options(\%rip_options, 'video_codec', '-e'), - get_options(\%rip_options, 'quantizer', '-q'), - get_options(\%rip_options, 'video_width', '-w'), - get_options(\%rip_options, 'video_height', '-l'), - get_options(\%rip_options, 'deinterlace'), - get_options(\%rip_options, 'audio_tracks', '-a'), - get_options(\%rip_options, 'audio_codec', '-E'), - get_options(\%rip_options, 'audio_names', '-A'), - get_options(\%rip_options, 'subtitle_tracks', '-s'), + get_options($rip_options, 'input_filename', '-i'), + get_options($rip_options, 'real_output_filename', '-o'), + get_options($rip_options, 'title'), + get_options($rip_options, 'format', '-f'), + get_options($rip_options, 'video_codec', '-e'), + get_options($rip_options, 'quantizer', '-q'), + get_options($rip_options, 'video_width', '-w'), + get_options($rip_options, 'video_height', '-l'), + get_options($rip_options, 'deinterlace'), + get_options($rip_options, 'audio_tracks', '-a'), + get_options($rip_options, 'audio_codec', '-E'), + get_options($rip_options, 'audio_names', '-A'), + get_options($rip_options, 'subtitle_tracks', '-s'), ); # Return a copy of the command used to rip the title - $response{handbrake_cmd} = @handbrake_cmd; + $response->{handbrake_cmd} = @handbrake_cmd; + + # flag the start of the job + $job->set_status(0, 100); # Execute the ripping process $log->debug("Beginning ripping process with command:\n" . join(' ', @handbrake_cmd)); - my ($child_in, $child_out); - my $child_pid = open2($child_out, $child_in, @handbrake_cmd); + my ($child_in, $child_out, $child_err); + my $child_pid = open3($child_in, $child_out, $child_err, @handbrake_cmd); # No need to write to the child process close($child_in); # Log the output from handbrake, and return it back to the client - $response{log} = (); + $response->{log} = (); my $line; while ($line = <$child_out>) { - push @{ $response{log} }, $line; - $log->debug($line); + # If the line is a progress report, record the current status + # otherwise, log the line + + # Encoding: task 1 of 1, 0.87 % (34.71 fps, avg 62.95 fps, ETA 00h07m56s) + if ($line =~ m/Encoding: task \d+ of \d+, (\d+\.\d+) %/) { + my $numerator = $1 * 100; + $job->set_status($numerator, 100); + $log->notice("Task is $numerator% complete"); + } else { + push @{ $response->{log} }, $line; + $log->notice($line); + } } close($child_out); + $job->set_status(100, 100); + # If the rip process failed, report an error status here waitpid($child_pid, 0); - $response{status} = $? >> 8; - $response{success} = $response{status} == 0; - if ($response{success}) { - $log->notice("Finished rip to $rip_options{real_output_filename}"); + $response->{status} = $? >> 8; + $response->{success} = $response->{status} == 0; + if ($response->{success}) { + $log->notice("Finished rip to $response->{real_output_filename}"); } else { - $log->warning("Ripping process returned error status: $response{status}"); + $log->warning("Ripping process returned error status: $response->{status}"); } - return freeze(\%response); + return freeze($response); } sub get_options {