#!/usr/bin/perl -w # dvd9to5.pl - v0.1.7 # This file is released under the GNU General Public License. All rights # reserved. Please check out http://www.gnu.org/copyleft/gpl.html before you # use this code. Minimal support may be provided by sending email to # Jason Cooper use strict; use Getopt::Std; my $VERSION="v0.1.7"; my @ChapterMarks; my $ChapList; my %Options; my $StartTime; my $StopTime; # User changeable vars my $DVDDir; my $WorkDir; my $LogName; my $HomeDir; my $DVDRipDev; my $DVDBrnDev; my $BurnSpd; my $TitleNum; my @AudioCH; my @SubCH; my $RequantFactor; my $MaxISOSize; # Config file locations. chdir; $HomeDir = `pwd`; $HomeDir =~ s/\n$//; my $etcConf_Loc = '/etc/dvd9to5.conf'; my $homeConf_Loc = $HomeDir . '/.dvd9to5.conf'; # Default values for user changeable vars my $dflt_DVDDir = 'dvd/VIDEO_TS'; my $dflt_WorkDir = '/var/tmp'; my $dflt_LogName = 'dvd9to5.log'; my $dflt_DVDRipDev = '/dev/dvd'; my $dflt_DVDBrnDev = '/dev/dvd'; my $dflt_BurnSpd = 2; my $dflt_TitleNum = 1; my @dflt_AudioCH; $dflt_AudioCH[0] = '0'; my @dflt_SubCH; my $dflt_RequantFactor = 1.0; my $dflt_MaxISOSize = 4706074624; #bytes, according to growisofs # Grab the Command Line Options my $CL_Opts = getopts("i:o:w:t:a:s:S:DIh", \%Options); if(!$CL_Opts || $Options{h}) { print("dvd9to5.pl $VERSION \n"); print("Usage: dvd9to5.pl \n"); print(" -i Source device. \n"); print(" -o Destination device. \n"); print(" -w Working directory. \n"); print(" -t Title # to use. \n"); print(" -a <0,1...n> Audio channel #'s to use. \n"); print(" -s <0,1...n> Subtitle channel #'s to use. \n"); print(" -S Burn Speed. \n"); print(" -D Delete unneeded files. \n"); # print(" -I Show DVD info and guesses. \n"); # print(""); # print(""); # print(""); print(" -h Print this help.\n"); exit(0); } sub LoadValues { # NOTE: Order of preference: # 1.) command line opts # 2.) ~/.dvd9to5.conf # 3.) /etc/dvd9to5.conf # 4.) hard coded defaults. # Setting hard coded defaults $WorkDir = $dflt_WorkDir; $DVDRipDev = $dflt_DVDRipDev; $DVDBrnDev = $dflt_DVDBrnDev; $BurnSpd = $dflt_BurnSpd; $TitleNum = $dflt_TitleNum; @AudioCH = @dflt_AudioCH; @SubCH = @dflt_SubCH; $MaxISOSize = $dflt_MaxISOSize; $DVDDir = $dflt_DVDDir; # Overwriting with "etc-config" my @args; if (-e $etcConf_Loc) { open(ETCCONF, "<$etcConf_Loc"); print("dvd9to5: Reading $etcConf_Loc\n"); while() { if($_ =~ m/^DVDRipDev/i) { @args = split; $DVDRipDev = $args[1]; } if($_ =~ m/^DVDBurnDev/i) { @args = split; $DVDBrnDev = $args[1]; } if($_ =~ m/^BurnSpd/i) { @args = split; $BurnSpd = $args[1]; } if($_ =~ m/^WorkDir/i) { @args = split; $WorkDir = $args[1]; } } close(ETCCONF); } else {print("dvd9to5: Config file $etcConf_Loc not found.\n");} # Overwriting with "user-config" if (-e $homeConf_Loc) { open(HOMECONF, "<$homeConf_Loc"); print("dvd9to5: Reading $homeConf_Loc\n"); while() { if($_ =~ m/^DVDRipDev/i) { @args = split; $DVDRipDev = $args[1]; } if($_ =~ m/^DVDBurnDev/i) { @args = split; $DVDBrnDev = $args[1]; } if($_ =~ m/^BurnSpd/i) { @args = split; $BurnSpd = $args[1]; } if($_ =~ m/^WorkDir/i) { @args = split; $WorkDir = $args[1]; } } close(HOMECONF); } else {print("dvd9to5: Config file $homeConf_Loc not found.\n");} # Overwriting with command line opts $DVDRipDev = $Options{i} if($Options{i}); $DVDBrnDev = $Options{o} if($Options{o}); $WorkDir = $Options{w} if($Options{w}); $BurnSpd = $Options{S} if($Options{S}); $TitleNum = $Options{t} if($Options{t}); @AudioCH = split /\,/, $Options{a} if($Options{a}); @SubCH = split /\,/, $Options{s} if($Options{s}); $WorkDir =~ s/\n$//; $WorkDir =~ s/\/$//; # Grab the dvd_name my $GetNameCmd = 'isoinfo -d -i ' . $DVDRipDev . ' | egrep "^Volume id"'; my $name = `$GetNameCmd`; my @words = split / /,$name; $name = $words[2]; $name =~ s/\n$// if($name); $WorkDir = $WorkDir . '/' . $name if($name); unless (-d $WorkDir) { my $MkDirCmd = 'mkdir -p ' . $WorkDir; system("$MkDirCmd"); } $LogName = $WorkDir . '/' . $dflt_LogName; if($Options{D}) { LogThis("WARNING: You are deleting files as you go. Very Dangerous!!!"); } } sub ExtractData { my $VideoCmd = 'tcextract -i vid.fifo -t vob -x mpeg2 > ofile.m2v &'; my $ChapsCmd = 'tcprobe -i ' . $DVDRipDev . ' -T ' . $TitleNum . ' -H 500 2>&1' . ' | egrep "\[Chapter ..\] "'; my $VidFifoCmd = 'mkfifo vid.fifo'; my $TeeList = 'vid.fifo '; my $RmFifoCmd = 'rm -f *.fifo'; chdir("$WorkDir"); LogThis("Changed to working directory [$WorkDir]."); LogThis("Grabbing chapter list."); my @ChapMarks = `$ChapsCmd`; for my $Line (@ChapMarks) { my @Items = split(/ /, $Line); if($Items[3] =~ /..\:..\:..\..../) { $Items[3] =~ s/\n$//; $ChapList .= $Items[3] . ','; } } $ChapList =~ s/,$//; RunThis("$VidFifoCmd"); RunThis("$VideoCmd"); my $fifoname; foreach(@AudioCH) { $fifoname = 'aud' . $_ . '.fifo'; $TeeList .= $fifoname . ' '; my $AudFifoCmd = 'mkfifo ' . $fifoname; my $AudioCmd = 'tcextract -i ' . $fifoname . ' -t vob -x ac3 -a ' . $_ . ' > ofile' . $_ . '.ac3 &'; RunThis("$AudFifoCmd"); RunThis("$AudioCmd"); }#foreach(@AudioCH) foreach(@SubCH) { $fifoname = 'sub' . $_ . '.fifo'; $TeeList .= $fifoname . ' '; my $SubFifoCmd = 'mkfifo ' . $fifoname; my $SubCmd = 'tcextract -i ' . $fifoname . ' -t vob -x ps1 -a ' . $_ . ' > ofile' . $_ . '.ps1 &'; RunThis("$SubFifoCmd"); RunThis("$SubCmd"); }#foreach(@SubCH) my $DumpCmd = 'tccat -i ' . $DVDRipDev . ' -T ' . $TitleNum . ',-1 -L | tee ' . $TeeList . ' >/dev/null'; RunThis("$DumpCmd"); LogThis("Done with source DVD, please remove it and insert a blank."); LogThis("Done extracting, cleaning up."); RunThis("$RmFifoCmd"); } sub RequantVideo { my $M2VName = 'ofile.m2v'; my $OutName = 'movie.m2v'; my $SizeSlop = 1.05; my $ret = 0; # Get file sizes my $M2VSize = GetSize("$M2VName"); my $AudSize; my $SubSize; foreach(@AudioCH) { my $audname = 'ofile' . $_ . '.ac3'; $AudSize += GetSize("$audname"); }#foreach(@AudioCH) foreach(@SubCH) { my $subname = 'ofile' . $_ . '.ps1'; $SubSize += GetSize("$subname"); }#foreach(@SubCH) #Calc Requant number. my $StreamSize = $SubSize + $AudSize; my $ReqNum = (($SizeSlop * $M2VSize) / ($MaxISOSize - $StreamSize)); my $ReqCmd = 'tcrequant -i ' . $M2VName . ' -o ' . $OutName . ' -f ' . $ReqNum; my $RmCmd = 'rm -f ofile.m2v'; if($ReqNum > 1.00) { LogThis("Requantizing with $ReqNum"); RunThis("$ReqCmd"); LogThis("Done requantizing."); if($Options{D}) { LogThis("Cleaning up."); RunThis("$RmCmd"); } $ret=1; } else { LogThis("Requantization not necessary, Continuing."); $ret=0; } return $ret; } sub MultiplexData { my $MplexCmd; my $RmOM2VCmd = 'rm -f ofile.m2v'; my $RmMM2VCmd = 'rm -f movie.m2v'; my $AudList; my $SubList; my $filename; foreach(@AudioCH) { $filename = 'ofile' . $_ . '.ac3 '; $AudList .= $filename; }#foreach(@AudioCH) foreach(@SubCH) { $filename = 'ofile' . $_ . '.ps1 '; $SubList .= $filename; }#foreach(@SubCH) my $RmAAC3Cmd = 'rm -f ' . $AudList; my $RmAPS1Cmd = 'rm -f ' . $SubList; if(RequantVideo()) { LogThis("Multiplexing requantized video with audio."); $MplexCmd = 'mplex -f 8 -S 0 -o movie.mpeg movie.m2v ' . $AudList . $SubList; } else { LogThis("Multiplexing original video with audio."); $MplexCmd = 'mplex -f 8 -S 0 -o movie.mpeg ofile.m2v ' . $AudList . $SubList; } RunThis("$MplexCmd"); if($Options{D}) { LogThis("Cleaning up."); if(-e 'ofile.m2v') { RunThis("$RmOM2VCmd"); } if(-e 'movie.m2v') { RunThis("$RmMM2VCmd"); } RunThis("$RmAAC3Cmd"); RunThis("$RmAPS1Cmd"); }#if($Options{D}) } sub AuthorDVD { my $AuthorChapCmd = 'dvdauthor -t -a ac3+en -c ' . $ChapList . ' -o dvd movie.mpeg'; my $AuthorTOCCmd = 'dvdauthor -T -o dvd'; my $MkDirCmd = 'mkdir -p ' . $DVDDir; my $RmMpegCmd = 'rm -f movie.mpeg'; RunThis("$MkDirCmd"); RunThis("$AuthorChapCmd"); RunThis("$AuthorTOCCmd"); if($Options{D}) { LogThis("Cleaning up."); RunThis("$RmMpegCmd"); } } sub MakeISO { my $STATE = "NO_JOY"; my $GrowISOCmd = 'growisofs -speed=' . $BurnSpd . ' -Z ' . $DVDBrnDev . ' -dvd-video dvd'; my $DVDReadyCmd = 'tcprobe -i ' . $DVDBrnDev . ' -H 500 2>&1'; my $RmDirCmd = 'rm -rf dvd'; my @Response = `$DVDReadyCmd`; for my $line (@Response) { if($line =~ /Can't open file VIDEO_TS/) { $STATE = "READY"; } } if($STATE eq "READY") { LogThis("Detected Blank DVD."); RunThis("$GrowISOCmd"); LogThis("Burned the Image to disc."); if($Options{D}) { LogThis("Cleaning up."); RunThis("$RmDirCmd"); } } else { LogThis("No blank in DVD drive, cannot continue."); LogThis("Use \'$GrowISOCmd\' to burn."); } } sub GetSize { my ($Filename) = @_; my $SizeCmd = 'du -sb ' . $Filename; my $String = `$SizeCmd`; my ($Size) = split(/\t/, $String); LogThis("$Filename is $Size bytes."); return $Size; } sub LogThis { my ($String) = @_; my @Date = localtime(time); my $LogStr = sprintf("%04d-%02d-%02d %02d:%02d:%02d LOGGING-> $String\n", $Date[5] + 1900, $Date[4] + 1, $Date[3], $Date[2], $Date[1], $Date[0]); if(-e $LogName) { open(CMDLOG, ">>$LogName"); } else { open(CMDLOG, ">$LogName"); } print("dvd9to5: $LogStr"); print(CMDLOG $LogStr); close(CMDLOG); } sub RunThis { my ($Command) = @_; my @Date = localtime(time); my $LogStr = sprintf("%04d-%02d-%02d %02d:%02d:%02d RUNNING-> $Command\n", $Date[5] + 1900, $Date[4] + 1, $Date[3], $Date[2], $Date[1], $Date[0]); if(-e $LogName) { open(CMDLOG, ">>$LogName"); } else { open(CMDLOG, ">$LogName"); } print("dvd9to5: $LogStr"); print(CMDLOG $LogStr); system("$Command") == 0 or die "dvd9to5: system $Command failed: $?"; if ($? == -1) { print "dvd9to5: failed to execute: $!\n"; } elsif ($? & 127) { printf "dvd9to5: child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without'; } close(CMDLOG); } LoadValues(); ExtractData(); MultiplexData(); print ("\nDIR AFTER MULTIPLEXING STAGE:\n"); RunThis ("ls -lA /downloads/tmp/"); print ("-----------------------------\n\n"); { my $junk = ; } AuthorDVD(); #MakeISO();