nav-left cat-right RSS
cat-right

How to Automatically Copy Images from Removable Media

Nikon D300 and AF-S VR Zoom-NIKKOR 70-200mm f/2.8G IF-ED

So, I’m lazy.  I also like things to work perfectly.  This is why I write tools to do things so that I don’t have to.  After recently moving to Linux for my main desktop, I wanted to be able to do everything I had previously done when it was running Windows, with a reasonable amount of effort.  One thing I needed to be able to do is manage my photos.  I was happy when I found out that Picasa runs just fine in Linux, so most of the battle is won.  One of the things that always bugged me in Windows was transferring photos from removable media, such as an SD card.  In my mind, this should be a simple process:

  • Copy the photos to a specific location, with a folder named appropriately
  • Skip duplicates
  • Do all of this without having to do anything manually

I whipped up a script to do this very thing, in such a way that actually was easier and better than in Windows!  I used perl for the job, and a CPAN module to get the file information.  It’s super fast to boot.  Here’s the code:

#!/usr/bin/perl
# Photo Importer
# Copies photos from removable media
# by Ed Salisbury (ed@edsalisbury.net)
# http://www.edsalisbury.net
# (c)2009 Ed Salisbury, Some Rights Reserved
#
# License:
# Except where otherwise noted, this work is licensed under Creative Commons
#   Attribution ShareAlike 3.0.
#
# You are free:
#   * to Share — to copy, distribute and transmit the work
#   * to Remix — to adapt the work
#
# Under the following conditions:
#   * Attribution. You must attribute the work in the manner specified by the
#     author or licensor (but not in any way that suggests that they endorse
#     you or your use of the work).
#   * Share Alike. If you alter, transform, or build upon this work, you may
#     distribute the resulting work only under the same, similar or ai
#     compatible license.
#   * For any reuse or distribution, you must make clear to others the license
#     terms of this work. The best way to do this is with a link to the
#     license's web page (http://creativecommons.org/licenses/by-sa/3.0/)
#   * Any of the above conditions can be waived if you get permission from the
#     copyright holder.
#   * Nothing in this license impairs or restricts the author's moral rights.

# The location of your photos
my $DEST = "/data/photos/Our Family";

use warnings;
use strict;
use Image::ExifTool qw(:Public);
use Data::Dumper;
use File::Copy;
use File::Find;

# File::Find can spit out some warnings that we don't care about, so disable
no warnings 'File::Find';

# Turn off output buffering
$|++;

my $mounted = 0;
my $count = 0;
my $mnt = '';

print "Checking for media...";
while (!$mounted)
{
    # I have Nikon and Canon cameras - change/add as needed
    $mounted = `mount | egrep 'NIKON|CANON'`;
    if (!$mounted)
    {
        # Look for a mounted device for 20 seconds
        if ($count < 20)
        {
            print ".";
            sleep(1);
            $count++;
        }
        else
        {
            # Give up
            print " not found.\n";
            exit();
        }
    }
    else
    {
        # Found a device, get the directory it's mounted on
        $mounted =~ /on (.*?) type/;
        $mnt = $1;
        print " found. ($mnt)\n";
    }
}

# Go through the mounted filesystems looking for files
find(\&cp, $mnt);

sub cp
{
    # Only copy jpegs - change if you use TIFs or RAW files
    /\.jpg$/i or return;

    print "Copying file $_... ";

    my $file = $_;

    # Get the EXIF data from the file and extract the date
    my $info = ImageInfo($File::Find::name);
    my $date_time = $info->{'DateTimeOriginal'};
    my @fields = split(":", $date_time);
    my $year = $fields[0];
    my $day = (split(" ", $fields[2]))[0];
    my $date = $fields[0] . "_" . $fields[1] . "_" . $day;

    # I use YEAR/MM_DD_YY for the dir name, feel free to change for your needs
    my $dest_dir = "$DEST/$year/$date";

    # Duplicate check
    if (-f "$dest_dir/$file")
    {
        print "skipping.\n";
        return;
    }

    # Create the directory if needed
    if (! -d "$dest_dir")
    {
        system("mkdir -p \"$dest_dir\"");
    }

    # Copy the file
    if (copy($File::Find::name, "$dest_dir/$file"))
    {
        print "done.\n";
    }
    else
    {
        # Something broke!
        print "FAILED.\n";
        exit();
    }
}

In order to run this script, you will need to install the Image::ExifTool module from CPAN (instructions)

A few things to pay attention to (and possibly change):

  • The output directory ($DEST)
  • The camera manufacturers it’s looking for
  • The date format

To make this process even better, when you insert a media card, your OS will usually ask you what you want to do.  If you have it run this script (every time), then it will automatically copy the files any time you stick a card in.  This is the way I have mine set up, and I love it!  Please let me know if you find this useful or have any improvement suggestions!

Photo credit: Dawvon on Flickr by 2.0 cc)

  • Ed
    Marco, the way I do it is when I stick the card in, Ubuntu pops up a message saying "what do you want to do with this..." or whatever, choose the script. The issue will be that you're using a USB drive, not a memory card, which might be slightly different. One thing you could do is to have the script constantly running in the background, checking for the usb drive periodically. If it's there, copy files, if not, go back to sleep. Send me an email (ed {@} edsalisbury {dot} net) if you want to discuss further, as this could be a longer discussion :)

    Thanks for the comment!
  • Marco
    I've been looking all around to finally find here what I was looking for. I am building a Photo Frame and was looking on how to make it copy automatically photos from an external device (like a USB stick).

    Now here is a quick question:

    I am using Ubuntu 9.04. The frame collects the pictures from Flickr (via FlickrFS) and copies them to the Pictures folder where FEH collects them and plays the slideshow. All automatic.

    NOow, how can I make your script automatic so that when people insert a USB pen in my frame, it kicks on, copies the files and unmount the pen?

    The goal is that I have to give this homemade frame to my father, so it must be painless and automatic. No user intervention.

    Do you have any suggestions?

    Great work!!!!
blog comments powered by Disqus