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