#!/usr/bin/perl 

use warnings;
use strict;
use File::Find;

# multidvd-backup.pl
# author: Jesse van Herk <jesse@superfastninja.com>
# This program is released under the Gnu Public License version 2. # (C) 2006
#
# USAGE:
# multidvd-backup.pl <VOLUME LABEL PREFIX> <DIRECTORY>
 
# It takes only 2 command line parameters: the volume label prefix, and the directory to backup. The volume label of your burned DVDs will be the prefix appended with "_1", "_2" and so on. 
# http://www.jess2.net/projects/multidvd-backup/

# this program is based on the jdbkdir.sh backup script as written by John D. Rowell <me@jdrowell.com>.
# However, this is a substantial rewrite, and any bugs are my own.


my $BATCHSIZE = 4690000000;  # a bit small, to be on the safe side.
my $DVDOPTS = "-allow-leading-dots -allow-lowercase -l -D -R -J -joliet-long -input-charset iso8859-1";
my $DEVICE = "/dev/dvd";

my $volumeLabel = shift;
my $sourceDir = shift;

my $batch = 0; # global
my $runningTotal = 0; # global
my $totalFiles = 0; # global
my $totalSize = 0; # global
my @batches; # global


if (!$sourceDir)
{
    print "USAGE:\n";
    print "$0 <VOLUME LABEL PREFIX> <DIRECTORY>\n";
    exit 1;
}

print "building file list...\n";
File::Find::find(\&addFile, $sourceDir); # find the files we're interested in

my $humanSize = humanSize($totalSize);
my $numDiscs = scalar(@batches);
print "Found $totalFiles files, $humanSize, $numDiscs DVDs.\n\n";


burnDVDs(); # do the actual burning!

exit 0;

#######################
sub humanSize
{
    my $size = shift;

    my @units = ( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB' );

    my $unitsIndex = 0;
    while($size >= 1000)
    {
        $size /= 1024;
        $unitsIndex++;
    }

    my $humanSize = sprintf("%2.2f", $size);
    $humanSize .= $units[$unitsIndex];

    return $humanSize;
}

#######################
sub burnDVDs
{
    my $numFiles = 0;
    my $volumeNumber = 1;
    foreach my $ref (@batches)
    {
        my $tempFile = `tempfile`;  # get a temp file to use.
        chomp($tempFile);

        open TEMPFILE, ">", $tempFile;
        my @files = @$ref;
        foreach my $line (@files)
        {
            print TEMPFILE "$line\n"; # adds the current file to our file listing
        }
        close TEMPFILE;

        write_dvd($tempFile, $volumeNumber);
        $volumeNumber++;

        unlink($tempFile); # done with the temp file, delete it.
    }
}

#################################
sub addFile
{
    my ($fullPath, $safePath);

    return if(/^\.+$/ );  # don't traverse up.
    return if(/^\s*$/ );  # apparently we can get blanks. 
    return if (! -f $_);  # only want files, not devices or sockets.

    return if(-l $_ && !-f readlink($_)); # don't save dangling symlinks.

    my @statData = lstat($_);
    my $size = $statData[7];

    { # new context to suppress warnings.
       no warnings;
       $fullPath = $File::Find::name ;
    }

    $safePath = $fullPath;
    $safePath =~ s/=/\\\\=/g;  # according to mkisofs, escape equal signs with a double backslash

    my $line = "$safePath=$fullPath";
    $runningTotal += $size;
    if($runningTotal > $BATCHSIZE)
    {
        # this file will not fit in the current batch. start a new one.
        $runningTotal = $size; # start with this file in the new batch.

        $batches[++$batch] = [ $line ];
    }
    else
    {
        push @{$batches[$batch]}, $line;
    }

    $totalSize += $size;
    $totalFiles++;

}

#################################
sub write_dvd 
{
    my $tempFile = shift;
	my $dvdNumber = shift;

    my $numDiscs = scalar(@batches);

    print "Insert a blank DVDR and press ENTER:";
    my $dummy = <STDIN>;  # all we want is an 'enter' discard the actual result.

	print "writing DVD $dvdNumber / $numDiscs...\n";

    `growisofs $DVDOPTS -Z $DEVICE -graft-points -path-list $tempFile -V ${volumeLabel}_${dvdNumber}`;

    `eject $DEVICE`;
}


#################################
