#!/usr/bin/env pythonw # # iphoto2album: A script to export an iPhoto album to the web as an Album # album. # # By Nathaniel Gray # # This works by looping through all of the pictures in the current iPhoto album, # symlinking them into a directory (no unnecessary copies), then running album # there. It's pretty simple, really. # # To install it, change the paths and flags listed below to suit yourself, # then make this file executable and move it here: # ~/Library/Scripts/Applications/iPhoto/iphoto2album # # You'll need your script menu enabled: # http://www.apple.com/applescript/scriptmenu/index.html # # Then run iPhoto, select an album, and select iphoto2album from the script # menu. # # Alternately, you can just run iphoto2album from the command line. import os, sys import EasyDialogs as dialogs, datetime from appscript import * home = os.getenv("HOME") # You'll probably need to edit some of these: # The location of your Album album's root. The new album will be placed # in a subdirectory of this: photos_dir = u"%s/Sites/photos/" % home # You probably need to set the PATH here because iPhoto runs in an # environment where it's probably not set to be very comprehensive path = os.getenv('PATH') + (":/usr/local/bin:/sw/bin:/opt/bin:%s/bin" % home) os.environ['PATH'] = path # The name of the album binary album_bin = 'album' # Flags to run album with. This needs to be a LIST of strings: album_flags = ("-medium 1024x1024 -known_images -just_medium" + \ " -clean -crop -theme ../album_themes/J_Peterman").split(' ') ############################################################################ # You shouldn't need to edit anything below here. # A helper function that makes a symlink with the same atime and mtime as # the file it points to # def datesymlink(points_to, f): # os.symlink(points_to, f) # s = os.stat(points_to) # os.utime( f, (s.st_atime, s.st_mtime) ) # First, retrieve the current album information from iPhoto ip = app('iPhoto.app') album_name = ip.current_album.name.get() msg = u"Welcome to iphoto2album. Would you like to export the album \"%s\"?" % \ album_name msg = msg.encode( 'latin-1' ) cont = dialogs.AskYesNoCancel( msg, cancel="") if cont != 1: sys.exit(1) photos = ip.current_album.photos img_properties = photos.properties.get() album_path = photos_dir + album_name if len(img_properties) > 50: cont = dialogs.AskYesNoCancel( """You have selected a large album to convert. This may take a long time. Are you sure you want to continue?""", cancel="") if cont != 1: sys.exit(1) # Make a directory for the album os.chdir( photos_dir ) if( os.path.exists( album_path ) ): msg = "Directory: %s\nalready exists. Click 'Yes' to delete it and"\ " continue or 'Cancel' to abort." % album_path msg = msg.encode( 'latin-1' ) #print "Message Length: ", len(msg) cont = dialogs.AskYesNoCancel( msg, default=-1, no="" ) if cont != 1: sys.exit(1) if os.system( "rm -rf \"%s\"" % album_path ) != 0: dialogs.Message( "Couldn't remove %s" % album_path ) sys.exit(1) os.makedirs( album_path ) # I should write a header file with any album comments but I can't get it to # work just yet. #hdr_file = open( album_path + "/header.txt", "w" ) ftr_file = open( album_path + "/footer.txt", "w" ) ftr_file.write( "Exported from iPhoto by iphoto2album on %s" % \ datetime.datetime.now().ctime() ) ftr_file.close() # For each file, symlink it into the directory and put an entry into the # captions file. # captions file format: # filename :: title :: caption # Properties of photos: # properties -- All of the object's properties. # image_filename (r/o) -- The name of the image file. # image_path (r/o) -- The path to the image file. # title -- The title (name) of the photo. # name -- The name (title) of the photo. # comment -- A comment about the photo. cap_file = open( album_path + "/captions.txt", "w" ) for img in img_properties: os.symlink(img[k.image_path], album_path + "/" + img[k.image_filename]) cap_file.write("%s :: %s :: %s\n" % (img[k.image_filename], img[k.title], img[k.comment])) cap_file.close() # Now we run album # This is messy because we want to be able to a) have an updating progress # bar dialog, b) with a functioning cancel button, and c) complain GUIlly if # an error occurred. album_args = [album_bin] + album_flags + ['-add', album_path, photos_dir] import thread, time, signal finished = 0 pid = 0 status = 0 def cmd_thread(): # Need to put this in a separate thread so the prog bar can update global pid, finished, status # Get the pid so we know what to kill on 'cancel' pid = os.spawnvp(os.P_NOWAIT, album_bin, album_args) # Get the status so we know if there was an error foo, status = os.waitpid(pid, 0) finished = 1 thread.exit() try: thread.start_new_thread(cmd_thread, () ) finished = 0 # For some reason, the prog bar only works in the main thread. :-/ pb = dialogs.ProgressBar("Generating album", 0, \ "Generating album. This may take a while.") while not finished: pb.inc() time.sleep(0.5) del pb except KeyboardInterrupt: # The user hit cancel if pid: print "killing pid %d" % pid os.kill(pid, signal.SIGKILL) # SIGINT doesn't seem to be enough sys.exit(1) # The process finished if status != 0: msg = "The album process exited with an error. Status = %s" % status dialogs.Message( msg ) else: dialogs.Message( "The album has been successfully exported." )