Added check on return status of handbrake process, and reports failure to cluster client. Fixed a bug whereby handbrake processes were not reaped after the rip completes
227 lines
6.7 KiB
Perl
Executable File
227 lines
6.7 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
use warnings;
|
|
use strict;
|
|
|
|
use Data::Dumper;
|
|
use Gearman::Client;
|
|
use Getopt::Long;
|
|
use Log::Handler;
|
|
use MIME::Lite::TT::HTML;
|
|
use Pod::Usage;
|
|
use Storable qw/freeze thaw/;
|
|
use YAML::Any;
|
|
|
|
# Handle interrupts, and term signals
|
|
$SIG{'INT'} = 'INT_handler';
|
|
$SIG{'TERM'} = 'TERM_handler';
|
|
|
|
# Globals
|
|
our %default_options = (
|
|
verbose => 0,
|
|
debug => 0,
|
|
quiet => 0,
|
|
log_file => '/tmp/handbrake-cluster-client.log',
|
|
silent => 0,
|
|
help => 0,
|
|
pretend => 0,
|
|
job_servers => ['build0.sihnon.net', 'build1.sihnon.net', 'build2.sihnon.net'],
|
|
report_email => '',
|
|
config_file => '',
|
|
);
|
|
our %options = map { $_ => undef } keys %default_options;
|
|
|
|
our %rip_options = (
|
|
nice => 15,
|
|
input_filename => '/dev/sr0',
|
|
output_filename => 'rip-output.mkv',
|
|
title => 0,
|
|
format => 'mkv',
|
|
video_codec => 'x264',
|
|
video_width => 720, # DVD resolution
|
|
video_height => 576, # DVD resolution
|
|
quantizer => 0.61,# x264 quantizer = 20
|
|
deinterlace => 0, # 0 = off, 1 = on, 2 = selective
|
|
audio_tracks => 1,
|
|
audio_codec => 'ac3',
|
|
audio_names => 'English',
|
|
subtitle_tracks => 1,
|
|
);
|
|
|
|
Getopt::Long::Configure( qw(bundling no_getopt_compat) );
|
|
GetOptions(
|
|
'verbose|v+' => \$options{verbose},
|
|
'debug|d' => \$options{debug},
|
|
'quiet|q' => \$options{quiet},
|
|
'silent|s' => \$options{silent},
|
|
'log|l=s' => \$options{log_file},
|
|
'help|h' => \$options{help},
|
|
'pretend|n' => \$options{pretend},
|
|
'job-servers|j=s@' => \$options{job_servers},
|
|
'report|r=s' => \$options{report_email},
|
|
'config|c=s' => \$options{config_file},
|
|
'nice|N=i' => \$rip_options{nice},
|
|
'input|i=s' => \$rip_options{input_filename},
|
|
'output|o=s' => \$rip_options{output_filename},
|
|
'title|t=s' => \$rip_options{title},
|
|
'format|f=s' => \$rip_options{format},
|
|
'video-encoder|e=s' => \$rip_options{video_codec},
|
|
'width|w=i' => \$rip_options{video_width},
|
|
'height|l=i' => \$rip_options{video_height},
|
|
'quantizer|Q=f' => \$rip_options{quantizer},
|
|
'deinterlate|D=i' => \$rip_options{deinterlace},
|
|
'audio-tracks|a=s' => \$rip_options{audio_tracks},
|
|
'audio-encoder|E=s' => \$rip_options{audio_codec},
|
|
'audio-name|A=s' => \$rip_options{audio_names},
|
|
'subtitle-tracks|S=s' => \$rip_options{subtitle_tracks},
|
|
) or pod2usage(-verbose => 0);
|
|
pod2usage(-verbose => 1) if ($options{help});
|
|
|
|
my @jobs;
|
|
my $config;
|
|
# Read a configuration file, if provided
|
|
if ($options{config_file}) {
|
|
$config = parse_config($options{config_file});
|
|
push @jobs, @{ $config->{jobs} };
|
|
}
|
|
|
|
# A list of jobs to run, formed from command line options or config file
|
|
if ($rip_options{title}) {
|
|
push @jobs, \%rip_options;
|
|
}
|
|
|
|
# Setup logging
|
|
my $log = Log::Handler->new();
|
|
my $maxLogLevel = ($options{debug} ? 'debug' : ($options{quiet} ? 3 : $options{verbose} + 4)); # default to logging warning+, unless quiet in which case log error+, or debug in which case log everything
|
|
my $maxFileLogLevel = ($options{debug} ? 'debug' : 'info');
|
|
|
|
if ( ! $options{silent}) {
|
|
$log->add(
|
|
screen => {
|
|
log_to => 'STDOUT',
|
|
minlevel => 'emergency',
|
|
maxlevel => $maxLogLevel,
|
|
},
|
|
);
|
|
}
|
|
if ( $options{log_file}) {
|
|
$log->add(
|
|
file => {
|
|
filename => $options{log_file},
|
|
minlevel => 'emergency',
|
|
maxlevel => $maxFileLogLevel,
|
|
},
|
|
);
|
|
}
|
|
|
|
# Setup the distribution client
|
|
my $client = Gearman::Client->new;
|
|
$client->job_servers($options{job_servers});
|
|
|
|
# Add new ripping task for each job to run
|
|
my $taskset = $client->new_task_set;
|
|
foreach my $job (@jobs) {
|
|
$taskset->add_task("rip", freeze($job),
|
|
{
|
|
on_complete => \&on_complete_handler,
|
|
on_fail => \&on_fail_handler,
|
|
}
|
|
);
|
|
}
|
|
$taskset->wait;
|
|
|
|
sub on_complete_handler {
|
|
my $result_ref = shift or die;
|
|
|
|
return on_fail_handler() unless defined $$result_ref;
|
|
|
|
if ($options{report_email}) {
|
|
my $email = MIME::Lite::TT::HTML->new(
|
|
From => $options{report_email},,
|
|
To => $options{report_email},
|
|
Subject => 'Rip completed',
|
|
TimeZone => 'Europe/London',
|
|
Encoding => 'quoted-printable',
|
|
Template => {
|
|
html => 'rip-completed.html',
|
|
text => 'rip-completed.txt',
|
|
},
|
|
Charset => 'utf8',
|
|
TmplOptions => {},
|
|
TmplParams => {},
|
|
);
|
|
$email->send;
|
|
}
|
|
|
|
$log->notice("Completed rip to $$result_ref");
|
|
}
|
|
|
|
sub on_fail_handler {
|
|
$log->error("Rip failed");
|
|
}
|
|
|
|
sub parse_config {
|
|
my $config_file = shift or die;
|
|
my $config = YAML::Any::LoadFile($options{config_file}) or die 'Unable to load configuration file: ' . $!;
|
|
|
|
# Update any unset options with values from config file
|
|
foreach my $option (keys %options) {
|
|
# Update the value of any option which has not been set by a command line argument
|
|
if (!$options{$option}) {
|
|
# Try the config file first, otherwise use the default value
|
|
if (defined $config->{options}->{$option}) {
|
|
$options{$option} = $config->{options}->{$option};
|
|
} else {
|
|
$options{$option} = $default_options{$option};
|
|
}
|
|
}
|
|
}
|
|
|
|
# Iterate through each job, and inject any preset variables that haven't been redefined by the job
|
|
foreach my $job (@{ $config->{jobs} }) {
|
|
if ($job->{presets}) {
|
|
foreach my $preset_name (@{ $job->{presets} }) {
|
|
foreach my $preset_key (keys %{$config->{presets}->{$preset_name}}) {
|
|
$job->{$preset_key} = $config->{presets}->{$preset_name}->{$preset_key} unless $job->{$preset_key};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $config;
|
|
}
|
|
|
|
|
|
sub INT_handler {
|
|
$log->error("Caught interrupt, exiting.");
|
|
exit 0;
|
|
}
|
|
|
|
sub TERM_handler {
|
|
$log->error("Caught SIGTERM, exiting.");
|
|
exit 0;
|
|
}
|
|
|
|
|
|
__END__;
|
|
=head1 NAME
|
|
|
|
handbrake-cluster-client - Sets up DVD ripping tasks to be distributed
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
handbrake-cluster-client.pl -h
|
|
handbrake-cluster-client.pl [-v [-d]|-q|-s]
|
|
[-l logfile]
|
|
[-n]
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Sets up one or more DVD ripping tasks, and hands them off to gearman for
|
|
distribution. Logging and ripping configuration can be controlled with
|
|
command line arguments.
|
|
|
|
=head1 OPTIONS
|
|
|
|
=cut
|