#!/usr/bin/perl -- # -*- Perl -*-

# Script for moving images from digicam media to the local filesystem
# version 0.1
# 2007-02-12
# Copyright (C) 2007 Norman Walsh
# Released under the GPL license
# http://www.gnu.org/copyleft/gpl.html

# NO WARRANTY: This works for me. I'm paranoid about losing images, so
# I think it's pretty conservative. But I don't claim it'll work for
# you. Maybe it will. Maybe it won't. Maybe it'll lose images. Maybe
# it'll cause your hair to fall out. I don't know and I'm not
# responsible.

use strict;
use vars qw($opt_n $opt_r);
use English;
use Image::ExifTool qw(ImageInfo %allTables);
use Getopt::Std;

my $usage = "Usage: $0 [-r] [-n file ... ]\n";

die $usage if ! getopts('nr');

my $JPEGTRAN="/usr/bin/jpegtran-mmx";
my $JHEAD="/usr/bin/jhead";
my $MOVE="/bin/mv";
my $EXIFTOOL="/usr/bin/exiftool";
my $UFRAW="/usr/local/bin/ufraw-batch";
my $EXIFRDF="/home/ndw/bin/exifrdf";
my $CONVERT="/usr/bin/convert";

foreach my $pgm ($JPEGTRAN, $JHEAD, $MOVE, $EXIFTOOL, $UFRAW, $EXIFRDF, $CONVERT) {
    die "Cannot find $pgm.\n" unless -x $pgm;
}

my $ROOT = "/share/Photos";
die "Cannot find $ROOT.\n" unless -d $ROOT;

my @FILES = ();
if ($opt_n) {
    @FILES = @ARGV;
} else {
    @FILES = moveFromMedia();
    my @RENAMED = ();
    foreach my $image (@FILES) {
	my $newName = dateTimeRename($image);
	push (@RENAMED, $newName) if $newName ne $image;
    }
    @FILES = @RENAMED;
}

my @jpgs = ();
my @nefs = ();
my @tifs = ();
foreach my $image (@FILES) {
    push (@jpgs, "$image") if $image =~ /\.jpe?g$/;
    push (@nefs, "$image") if $image =~ /\.nef$/;
    push (@tifs, "$image") if $image =~ /\.tiff?$/;
}

extractMetadata(@jpgs,@nefs,@tifs) if !$opt_r;

makeJpgPreview(@jpgs);
makeNefPreview(@nefs);
makeTifPreview(@tifs);

exit;

# ============================================================

sub moveFromMedia {
    # Find the DCIM directory...
    my $MOUNT = undef;
    my $DCIMROOT = undef;

    opendir (DIR, "/media");
    while (my $dir = readdir(DIR)) {
	next if $dir =~ /^\.\.?$/;
	for my $dcim ('dcim', 'DCIM') {
	    if (-d "/media/$dir/$dcim") {
		$MOUNT = "/media/$dir";
		$DCIMROOT = "$MOUNT/$dcim";
		last;
	    }
	}
	last if defined ($MOUNT);
    }

    die "Cannot find CF card.\n" unless defined($MOUNT);

    my @FILES = ();
    open (FIND, "find \"$DCIMROOT\" -type f -print |");
    while (<FIND>) {
	chop;

	if (/\.jpe?g$/i || /\.nef$/i || /\.tiff?$/i) {
	    my $image = $_;
	    $image =~ s/^.*\/([^\/]+)$/$1/;
	    $image = lc($image);

	    if (-f "$ROOT/$image") {
		warn "Cannot move $_; already exists.\n";
	    } else {
		print "Moving $image...\n";
		system("$MOVE \"$_\" $ROOT/$image");
		chmod (0644, "$ROOT/$image");
		push(@FILES, $image);
	    }
	} elsif (/\.te?xt$/i) {
	    # nop
	} else {
	    print "Not an image: $_\n";
	}
    }
    close (FIND);

    print "Move finished; you may unmount the device.\n";
    return @FILES;
}

