2 # rip_movie uses HandBrakeCLI to rip movies, and is released
3 # under the terms of the GPL version 2, or any later version, at your
4 # option. See the file README and COPYING for more information.
5 # Copyright 2011 by Don Armstrong <don@donarmstrong.com>.
6 # $Id: perl_script 1432 2009-04-21 02:42:41Z don $
17 rip_movie - Rips Movies using HandBrakeCLI
24 --device Device to rip from (default /dev/dvd)
25 --output Output filename
26 --min-duration Minimum duration in minutes for episode
27 --titles Optional titles to rip; overrides title autodetection
28 --debug, -d debugging level (Default 0)
29 --help, -h display this help
30 --man, -m display manual
38 Device to rip from (default /dev/dvd)
42 Override the output filename
44 =item B<--min-duration>
46 Minimum duration in minutes for a track to be considered an episode
51 Optional titles to rip; overrides title autodetection
55 Debug verbosity. (Default 0)
59 Display brief usage information.
73 use List::Util qw(reduce);
77 my %options = (debug => 0,
87 'min_duration|min-duration=i',
88 'debug|d+','help|h|?','man|m');
90 pod2usage() if $options{help};
91 pod2usage({verbose=>2}) if $options{man};
93 $DEBUG = $options{debug};
98 if (exists $options{min_duration}) {
99 $options{min_duration}*=60;
101 if (exists $options{titles}) {
102 for my $title (@{$options{titles}}) {
104 push @USAGE_ERRORS,"Title '$title' is not a valid title; must be greater than zero.";
106 push @titles_to_rip,$title;
110 pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
113 my $result = qx(HandBrakeCLI -i $options{device} -t 0 -o /dev/null 2>&1);
115 #print STDERR $result;
116 my ($title) = $result =~ /^libdvdnav:\s+DVD\s+Title:\s+(.+)/im;
117 die "no title" if not defined $title;
118 my $filename = lc($title);
119 $filename =~ s/[\s_-]+/_/;
124 for my $title_bit ($result =~ /\n(\+ title \d+:.+?)(?=\n\+ title\s+|$)/gs) {
127 # $title{bit} = $title_bit;
128 $title_bit =~ /^\+ title (\d+):/;
130 my ($duration) = $title_bit =~ /^\s+\+\s+duration:\s+([\d:]+)$/m;
131 # HH:MM:SS, so (HH*60 + MM) * 60 + SS
132 $duration = reduce {defined $a and defined $b ? $a * 60 + $b : undef } split /:/, $duration;
133 $title{duration} = $duration;
134 # figure out audio tracks
136 $title_bit =~ /(?<leadspace>\ +)\+\ +audio\ +tracks:\s*\n # audio track header
137 (?<audiotracks>(?:\g{leadspace}\ +\+\ +\d+\,.+\n){0,}) # audio track information
139 my $at_info = $+{audiotracks};
140 # $title{at_info} = $at_info;
141 my @at_nums = $at_info =~ /^\s+\+\s+(\d+)\,/mg if defined $at_info;
142 $title{audio_tracks} = [@at_nums];
143 # figure out subtitle tracks
144 $title_bit =~ /(?<leadspace>\ +)\+\ +subtitle\ +tracks:\s*\n # subtitle track header
145 (?<subtittracks>(?:\g{leadspace}\ +\+\ +\d+\,.+(?:\n|$)){0,}) # subtitle track information
147 my $st_info = $+{subtittracks};
148 # $title{st_info} = $st_info;
149 my @st_nums = $st_info =~ /^\s+\+\s+(\d+)\,/mg if defined $st_info;
150 $title{subtitle_tracks} = [@st_nums];
151 $title{pos_num} = @titles;
152 push @titles,\%title;
157 #print STDERR Dumper(\@titles);
160 if (not @titles_to_rip) {
162 for my $i (0..$#titles) {
163 my $title = $titles[$i];
164 if (not defined $longest_title or
165 $title->{duration} > $titles[$longest_title]{duration}) {
168 if ($title->{duration} >= $options{min_duration}) {
169 push @titles_to_rip,$i+1;
172 if (not @titles_to_rip) {
173 push @titles_to_rip,$longest_title+1;
177 my $invalid_titles = 0;
178 for my $i (@titles_to_rip) {
179 if ($i > @titles or $i < 1) {
180 print STDERR "Invalid title $i\n";
184 exit 1 if $invalid_titles;
186 my $multiple_titles = @titles_to_rip > 1;
188 #print STDERR Dumper(\@titles_to_rip,[@titles[map {$_} @titles_to_rip]]);
194 for my $i (@titles_to_rip) {
195 system('HandBrakeCLI','-i',$options{device},'-t',$titles[$i-1]{num},
196 '-o',$filename.($multiple_titles ? '_'.$titles[$i-1]{num} : '').".mkv",
197 '-e','x264','-2','-q','21',
198 '-a',join(',',@{$titles[$i-1]{audio_tracks}}),
199 '-s',join(',',@{$titles[$i-1]{subtitle_tracks}}),