Perl is fun

When I reinstalled my iMac 24″, I restored a backup of the iPhoto Library.

But the software I’m working on (eXaPhoto Publisher) couldn’t import correctly an iPhoto album: a lot of pictures were missing. The imedia browser had the same problem, but iPhoto seems smart enough to find the pictures.

When you manage yourself your picture, iPhoto maintains a dummy structure inside the “iPhoto Library”. This structure contains links (aliases) to the original files.

I don’t know how it happened, but all the iPhoto aliases were marked as being on the backup volume. So instead of reimporting all my pictures and risk losing all my albums, I’ve written a Perl script to update all the aliases in “iPhoto Library/Originals” so they point now to the right location.

Below is the script: it shows how to manage files and aliases in Perl.


#!/usr/bin/perl

# script to update all iPhoto aliases so they point to the right location
# iPhoto Library was restored from a backup and all aliases were pointing to the bad volume ie.
# instead of "/Volumes/iMac/...", they were pointing to "/Volumes/Bk_Imac"
# Stephan Burlot, 2008-02-14

use Mac::Memory;
use Mac::Resources;
use Mac::MoreFiles;
use Mac::Files;
use Data::Dumper;

my $path;
my $alias;
my $alis;
my $bad_volume = "Bk_iMac";
my $good_volume = "iMac";
my $updated_files = 0;
my $total_files = 0;

sub update_alias {

  my ($path, $data) = @_;

  if (-d $path) {
    print "Parsing directory $path\n";
    return 0;
  }

  $total_files++;
  $finfo = FSpGetFInfo($path);
  if (($finfo->fdFlags & 0x8000) == 0) {
    print "$spec is a file\n";
    return 0;
  }

  $RF = FSOpenResourceFile($path, "rsrc", fsRdWrPerm) ;
  if (!defined($RF)) {
    warn "$path has no resource";
    return 0;
  }

  if (defined($RF)) {

    # get resource by index; get first "alis" resource
    $alis = GetIndResource('alis', 1) ;
    if (!defined($alis)) {
      warn $Mac::Errors::MacError;
      return;
    }

    $fullpath = "";
    $index = 0;

    # construct the full path
    while ($info = GetAliasInfo($alis, $index++)) {
      $fullpath = "/" . $info . $fullpath ;
    }

    # add the volume name
    $volname = GetAliasInfo($alis, asiVolumeName);
    $fullpath = "/Volumes/" . $volname . $fullpath;

    # if the file already exists, the alias is OK
    if (-f $fullpath) {
      print "Alias $path is already OK\n";
    } else {

      # replace bad volume with the good one
      ($target = $fullpath) =~ s/$bad_volume/$good_volume/;

      # double check the new target exists
      if (-f $target) {

        $new_alias = NewAlias($target);
        $updated = UpdateAlias($target, $alis);
        print "$fullpath was updated to $target\n";
        if ($updated) {
          $updated_files++;
          ChangedResource($alis);
          WriteResource($alis);
          UpdateResFile($RF);
        }
      } else {
        print "target $target wasnt found\n";
      }
    }
    CloseResFile($RF);
  }

  return 0;
}

my $path = '/Users/stephan/Pictures/iPhoto Library/Originals/';

FSpIterateDirectory($path, 0, \&update_alias, nil);

print "Updated $updated_files files of a total of $total_files\n";

2 thoughts on “Perl is fun

  1. I was looking for some info about accessing ressource fork of iPhoto, having the imedia bug !
    I like your perl 😉
    I need to find my way to have something like that in Cocoa !
    thanks

  2. There is potentially a big issue with this with long paths as filenames over 255 bytes are mangled by replacing the trailing part of the name with a hexadecimal representation of the file’s Catalog Node ID.

    Those over 255 can not be fixed because when thy were moved on another volume they got a new Catalog Node ID.

Leave a Reply

Your email address will not be published. Required fields are marked *

*