I've had a terrible time getting a decent export of iPhoto. If I try to export it by highlighting every event, I get a single directory full of 3400 images. If I want to have them come out in a reasonable manner, I have to export every Roll/Event individually.

Apple recommends backing up to CD, but as I generally use 1GB sticks in my camera, I frequently can't fit even a single camera load onto a CD. That's clearly a loser.

So I sat down this morning and wrote a quick-n-dirty Perl script to copy my images from iPhoto to disk.

This should not be as difficult as it is, but between Apple's flat plist fixation and their "moving target" philosophy, the first two CPAN modules I tried (Mac::PropertyList and Mac::iPhoto) simply errored out. I finally found Mac::PropertyList::SAX (interestingly based on Mac::PropertyList), and it worked fine.

This code probably violates all my own rants on style and coding: it's a morning's hack session, and looks like it. I'm putting it here in case anyone else needs it.

#!/usr/bin/perl ## # Export iPhoto data by Event/Roll via Perl # # MPeever # 2009-01-25 # use strict; use warnings; use Data::Dumper; use File::Copy qw/copy/; use File::Spec; use Mac::PropertyList::SAX qw/parse_plist_fh/; my $targetDir = pop (@ARGV); my @paths = grep { -f } @ARGV ; my $VERBOSE = grep { $_ eq '-v' } @ARGV; die "Usage: $0 [-v][-v] \$albumData.xml [\$albumData.xml]... \$targetD +ir\n" unless scalar @paths; print STDERR "Creating $targetDir\n" and mkdir $targetDir unless -d $targetDir; my @libraries = map { parseLibrary($_) } @paths; foreach my $library (@libraries) { my $archiveLocation = $library->{'Archive Path'}; my $rolls = rolls ($library); foreach my $roll (@{$rolls}) { my $name = $roll->{'name'}; my $archivePath = File::Spec->catdir($targetDir, $name); $roll -> {'archivePath'} = $archivePath; } print STDERR Dumper $rolls if ($VERBOSE > 1); foreach my $folder ( map { $_->{'archivePath'}} @{$rolls} ){ warn "Folder exists! '$folder'" and next if -d $folder; print STDERR "Creating \"$folder\"\n" if $VERBOSE; mkdir $folder or warn "Error creating '$folder': $!"; } foreach my $roll (@{$rolls}) { my $archivePath = $roll->{'archivePath'}; foreach my $image (@{$roll->{'images'}}){ my $src = $image->{'ImagePath'}->value; my $filename = (File::Spec->splitpath($src))[-1]; my $dest = File::Spec->catfile($archivePath, $filename); warn "File Exists! '$dest'" and next if -f $dest; print qq{cp '$src' '$dest'}, "\n" if $VERBOSE; copy ($src, $dest) or warn "Error copying $src -> $dest: $!"; } } } ## Parse a library file ## return Mac::PropertyList object sub parseLibrary { my $path = shift; open (my ($fh), $path) or warn "Couldn't open $path: $!" and return ''; my $data = parse_plist_fh ($fh); close $fh; return $data; } ## Get the list of Rolls from a library ## return $hashRef to rolls ## Each Roll contains its own images sub rolls { my $library = shift; my $photos = $library -> {'Master Image List'}; my $rollNameKey = my @rolls = map { my $rollIdKey = defined $_->{'RollID'} ? 'RollID' : 'AlbumId'; my $rollNameKey = defined $_->{'RollName'} ? 'RollName' : 'AlbumNa +me'; my $name = $_->{$rollNameKey}->value; my $id = $_->{$rollIdKey}->value; my @imageIds = map {$_ -> value } @{ $_->{'KeyList'}->value}; my @images = map {$photos -> {$_}} @imageIds; { 'name' => $name, 'id' => $id, 'imageIds' => \@imageIds , 'images' => \@images } } @{$library->{'List of Rolls'}}; return \@rolls; }

Update: fixed usage message

Update: fixed dictionary keys in &rolls. To make it work on my other MacBook.

Replies are listed 'Best First'.
Re: iPhoto Export
by graff (Chancellor) on Jan 26, 2009 at 00:57 UTC
    Thanks for posting this -- when I get some time to play with it, I think it will point out lots of other useful things I can do with my non-work-related macosx features. As for this point:
    Apple recommends backing up to CD, but as I generally use 1GB sticks in my camera, I frequently can't fit even a single camera load onto a CD. That's clearly a loser.

    Don't you have one of those "combo" drives that can burn a DVD? You get ~4.5 GB that way...

      You know, it actually occurred to me to use a DVD instead of a CD at some point, but by then I was determined to get an export working in code. I'm glad I chose the code route now, because I have all my photos from two MacBooks on a single drive and I can work on merging the libraries.

      I might need to write a merge too, but I think I can use one I wrote in Scheme a while back. I use it to back up iTunes now, and it works great. iTunes is easier than iPhoto, because the files are more visible: but now that Perl's got the images exporting, standard tools ought to make the rest of the chores easier.

      Storage on external Hard Drives seems to be the current trend as there are many reasonable priced ones with 1TB plus for around £100.00

      My iPhoto DB is approaching 100GB of family pictures and video (yeah, I am one of them parents :-). To backup by anything like a tape / CD / DVD / etc is just painful.

      I have been backing up (is some meaning of the word) by exporting everything (and flattening the structure in the process) to a remote share on my home network. Not the nicest solution

      I will also have to play with this script to see what it can do for me. Looks interesting.

      --MidLifeXis