Added a config file to describe multiple jobs to run, and to provide values for options not specified on command line. In the config file, presets can be used to save duplication of rip options common to multiple jobs.
226 lines
6.7 KiB
Perl
Executable File
226 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;
|
|
|
|
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} }) {
|
|
print "Copying values for preset $preset_name into job\n";
|
|
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
|