#!/usr/bin/perl -w
use strict;
use Getopt::Std;
use Term::ReadLine;

# VERSION: 1.0
# 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 
# james@nontrivial.org. The rtvtools (http://www.avsforum.com/), transcode 
# 0.6.3 (http://www.theorie.physik.uni-goettingen.de/~ostreich/transcode/), and
# dvdauthor 0.6 (http://dvdauthor.sourceforge.net/) packages are required.
# If you use this code on copyrighted movies then your government probably
# considers you a dirty, smelly, hellbound pirate, and (especially in the USA) 
# you could face fines, confiscation of your computer(s) and/or imprisonment!
# *********** USE THIS CODE AT YOUR OWN RISK!! *************

my %Options;
my @Chapters;
my %ChapterNames;
my $Chapters = 1;
my $OptsOK = getopts("i:v:w:o:hkbd", \%Options);
my $WorkDir = $ENV{HOME} . '/video';
my $SourceDir = $ENV{HOME} . '/video/rtv';
my $MaxOutSize = 4482;
my $DVDVolume = "DVD";
my $Term = new Term::ReadLine 'Howdy';

if (!$OptsOK || $Options{h}) {
  print("\n  Usage: rtv2iso <options>\n");
  print("     -i <dir>  Source directory/device.        [HOME/video/rtv]\n");
  print("     -w <dir>  Working directory.              [HOME/video]\n");
  print("     -o <name> Name of the resulting ISO file. [0000.iso]\n");
  print("     -v <name> DVD volume name                 [DVD]\n"); 
  print("     -d Delete original video. (Could be bad!)\n");
  print("     -b Burn the dvd.                         \n");
  print("     -h This help.                            \n");
  print("     -k Do everything but create an image.    \n");

  exit(0);
}

$MaxOutSize = $MaxOutSize * 1048576;
if (defined $Options{w}) { 
  $WorkDir = $Options{w}; 
  $SourceDir = $WorkDir . '/rtv';
}
if (defined $Options{v}) { $DVDVolume = $Options{v}; }
if (defined $Options{i}) { $SourceDir = $Options{i}; }

# Clear the logs
RunThis("rm -rf $WorkDir/*.log");
open(LOG, ">$WorkDir/rtv2iso.log");
close(LOG);
open(LOG, ">$WorkDir/command.log");
close(LOG);

PrepFiles();
ConvertFiles();
if (!$Options{k}) { 
  MakeImageDir(); 
  MakeImage();
}

# Subroutines
sub MakeImage {
  LogThis("Creating DVD image...");
  my $Done = 0;
  my $CurrDir = $WorkDir . '/iso';
  for (my $Num = 0; $Num <= 9999; $Num++) { 
    if (!$Done) {
      my $Name = sprintf("%s/%04d.iso", $WorkDir, $Num);
      if ($Options{o}) { $Name = $Options{o}; }
      if ($Options{o} || !GetSize('iso', $Num, 0, 'iso')) {
	$Done = 1;
	RunThis("mkisofs -udf -dvd-video -V $DVDVolume -o $Name $CurrDir");
	my $IsoSize = GetSize($Name);
	if (!$IsoSize) {
	  LogThis("Unable to create file $Name! Aborting!",2);
	} else {
	  RunThis("chmod -Rf 755 $CurrDir");
	  RunThis("rm -rf $CurrDir");
	  $CurrDir = $WorkDir . '/out';
	  RunThis("rm -rf $CurrDir");
	  $CurrDir = $WorkDir . '/raw';
	  RunThis("rm -rf $CurrDir");
	  if (defined $Options{d}) {
	    $CurrDir = $WorkDir . '/rtv';
	    RunThis("rm -rf $CurrDir");
	  }
	  LogThis("DVD image file $Name of size $IsoSize has been created.");
	  if (defined $Options{b}) {
	    RunThis("dvdrecord -dao speed=1 dev=1,0,0 $Name");
	    LogThis("DVD image $Name has been burned.");
	  } else {
	    LogThis("To burn: dvdrecord -v -dao speed=1 dev=1,0,0 $Name",0);
	  }
	}
      }
    }
  }
}

