From 3ea5aa5706f6edfff7db95c403a29c2b0222101c Mon Sep 17 00:00:00 2001 From: Ben Roberts Date: Wed, 17 Feb 2010 01:13:58 +0000 Subject: [PATCH] Batch processing and config file support 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. --- .gitignore | 1 + config.yml | 40 ++++++++++++++++ handbrake-cluster-client.pl | 92 +++++++++++++++++++++++++++++++++---- handbrake-cluster-worker.pl | 9 +++- rip-completed.html | 1 + rip-completed.txt | 1 + 6 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 .gitignore create mode 100644 config.yml create mode 100644 rip-completed.html create mode 100644 rip-completed.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a01ee28 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.*.swp diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..77c4f03 --- /dev/null +++ b/config.yml @@ -0,0 +1,40 @@ +--- +options: + verbose: 1 + log_file: /tmp/handbrake-cluster-client.log + job_servers: + - build0.example.com + - build1.example.com + report_email: me@example.com + +presets: + tvseries: + nice: 15 + format: mkv + video_codec: x264 + video_width: 720 + video_height: 480 + quantizer: 0.61 + deinterlace: 2 + audio_tracks: 1 + audio_codec: ac3 + audio_names: English + subtitle_tracks: 1,5 + disk1: + input_filename: /dev/sr1 + disk2: + input_filename: /tmp/dvds/disk2/VIDEO_TS + +jobs: + - presets: + - tvseries + - disk1 + output_filename: /tmp/title1.mkv + title: 1 + + - presets: + - tvseries + - disk2 + output_filename: /tmp/title2.mkv + title: -1 + diff --git a/handbrake-cluster-client.pl b/handbrake-cluster-client.pl index 97202a0..544efe2 100755 --- a/handbrake-cluster-client.pl +++ b/handbrake-cluster-client.pl @@ -7,28 +7,35 @@ 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 %options = ( +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 => -1, + title => 0, format => 'mkv', video_codec => 'x264', video_width => 720, # DVD resolution @@ -51,6 +58,8 @@ GetOptions( '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}, @@ -68,6 +77,19 @@ GetOptions( ) 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 @@ -96,19 +118,39 @@ if ( $options{log_file}) { my $client = Gearman::Client->new; $client->job_servers($options{job_servers}); -# Add new ripping task +# Add new ripping task for each job to run my $taskset = $client->new_task_set; -$taskset->add_task("rip", freeze(\%rip_options), - { - on_complete => \&on_complete_handler, - on_fail => \&on_fail_handler, - } -); +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"); } @@ -116,6 +158,38 @@ 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."); diff --git a/handbrake-cluster-worker.pl b/handbrake-cluster-worker.pl index ab10991..29d5e6d 100755 --- a/handbrake-cluster-worker.pl +++ b/handbrake-cluster-worker.pl @@ -87,13 +87,20 @@ sub do_rip { push @deinterlace_options, '-d' if ($rip_options{deinterlace} == 1); push @deinterlace_options, '-5' if ($rip_options{deinterlace} == 2); + my @title_options; + if ($rip_options{title} < 0) { + push @title_options, '-L'; + } else { + push @title_options, '-t', $rip_options{title}; + } + # Generate the command line for handbrake my @handbrake_cmd = ( 'nice', '-n', $rip_options{nice}, $options{handbrake}, '-i', $rip_options{input_filename}, '-o', $rip_filename, - '-t', $rip_options{title}, + @title_options. '-f', $rip_options{format}, '-e', $rip_options{video_codec}, '-q', $rip_options{quantizer}, diff --git a/rip-completed.html b/rip-completed.html new file mode 100644 index 0000000..0b2757b --- /dev/null +++ b/rip-completed.html @@ -0,0 +1 @@ +A rip has completed successfully diff --git a/rip-completed.txt b/rip-completed.txt new file mode 100644 index 0000000..0b2757b --- /dev/null +++ b/rip-completed.txt @@ -0,0 +1 @@ +A rip has completed successfully