#!/usr/bin/perl

# Martin Dengler <martin@martindengler.com>
# Based on Bill Moseley's script from
# http://www.nabble.com/Import-and-duplicate-images.-td19185234.html
# Released under the GPL v2+

use strict;
use warnings;

# Script to import photos from memory card, avoid dupicates and
# update f-spot's database.
# Questions: Bill Moseley <moseley@...>

use Image::ExifTool qw(:Public);
use File::Find;
use File::Path;
use File::Temp 'tempdir';
use File::Copy;
use Data::Dump 'dump';
use File::MimeInfo::Magic;
use Time::Local;
use Class::DBI::Loader;
use File::Basename;

# $dest_root is the dir where photos are stored
my $dest_root = '/mnt/av/pictures';

# f-spot db file, of course.
my $db_file   = '/home/martin/.gnome2/f-spot/photos.db';

# place to put backup of the f-spot database of the photos before
# running script.
$ENV{TMPDIR} = '/mnt/av/pictures/backups';

my $total_source;       # count of total source files
my $existing_count = 0; # count of files already imported from card
my @files_to_move;      # list of source paths to copy
my %day_count;          # to summarize imports by day

# What EXIF fields to look for, and in which order, for the date
my @date_fields = qw/
    CreateDate
    DateTimeOriginal
    ModifyDate
/;

# Process all directories on the command line
for my $directory (@ARGV) {
    find( \&find_images, $directory);
}

# Show summary of files ready to import
my $count = scalar @files_to_move;
unless ( $count ) {
    print "nothing to import\n";
    exit;
}

print <<EOF;
Total source files: $total_source
Total to import:    $count
Existing skipped:   $existing_count

Counts by folder:
EOF

dump \%day_count;

my $tmpdir = tempdir();
print "\nbacking up to $tmpdir\n";

# Backup the database and images
system( "cp $db_file $tmpdir/photos.db" );

# Auto-load the database schema
my $loader = Class::DBI::Loader->new(
    dsn               => "dbi:SQLite:dbname=$db_file",
    options           => { RaiseError => 1, AutoCommit => 1 },
    #left_base_classes => qw/Class::DBI::Sweet/,                  # or arrayref
    relationships     => 1,
);

# Load all columns on SELECT
for ( $loader->classes ) {
    $_->columns( Essential => map {"$_"} $_->columns );
}


# Do the job
import_images();

# Find what images need to be imported
sub find_images {
    return unless -f;   # only look at regular files
    return if /^\./;    # skip hidden

    my $file = $_;      # get name

    $total_source++;

    print "$file\n";
    print "$total_source\n" unless $total_source % 50;

    my $mtime;

    # First try and extract out the time from the EXIF info.

    # Create a new Image::ExifTool object
    my $exifTool = new Image::ExifTool;

    # Extract meta information from an image
    $exifTool->ExtractInfo( $file );

    my $dates = $exifTool->GetInfo( @date_fields );

    for my $fld ( @date_fields ) {
        my $datetime = $dates->{$fld} || next;

        my ( $date, $time ) = split /\s+/, $datetime;
        my ( $y, $m,   $d ) = split /:/, $date;
        my ( $h, $min, $s ) = split /:/, $time;

        next if $y < 1970;

        $mtime = timelocal( $s, $min, $h, $d, $m - 1, $y - 1900 );
        last;
    }

    $mtime ||= ( stat $file )[9];

    # Build destination directory and file name.

    my ( $y, $m, $d ) = ( localtime $mtime )[ 5, 4, 3 ];

    my $dir = sprintf( "$dest_root/%04d/%02d/%02d", $y + 1900, $m + 1, $d );

    my $dest_file = lc $file;

    my $dest = "$dir/$dest_file";

    # does this file already exist?
    if ( -f $dest ) {
        $existing_count++;
        return;
    }

    # Save for copy later.
    push @files_to_move, {
        source => $File::Find::name,
        dir    => $dir,
        file   => $dest_file,
        mtime  => $mtime,
    };

    $day_count{$dir}++;

    return;

} ## end sub find_images

sub import_images {

    # Create an import roll.
    my $roll = Rolls->create( {
            time => time,
    } );

    my $cnt;
    for my $image ( @files_to_move ) {

        $cnt++;
        print "imported $cnt\n" unless $cnt % 20;

        my $dir    = $image->{dir};
        my $file   = $image->{file};
        my $source = $image->{source};

        my $tag = basename(dirname($source));
        my @tags = split("_", $tag);
        my @non_date_tags = @tags[1 .. $#tags];
        my $bigtag = join("-", @non_date_tags);
        push @non_date_tags, ( $bigtag );

        # And insert photo into database.
        # "uri" is for newer f-spot
        # (directory => $dir,  name => $file for older)
        #
        my $photo = Photos->create( {
                time               => $image->{mtime},
                uri                => ( 'file://' . $source ),
                roll_id            => $roll,
                default_version_id => 1,
                description        => '@tags',
        } );

        for my $onetag ( @non_date_tags ) {
            my $tagrow = Tags->find_or_create( {
                name  => $onetag,
                category_id => 0,
                is_category => 0,
                icon => '',
                                            } );

            my $phototag = PhotoTags->create( {
                photo_id => $photo->id,
                tag_id => $tagrow->id,
                                              } );
        } ## end for my $onetag ( @tags )

    } ## end for my $image ( @files_to_move)

    print "imported $cnt\n";

} ## end sub import_images