sub makeNefPreview {
    my @images = @_;

    return if !@images;

    print "Making JPG preview...\n";

    foreach my $image (@images) {
	my $jpg = $image;
	$jpg =~ s/^(.*)\/([^\/]+)\/([^\/]+)$/$1\/$3/;
	$jpg =~ s/\.[^\/\.]+$/.jpg/;
	die if $jpg eq $image;
	next if -f $jpg;

	my $exif = exif($image);
	my ($width, $height) = scaledGeometry($exif);
	my $size = $width > $height ? $width : $height;

	print "Making preview for $image...\n";
	system("$UFRAW --conf=/home/ndw/.ufrawrc-basic --size=$size --unclip --out-type=jpeg --output $jpg $image");
    }
}

sub makeTifPreview {
    my @images = @_;

    return if !@images;

    print "Making JPG preview...\n";

    foreach my $image (@images) {
	my $jpg = $image;
	$jpg =~ s/^(.*)\/([^\/]+)\/([^\/]+)$/$1\/$3/;
	$jpg =~ s/\.[^\/\.]+$/.jpg/;
	die if $jpg eq $image;
	next if -f $jpg;

	my $exif = exif($image);
	my ($width, $height) = scaledGeometry($exif);

	print "Making preview for $image...\n";
	system("$CONVERT -resize ${width}x$height $image $jpg");

	if (! -f $jpg) {
	    my @junk = ();
	    my $count = 0;
	    my $bigjpg = undef;
	    my $base = $jpg;
	    $base =~ s/\.jpe?g$//;
	    while (-f "$base-$count.jpg") {
		my $tmpjpg = "$base-$count.jpg";
		if (!defined($bigjpg)) {
		    $bigjpg = $tmpjpg;
		} else {
		    if (-s $tmpjpg > -s $bigjpg) {
			push (@junk, $bigjpg);
			$bigjpg = $tmpjpg;
		    } else {
			push (@junk, $tmpjpg);
		    }
		}
		$count++;
	    }
	    rename($bigjpg, $jpg);
	    push(@junk, $bigjpg);
	    unlink @junk;
	}

	rotate($jpg, $exif);
    }
}

sub makeJpgPreview {
    my @images = @_;

    return if !@images;

    print "Making JPG preview...\n";

    foreach my $image (@images) {
	my $jpg = $image;
	$jpg =~ s/^(.*)\/([^\/]+)\/([^\/]+)$/$1\/$3/;
	die if $jpg eq $image;
	next if -f $jpg;

	my $exif = exif($image);
	my ($width, $height) = scaledGeometry($exif);

	print "Making preview for $image...\n";
	system("$CONVERT -resize ${width}x$height $image $jpg");

	rotate($jpg, $exif);
    }
}

sub exif {
    my $image = shift;
    my $exifTool = new Image::ExifTool;
    my %options = ();

    $exifTool->ExtractInfo($image, \%options);

    return $exifTool;
}

sub scaledGeometry {
    my $exifTool = shift;

    # Can we figure out the size of this image? Surely there's a better way?
    my $width = -1;
    my $height = -1;
    foreach my $tag ($exifTool->GetFoundTags('File')) {
	my $value = $exifTool->GetValue($tag);
	$width = $value if $tag =~ /width/i && $value > $width;
	$height = $value if $tag =~ /height/i && $value > $height;
    }

    # What if we're going to rotate?
    if ($exifTool->GetValue('Orientation') =~ /rotate 270/i
	|| $exifTool->GetValue('Orientation') =~ /rotate 90/i) {
	my ($rwidth, $rheight) = scaledToFit($height, $width);
	# but we haven't yet...
	return ($rheight, $rwidth);
    } else {
	return scaledToFit($width, $height);
    }
}

