#!/usr/bin/perl

#------------------------------------------------------------------------------
# Tivo Video Extraction Script, 
# ----------------------------
# Version 0.2, October 2003, 
# (c) Daniel Drysdale (daniel@drysdale.org), licensed under the GPL,
# (see http://www.gnu.org/copyleft/gpl.html for details)
#
# The purpose of this script is to grab the list of programs in "Now Showing"
# from your Tivo, present them in a formatted list and allow you to easily
# nominate a program to be extracted to disk. The program is encoded in DivX
# format, with the audio left as is.
#
# You can select a bitrate for the encoding or use a default value, you can
# also select Single or Multipass encoding.
#
# This was written to ease the pain of extracting and encoding video from the
# Tivo, it is very "rough and ready" and may well fail completely on your
# system.
#
# No guarantee is made that this will be at all useful to you, but if you do
# find it useful let me know. 
# If you have any suggestions, by all means pass them on.
# If you add any features, fling me a copy so I can have a look at them.
#
# Audio is not re-encoded because I've found that I have problems with audio
# sync if I do, YMMV.
#
# Video encoded with this script is editable in applications such as avidemux2
# what you do with it is up to you.
#
# This script makes use of the perl "Expect" module available from www.cpan.org
# and of course tivo-mplayer ( http://tivo-mplayer.sourceforge.net/ )
#
# Appologies for my poor perl coding style and criminal lack of comments.. :)
#
# Revision History
# ----------------
#
# Date          Rev     Comments
# 15/10/2003    0.1     Initial Release
# 16/10/2003    0.2     Modified to use llist and exclude "Live" buffer, also
#                       fixed a few hard-coded Tivo IP's
#------------------------------------------------------------------------------

use Expect;

my $tivo_ip = "tivo";
my $default_bitrate = "1750";

my $command;
my @params;
my $exp = new Expect;
$exp->raw_pty(1);
$exp->log_stdout(0);

$command="mplayer";
push @params, "tivo://" . $tivo_ip . "/llist";
$exp->spawn($command, @params)
  or die "Cannot spawn $command: $!\n";

my $date;
my $fsid;
my $title;
my $episode;
my @dataset;

print "\n\nRetrieving Data.";

while( $exp->expect(10, '-re', '^\|') )
{
        my %record;

        print ".";

        $line=$exp->after();
        chomp $line;
        
        #
        # Records from mplayer look like this
        #
        #|10/14/03|07:30|Crossing Jordan|DEAD WIVES CLUB|623717|6|
        #
        # I map these to the following variables ( in order )
        # $date, undef, $title, $episode, $fsid, undef
        # undef discards the data.
        #

        ($date, undef, $title, $episode, $fsid, undef) = split(/\|/, $line);

        $fsid =~ s/ //g;
        $title =~ s/\s+$//;
        $episode =~ s/\s+$//;
        $episode =~ s/\(null\)//;

        $record{'fsid'} = $fsid;
        $record{'date'} = $date;
        $record{'title'} = $title;
        $record{'episode'} = $episode;

#        print "fsid($fsid) title($title) episode($episode)\n";

        # Live buffer has a date that ends in '69', we should exclude this from the List
        if ( $date =~ /69/ )   { 
                next;
        } 

        push @dataset, \%record;
}

print "\n\nItem No\tDate\t\tTitle\t\t\t\tEpisode\n";
print
"----------------------------------------------------------------------------------------------------------\n";

my $item_num=0;
foreach $record (@dataset)
{
        $item_num++;
        print "$item_num\t$record->{'date'}\t$record->{'title'}\t\t\t$record->{'episode'}\n";
}

print "\n\n";

my $not_valid=1;
while( $not_valid )
{
        print "Select Item to process: ";
        chomp($answer = <STDIN>);

        if ( $answer =~ m/\D/g )   { 
                print "Not a number!\n";
                next;
        } 
        
        if ( $answer < 1 ) { 
                print "Lowest Item is 1\n"; 
                next;
        }

        if ( $answer > $item_num ) { 
                print "Highest Item is $item_num\n"; 
                next;
        } 
        $not_valid = 0;
}