sub MakeImageDir {
  my $OutDir = $WorkDir . '/out';
  my $CurrDir = $WorkDir . '/iso';
  LogThis("Preparing to make DVD image...");
  if (-e $CurrDir) {
    RunThis("chmod -Rf 755 $CurrDir");
    RunThis("rm -rf $CurrDir");
  }

  mkdir $CurrDir or LogThis("Could not create $CurrDir: $!",1);
  mkdir "$CurrDir/AUDIO_TS" or 
    LogThis("Could not create $CurrDir/AUDIO_TS: $!",1);
  mkdir "$CurrDir/VIDEO_TS" or 
    LogThis("Could not create $CurrDir/VIDEO_TS: $!",1);

  opendir(OUTDIR, $OutDir) or LogThis("Could not open $OutDir: $!",1);
  my @Files = readdir OUTDIR;
  closedir(OUTDIR);

  my $Names = '';
  for my $File (sort @Files) {
    if (length($File) > 4 && substr($File, -4) eq '.vob') {
      $Names = $Names . ' ' . sprintf("%s/%s", $OutDir, $File);
    }
  }

  RunThis("dvdauthor -o $CurrDir $Names"); 
  RunThis("dvdauthor -T -o $CurrDir");
  RunThis("chmod -f 500 $CurrDir/AUDIO_TS");
  RunThis("chmod -f 500 $CurrDir/VIDEO_TS");
  RunThis("chmod -f 400 $CurrDir/VIDEO_TS/*.*");
}

sub ConvertFiles {
  my $FakeIt = 0;
  my $CurrDir = $WorkDir . '/out';
  my $RawDir = $WorkDir . '/raw';
  LogThis("Converting files...");
  if (-e $CurrDir) {
    if (GetSize('out', 1, 0, 'vob')) {
      if (YesNo("y", "Converted files exists. Use them? (Y/n) ")) {
	$FakeIt = 1;
      } else {
	my $Files = sprintf("%s/*", $CurrDir);
	RunThis("rm -f $Files");
      }
    }
  } else {
    mkdir $CurrDir or LogThis("Could not create $CurrDir: $!",1);
  }
  
  chdir($CurrDir);
  my $TotalSize = GetSize('out', 1, 0, 'vob'); 
  for (my $Chapter = 1; $Chapter <= $Chapters; $Chapter++) {
    my $ThisSize = GetSize('raw', 1, $Chapter, 'mpg');
    if (($TotalSize + $ThisSize) < $MaxOutSize) {
      my $HackFile = sprintf("%s/%03d", $CurrDir, $Chapter);
      my $ThisFile = sprintf("%s/001.%03d", $RawDir, $Chapter);
      my $ThatFile = sprintf("%s/001.%03d", $CurrDir, $Chapter);
      LogThis("Trying to fit $ChapterNames{$Chapter} onto the DVD...");
      if (!$FakeIt) {
	RunThis("rtvconvert -d $ThisFile.mpg $HackFile");
	RunThis("mv $HackFile.m2v $ThatFile.m2v");
	RunThis("mv $HackFile.mp2 $ThatFile.mp2");
	#mplex seems to have a 2 Gig limit. -S0 should work but doesn't.
	#RunThis("mplex -S0 -f8 -b500 -o$ThatFile.vob $ThatFile.m2v $ThatFile.mp2");
	RunThis("tcmplex -md -o$ThatFile.vob -i$ThatFile.m2v -p$ThatFile.mp2");
	RunThis("rm -f *.m2v *.mp2");
      }
      push(@Chapters, "$ThatFile.vob");
      if (!GetSize('out', 1, $Chapter, 'vob')) {
	LogThis("There was a problem converting $ChapterNames{$Chapter}!",1);
      }
    } else {
      LogThis("Not trying to fit $ChapterNames{$Chapter} onto the DVD.");
    }
    $TotalSize = GetSize('out', 1, 0, 'vob'); 
    LogThis("$ChapterNames{$Chapter} has been added to the DVD.");
    LogThis("Current video size is $TotalSize out of $MaxOutSize.");
  }
}