sub scaledToFit {
    my $width = shift;
    my $height = shift;

    if ($width > 1600 || $height > 1200) {
	my $f1 = 1600 / $width;
	my $f2 = 1200 / $height;
	if ($f1 > $f2) {
	    $width = int($width * $f2);
	    $height = int($height * $f2);
	} else {
	    $width = int($width * $f1);
	    $height = int($height * $f1);
	}
    }

    return ($width, $height);
}

sub rotate {
    my $image = shift;
    my $exifTool = shift;

    my $tempfn = "$ROOT/copycf.$$.tmp";

    if ($exifTool->GetValue('Orientation') =~ /rotate 270/i) {
	print "\tRotating 270 CW\n";
	system("$JPEGTRAN -outfile $tempfn -rotate 270 $image");
	system("mv $tempfn $image");
    } elsif ($exifTool->GetValue('Orientation') =~ /rotate 90/i) {
	print "\tRotating 90 CW\n";
	system("$JPEGTRAN -outfile $tempfn -rotate 90 $image");
	system("mv $tempfn $image");
    }
}

sub extractMetadata {
    my @images = @_;

    return if !@images;

    print "Extracting metadata...\n";
    foreach my $image (@images) {
	my $rdf = $image;
	$rdf =~ s/^(.*)\/([^\/]+)\/([^\/]+)$/$1\/rdf\/$3/;
	$rdf =~ s/\.[^\/\.]+$/.rdf/;
	if (-f $rdf) {
	    warn "Leaving existing metadata: $rdf\n";
	    next;
	}
	system("$EXIFRDF $image > $rdf");
    }
}

sub dateTimeRename {
    my $image = shift;
    my $info = ImageInfo("$ROOT/$image");

    my $date = undef;
    if (exists $info->{'SubSecDateTimeOriginal'}) {
	$date = $info->{'SubSecDateTimeOriginal'};
    } elsif (exists $info->{'DateTimeOriginal'}) {
	$date = $info->{'DateTimeOriginal'};
    } elsif (exists $info->{'ModifyDate'}) {
	$date = $info->{'ModifyDate'};
    } else {
	warn "Cannot rename $image (no EXIF date).\n";
	return $image;
    }

    my @datebits = split(/[:\.\s]/, $date);
    my ($year, $month, $day) = ($datebits[0], $datebits[1], $datebits[2]);
    $date =~ s/[\s\.]/\-/sg;
    $date =~ s/://sg;

    mkdir("$ROOT/$year", 0755) unless -d "$ROOT/$year";
    mkdir("$ROOT/$year/$month", 0755) unless -d "$ROOT/$year/$month";
    mkdir("$ROOT/$year/$month/$day", 0755) unless -d "$ROOT/$year/$month/$day";
    mkdir("$ROOT/$year/$month/$day/rdf", 0755) unless -d "$ROOT/$year/$month/$day/rdf";

    die "Cannot create $ROOT/$year/$month/$day\n"
	unless -d "$ROOT/$year/$month/$day";

    die "Cannot create $ROOT/$year/$month/$day/rdf\n"
	unless -d "$ROOT/$year/$month/$day/rdf";


    my $ext = $image;
    $ext =~ s/^.*\.//g;
    $ext = 'tif' if $ext eq 'tiff';
    $ext = 'jpg' if $ext eq 'jpeg';

    mkdir("$ROOT/$year/$month/$day/$ext", 0755) unless -d "$ROOT/$year/$month/$day/$ext";
    die "Cannot create $ROOT/$year/$month/$day/$ext\n"
	unless -d "$ROOT/$year/$month/$day/$ext";

    my $newName = "$year/$month/$day/$ext/$date.$ext";

    if (-f "$ROOT/$newName") {
	warn "Cannot rename $image (already exists in $ROOT).\n";
	return $image;
    }

    rename("$ROOT/$image", "$ROOT/$newName");
    print "Rename $image -> $newName\n";

    return "$ROOT/$newName";
}