$not_valid=1;
while( $not_valid )
{
        print "Select Bitrate[". $default_bitrate . "]: ";
        chomp($bitrate = <STDIN>);
        
        if ( $bitrate =~ m/^$/ )
        {
                $bitrate = $default_bitrate;
        }

        if ( $bitrate =~ m/\D/g )   { 
                print "Not a number!\n";
                next;
        } 
        $not_valid = 0;
}

$not_valid=1;
while( $not_valid )
{
        print "(S)ingle or (M)ultipass Encoding [S]: ";
        chomp($num_passes = <STDIN>);
        
        if ( $num_passes =~ /^$/ )
        {
                $num_passes = "S";
        }

        if ( $num_passes !~ /[SP]/ ) { 
                print "Must be S or P !\n";
                next;
        } 
        $not_valid = 0;
}


print "You chose Item $answer -> $dataset[$answer-1]->{'title'}\n";

if ( $num_passes =~ /S/ )
{
        print "You chose Single-Pass Encoding @ " . $bitrate . "kbps!\n";
}
else
{
        print "You chose Multi-Pass Encoding @ " . $bitrate . "kbps!\n";
}

my $tmp_fname = $dataset[$answer-1]->{'title'};

if ( $dataset[$answer-1]->{'episode'} !~ m/^$/ )
{
        $tmp_fname = $tmp_fname . "-" . $dataset[$answer-1]->{'episode'};
}

$tmp_fname =~ s/ /_/g;
$tmp_fname =~ s/\:/-/g;

$fname_raw=$tmp_fname . "_RAW.avi";
$fname_final=$tmp_fname . "_" . $bitrate . "kbps.avi";

# Retrieve data....

if ( $num_passes =~ /S/ )
{
        my @args;

        push @args, "mencoder";
        push @args, "tivo://" . $tivo_ip . "/" . $dataset[$answer-1]->{'fsid'};
        push @args, "-oac"; 
        push @args, "copy";
        push @args, "-ovc"; 
        push @args, "lavc"; 
        push @args, "-lavcopts"; 
        push @args, "vcodec=mpeg4:vbitrate=" . $bitrate; 
        push @args, "-o";
        push @args, $fname_final;

#        print @args;

        system (@args) == 0
                or die "system @args failed: $?";
}
else
{
        my @args;

        push @args, "mencoder";
        push @args, "tivo://" . $tivo_ip . "/" . $dataset[$answer-1]->{'fsid'};
        push @args, "-oac"; 
        push @args, "copy";
        push @args, "-ovc"; 
        push @args, "copy";
        push @args, "-o";
        push @args, $fname_raw;

#        print @args;

        system (@args) == 0
                or die "system @args failed: $?";
}


if ( $num_passes =~ /M/ )
{
        # PASS 1 / 3

        my @args;

        push @args, "mencoder";
        push @args, $fname_raw;
        push @args, "-ovc"; 
        push @args, "frameno";
        push @args, "-oac"; 
        push @args, "copy";
        #push @args, "mp3lame";
        #push @args, "-lameopts";
        #push @args, "cbr";
        push @args, "-o";
        push @args, "frameno.avi";

#        print @args;

        system (@args) == 0
                or die "system @args failed: $?";


        # PASS 2 / 3

        my @args;

        push @args, "mencoder";
        push @args, $fname_raw;
        push @args, "-ovc"; 
        push @args, "lavc"; 
        push @args, "-lavcopts"; 
        push @args, "vcodec=mpeg4:vpass=1:vbitrate=" . $bitrate; 
        push @args, "-oac"; 
        push @args, "copy";
        push @args, "-o";
        push @args, $fname_final;

#        print @args;

        system (@args) == 0
                or die "system @args failed: $?";

        # PASS 3 / 3

        my @args;

        push @args, "mencoder";
        push @args, $fname_raw;
        push @args, "-ovc"; 
        push @args, "lavc"; 
        push @args, "-lavcopts"; 
        push @args, "vcodec=mpeg4:vpass=2:vbitrate=" . $bitrate; 
        push @args, "-oac"; 
        push @args, "copy";
        push @args, "-o";
        push @args, $fname_final;

#        print @args;

        system (@args) == 0
                or die "system @args failed: $?";
}

# Cleanup

my @args;

push @args, "rm";
push @args, "-f";
push @args, "divx2pass.log";
push @args, "frameno.avi";
push @args, $fname_raw;

#print @args;

system (@args) == 0
        or die "system @args failed: $?";