sub PrepFiles {
  my $FakeIt = 0;
  my $CurrDir = $WorkDir . '/raw';
  LogThis("Preparing files to be converted...");
  if (-e $CurrDir) {
    if (GetSize('raw', 1, 0, 'mpg')) {
      if (YesNo("y", "Prepared files exists. Use them? (Y/n) ")) {
	$FakeIt = 1;
      } else {
	my $Files = sprintf("%s/*", $CurrDir);
	RunThis("rm -f $Files");
      }
    }
  } else {
    mkdir $CurrDir or LogThis("Could not create $CurrDir: $!",1);
  }
  
  opendir(SOURCEDIR, $SourceDir) or LogThis("Could not open $SourceDir: $!",1);
  my @Files = readdir SOURCEDIR;
  closedir(SOURCEDIR);

  chdir($SourceDir);
  for my $File (sort @Files) {
    if (length($File) > 4 && substr($File, -4) eq '.evt') {
      if (!$FakeIt) {
	my $ThatFile = sprintf("%s/001.%03d.mpg", $CurrDir, $Chapters);
	my $ThisFile = $SourceDir . '/' . $File;
	$ThisFile =~ s/ /\\ /g;
	RunThis("echo T$ThatFile > evtdump.txt");
	RunThis("evtdump -p1 $ThisFile >> evtdump.txt");
	RunThis("rtvedit evtdump.txt");
	RunThis("rm -f evtdump.txt");
      }
      $ChapterNames{$Chapters} = substr($File, 0, length($File) - 4);
      $Chapters = $Chapters + 1;
    }
  }
  $Chapters = $Chapters - 1;
  if ($Chapters == 0) { LogThis("Could not find any files to convert!",1); }
}

sub GetSize {
  my ($Directory, $Title, $Chapter, $Type) = @_;
  # Built in Perl file functions seem to choke on large files.
  
  my $Name;
  if (!$Type) { $Type = '*'; }
  if ($Directory eq 'iso' && $Type eq 'iso') {
    $Name = sprintf("%s/%04d.iso", $WorkDir, $Title);
  } elsif (!$Title && !$Chapter && $Type eq '*') {
    $Name = $Directory;
  } elsif (!$Title) {
    $Name = sprintf("%s/%s/*.%s", $WorkDir, $Directory, $Type);
  } elsif (!$Chapter) {
    $Name = sprintf("%s/%s/%03d.*.%s", $WorkDir, $Directory, $Title, $Type);
  } else {
    $Name = sprintf("%s/%s/%03d.%03d.%s", $WorkDir, 
		    $Directory, $Title, $Chapter, $Type);
  }

  my $Size = 0;
  my $NotThere = system("ls $Name 1>/dev/null 2>&1");
  if (!$NotThere) { 
    my @FileSizes = `du -sb $Name`;
    for my $FileSize (@FileSizes) {
      ($FileSize) = split(/\t/, $FileSize);
      $Size = $Size + $FileSize;
    } 
  }

  return $Size;
}

sub YesNo {
  my ($Default, $Question) = @_;

  my $Result;
  LogThis($Question);
  while (!$Result) {
    $Result = $Term->readline($Question);
    if (!$Result) { $Result = "$Default"; }
    if ($Result eq "Y" || $Result eq "y") { return 1; }
    if ($Result eq "N" || $Result eq "n") { return 0; }
    $Result = 0;
  }
}

sub RunThis {
  my ($Command) = @_;

  my @Date = localtime(time);
  my $Text = sprintf("\n%04d-%02d-%02d %02d:%02d:%02d RUNNING-> $Command\n", 
		     $Date[5] + 1900, $Date[4], $Date[3], $Date[2], $Date[1], 
		     $Date[0]);
  open(CMD, ">>$WorkDir/command.log");
  print(CMD $Text);
  close(CMD);
  open(OLDOUT, ">&STDOUT");
  open(OLDERR, ">&STDERR");
  close(STDOUT);
  close(STDERR);
  open(STDOUT, ">>$WorkDir/command.log");
  open(STDERR, ">>$WorkDir/command.log");
  system($Command);
  close(STDOUT);
  close(STDERR);
  open(STDOUT, ">&OLDOUT");
  open(STDERR, ">&OLDERR");
  close(OLDOUT);
  close(OLDERR);
}

sub LogThis {
  my ($Text, $Level) = @_;
  
  my @Date = localtime(time);
  $Text = sprintf("%04d-%02d-%02d %02d:%02d:%02d - $Text\n",
		     $Date[5] + 1900, $Date[4], $Date[3], $Date[2], $Date[1], 
		     $Date[0]);
  print($Text);
  open(LOG, ">>$WorkDir/rtv2iso.log");
  print(LOG $Text);
  close(LOG);
  if (defined $Level) {
    exit($Level);
  }
}
