#!/usr/bin/perl use strict; $0 =~ s/.*\///; $| = 1; use Getopt::Mixed; my $INTERRUPTED = 0; my $OUTPUT = ""; my $ASPECT = "auto"; my $TITLE = 'auto'; my $FILE = 'dvd://'; my $VBITRATE = 1600; my $ABITRATE = 192; my $VOLUME = 20; my $WIDTH = 800; my $TWOPASS = 'no'; my $DEBUG = 'no'; my $LANG = 'en'; my $SLANG = 'none'; my $SID = -1; my $FRAMESCAN = 500; my $VERBOSE = 0; my @CLIP = ('auto'); my $OVC = 'divx'; my $OAC = 'copy'; my $DEINT = 'no'; my $TV = 'no'; undef($ENV{DISPLAY}); $SIG{INT} = sub { $INTERRUPTED = 1; }; $SIG{QUIT} = sub { $INTERRUPTED = 1; }; sub error($) { print(STDERR "\e[1m\e[31m$0: $_[0]\n"); print(STDERR "Try `rip --help' for more information.\e[0m\n"); exit 1; } sub info { print("\e[1m\e[32m" . $_[0] . "\e[0m"); } sub usage() { print( "usage: $0 [] This is a wrapper for the DVD ripper `mencoder'. --help This help -t|--title Rip title ($TITLE) -f|--file Rip from file ($FILE) -a|--aspect Use aspect ratio ($ASPECT) -w|--width Specify output width ($WIDTH) -v|--volume Volume adjustment, in decibles (-200 to +40) ($VOLUME) --vrate Video recoding bitrate ($VBITRATE) --arate Audio recoding bitrate ($ABITRATE) --prescan Number of frames to pre-scan for clipping region size and aspect ratio. ($FRAMESCAN) -2|--twopass Use higher quality two-pass mode. This is useful for action movies with high change deltas. ($TWOPASS) --lang Select audio language ($LANG) --slang Select subtitle language ($SLANG) --debug Debug mode ($DEBUG) --clip Specify clipping region (" . join(":", @CLIP) . ") --codec Specify video CODEC to use ($OVC) --acodec Specify audio CODEC to use ($OAC) --deinterlace Deinterlace ($DEINT) --tv Record from TV ($TV) eg. Rip the longest title on a DVD to foobar.avi: rip foobar.avi Rip title 10 to foobar.avi: rip --title 10 foobar.avi "); exit 1; } Getopt::Mixed::init( "t=i title>t f=s file>f a=s aspect>a w=i width>w v=i volume>v debug prescan=i vrate=i arate=i clip=s lang=s slang=s 2 twopass>2 codec=s acodec=s deinterlace=s help"); while (my ($option, $value, $extra) = Getopt::Mixed::nextOption()) { if ($option eq "t") { $TITLE = $value; } elsif ($option eq "f") { $FILE = $value; } elsif ($option eq 'deinterlace') { $DEINT = 'no'; } elsif ($option eq "a") { $ASPECT = $value; error("invalid aspect ratio, should be in the form n:m") unless $ASPECT =~ /^[0-9]+:[0-9]+/; } elsif ($option eq "w") { $WIDTH = $value; } elsif ($option eq "v") { $VOLUME = $value; } elsif ($option eq "vrate") { $VBITRATE = $value; } elsif ($option eq "arate") { $ABITRATE = $value; } elsif ($option eq "2") { $TWOPASS = 'yes'; } elsif ($option eq 'prescan') { $FRAMESCAN = $value; } elsif ($option eq 'debug') { $DEBUG = 'yes'; } elsif ($option eq 'clip') { @CLIP = split(/:/, $value); } elsif ($option eq 'lang') { $LANG = $value; } elsif ($option eq 'slang') { $SLANG = $value; } elsif ($option eq 'codec') { $OVC = $value; } elsif ($option eq 'acodec') { $OAC = $value; } elsif ($option eq "help") { usage; } else { error("unhandled option '$option'"); } } undef $TITLE unless ($FILE eq 'dvd://'); Getopt::Mixed::cleanup(); $OUTPUT = $ARGV[0] if @ARGV; error("file name not specified") if $OUTPUT eq ""; #print( #" #Rip parameters: # Filename: $OUTPUT # Title: $TITLE # Two-pass: $TWOPASS # Aspect: $ASPECT # Width: $WIDTH # Volume: $VOLUME # Video bitrate: $VBITRATE # Audio bitrate: $ABITRATE # #"); # Find correct title to rip (longest)... if ($TITLE eq 'auto') { my $TITLES = 999; my %TITLETIMES = (); my ($LTITLE, $LTIME) = (-1, -1); info("Finding longest title\n"); for ($TITLE = 1; $TITLE <= $TITLES; ++$TITLE) { open(P, "mplayer dvd://$TITLE -frames 0 -identify 2>&1 |") || error("couldn't do -identify via mplayer"); while (

) { if (/There are (\d+) titles/) { $TITLES = $1; } elsif (/ID_LENGTH=(\d+)/) { $TITLETIMES{$TITLE} = $1; info(" Title $TITLE/$TITLES is $1 seconds long"); if ($1 > 3600) { info(" (looks good)"); } info("\n"); if ($1 > $LTIME) { $LTITLE = $TITLE; $LTIME = $1; } } } close(P); } $TITLE = $LTITLE; info("Chose title $TITLE (" . sprintf("%i", $LTIME / 60 / 60) . ":" . sprintf("%02i", ($LTIME / 60) % 60) . ")\n"); } # Find available subtitle languages if ($LANG ne 'none') { info("Finding available languages\n"); my $CMD = "mencoder '$FILE$TITLE' -v"; info("$CMD\n") if $DEBUG eq 'yes'; open(P, "$CMD 2>&1 |") || error("couldn't find subtitles via mencoder?"); my @AVAILSL; my @AVAILL; my $ALANG = 'none'; LOOP: while (

) { if (/^\[open\] subtitle \( sid \): (\d+) language: (..)$/) { push(@AVAILSL, $2); $SID = $1 if $2 eq $SLANG or $1 eq $SLANG; } elsif (/^\[open\] audio stream.*language: (..)/) { push(@AVAILL, $1); $ALANG = $1 if $1 eq $LANG; } } error("Couldn't find sub-titles in language '$SLANG'.\n Available sub-title languages are:\n @AVAILSL") if $SLANG ne 'none' and $SID == -1; error("Couldn't find audio language '$LANG'.\n Available audio languages are:\n @AVAILL") if $ALANG eq 'none'; close(P); } info(" Chose $LANG for audio, $SLANG for sub-titles\n"); # Find clipping region and/or aspect ratio if ($CLIP[0] eq 'auto' || $ASPECT eq 'auto') { my $CLIPFIND = 0; my $FOUND = 0; $CLIPFIND = 1 if ($CLIP[0] eq 'auto'); @CLIP = (0, 0, 999, 999) if ($CLIP[0] eq 'auto'); info("Finding clipping region and aspect ratio\n"); # Find aspect ratio my $CMD = "mencoder '$FILE$TITLE' -ovc raw -vop cropdetect,scale -zoom -xy $WIDTH -oac copy -frames $FRAMESCAN -o /dev/null"; $CMD .= " -alang $LANG" if $LANG ne 'none'; info("$CMD\n") if $DEBUG eq 'yes'; open(P, "$CMD 2>&1 |") || error("couldn't find clipping region via mencoder"); LOOP: while (

) { print($_) if ($DEBUG eq 'yes'); if (/^VIDEO:\s+(\w+)\s+(\d+)x(\d+)/) { info(" Video format is $1 with dimensions $2 x $3\n"); } elsif (/^Movie-Aspect is ([\d.]+):([\d.]+)/) { info(" Aspect ratio is $1:$2\n"); if ($ASPECT eq "auto") { info(" Auto-adjusting aspect ratio\n"); $ASPECT = "$1:$2" } else { my @AS = split(/:/, $ASPECT); my $AR = sprintf("%0.2f", $AS[0] / $AS[1]); if ($AR != $1 / $2) { info(" WARNING: Movie aspect ratio (" . ($1 / $2) . ") does not agree with user aspect ratio (" . ($AR) . ")\n"); } else { info(" User supplied aspect ratio agrees with DVD\n"); } } $FOUND |= 1; } elsif (/-(?:vop|vf) crop=(\d+):(\d+):(\d+):(\d+)/) { $FOUND |= 2; if ($CLIPFIND) { if ($1 > $CLIP[0] || $2 > $CLIP[1] || $3 < $CLIP[2] || $4 < $CLIP[3]) { info("\r\e[2K Adjusting cropping region to $3+$1 hor, $4+$2 vert..."); @CLIP = ($1, $2, $3, $4); } } else { last LOOP; } } } error("Couldn't find clipping region or aspect. Possible reasons for this are:\n - No DVD in the drive\n - Your mencoder is broken\n") if $FOUND != 3; print("ok\n"); close(P); } unlink("frameno.avi") if -e 'frameno.avi'; # Rip movie my $ARGS = "-af volume=$VOLUME:sc -aspect $ASPECT '$FILE$TITLE' -vop crop=" . join(":", @CLIP) . ",scale -zoom -xy $WIDTH -o \"$OUTPUT\""; $ARGS .= " -alang $LANG" if $LANG ne 'none'; if ($OAC eq 'mp3') { $ARGS .= " -oac mp3lame -lameopts cbr:preset=$ABITRATE"; } elsif ($OAC eq 'copy') { $ARGS .= " -oac copy"; } else { error("supported codecs are mp3 and copy"); } if ($OVC eq 'divx') { $ARGS .= " -ovc lavc -lavcopts vcodec=mpeg4:vhq:vbitrate=$VBITRATE"; $ARGS =~ s/-vop /-vop lavcdeint,/ if $DEINT eq 'yes'; } elsif ($OVC eq 'xvid') { $ARGS = "$ARGS -ovc xvid -xvidencopts me_quality=6:4mv:bitrate=$VBITRATE"; # Multi-pass? $ARGS =~ s/(:4mv:)/\1pass=2:/ if $TWOPASS eq 'yes'; } else { error("supported video codecs are xvid and divx"); } # Subtitles? $ARGS = "$ARGS -sid $SID -subfont-blur 6 -subfont-outline 1 -subfont-text-scale 3" if $SLANG ne 'none'; if ($OVC eq 'divx' and $TWOPASS eq 'yes') { for (my $pass = 1; $pass < 3; ++$pass) { info("Encoder pass $pass\n"); $ARGS =~ s/(vcodec=mpeg4:)/\1vpass=$pass:/; my $CMD = "mencoder $ARGS"; info("$CMD\n") if $DEBUG eq 'yes'; system($CMD); } unlink("divx2pass.log"); } else { my $CMD = "mencoder $ARGS"; info("$CMD\n") if $DEBUG eq 'yes'; exit(1) if system($CMD) != 0; } system("eject&") unless $INTERRUPTED;