On lines 1 and 2, I define some names that I will use later. My
directories for this task start with Nokia, and I'm going to move imported images to Nokia-Imported.
On line 4, I start with the Folder Action handler. I want to run only when I add files to this folder. AppleScript puts the name of the folder and the list of items in this_folder and these_items.
On line 5, I log the arguments so I can inspect them later. This
handler is defined in line 100. I simply convert the paths to POSIX
paths (because I like Unix paths more than Mac paths) and give them
as arguments to an external Perl script. Alternatively, I just call
/bin/false, which is virtually a no-op. I've commented out this line,
hoping that you won't have the same problem. If you do, though, try
uncommenting it. If iPhoto is going crazy on you, logging out
and logging back in should fix that.
On line 7, I pause the script. The first image triggers the Folder Action, but I don't want the rest of the script to run until all the files have transferred. It takes 1 or 2 seconds for each file to transfer, so I pause for the number of files multiplied by 3 just to be safe.
One line 10, I ensure that I have some files to process. As I said earlier, the Folder Action was springing into action almost randomly, and I don't want to start up iPhoto if I don't need to, especially if I am trying to work. As a side note, although the current form of the script works for me, I have also considered breaking the script into separate files and running each file as needed. That way, the main script doesn't need to do anything with iPhoto, including looking in its dictionary, if it has nothing for iPhoto to do.
On line 12, I start the actual meat of the task. I have at least one thing in these_items, and I want to process it. On line 13, I select my iPhoto library, since I store all my phone's pictures in the same library. The handler is defined on line 27 and is very simple: iPhoto Library Manager has a nice AppleScript library. Before I tell iPhoto Library Manager what to do, I quit iPhoto, just to be safe. I get a list of the libraries that iPhoto Library Manager knows about and select the one that matches my argument in library_name.
On line 15, I get the name of the folder where I will put the images after I have imported them. I could simply trash the images once I import them, but I know that the second I do, the import will have gone awry, I will have already deleted the images from my phone, and they will be nowhere. That will also be the photo set that has proof positive of intelligent extraterrestrial life or Britney Spears (which now apparently go for a couple of thousand dollars if you're the lucky photographer who can snap her picture).
This folder combines the base
name, in the myLibrary property with the string in the extension
property. Once I delete pictures from my phone, the numbering starts
all over again, and I haven't found a way to change that. I have a
lot of Image-(01).jpg files in iPhoto. If you know how to change
that, please let me know.
On line 17, I import the photos, and on line 19, I delay again. I have to wait while iPhoto works its magic. Importing the images is a programmatic pain. As I said earlier, this basic operation doesn't show up in iPhoto's dictionary, so I have to use GUI Scripting.
Believe it or not, I have to simulate typing and mousing to make this work, and System Events is the application I actually talk to, which then talks to AppleScript for me. Luckily the process isn't so bad, and I even learned some iPhoto shortcuts. If I type Apple-Shift-I, I get a sheet asking me for a path from which to import photos. I just have to fill in that path.
The tricky part is knowing how to address the windows, sheets, and buttons, but Apple's UI Element Inspector makes that easy. I hover the mouse over any user interface element, and the Inspector window gives me everything I need to fill in the names and numbers of the elements.
Once I import all the files, I need to move them into a backup directory that I can recover later. On line 21, I call the move_files handler, which simply moves the files. In this case, the Finder is doing the work, so I have to tell the Finder to do these things. I often forget that AppleScript is just scripting other applications and can't do these things on its own.
That's all of the work! There is still a Bluetooth File Exchange window, though, which tells me which files I just transferred; I want to close that. I don't have a way to tell the actual application OBEXAgent to quit, so I have to use GUI Scripting again. Using UI Element Inspector, I figure out the window name and the appropriate button to press to make this window go away.
Now that I have everything in place, I can make things happen simply by transferring files from my phone. As a side note, I took the screenshots of my phone with ScreenTaker, a free application from Symbianware. Despite what the screenshot says, on the Nokia 3650 I use the pencil button with the * key to grab the screen image.
![]() Screentaker grabs screenshots from my Nokia 3650 |
![]() Marking the files to send |
![]() Sending the files via Bluetooth |
![]() Sending the files via Bluetooth |
I've been using this script for about six months, and although there is a lot of room for improvement, I stopped thinking about it when it accomplished the task without messing up or being too annoying.
There are some things it could use, though. I assumed that every file in the folder would be an image file. That's OK, because iPhoto will simply ignore those in the import. I do import the entire folder, so I would have to move any other files out of the way first. For instance, I can transfer text, sound, and video files from my phone to my PowerBook, but this script is going to move them all to Nokia-Imported unless I do something with them first.
I keep thinking I should rewrite this in Perl using the Mac::Glue module. AppleScript almost does a decent job of the application control, but it doesn't make file and folder handling very easy. I can still use AppleScript and Folder Actions to invoke the future Perl script, but I'm ready to not think about this for a while. As long as it leaves me alone, I'll leave it alone.
1 property extension : "Imported"
2 property myLibrary : "Nokia"
3
4 on adding folder items to this_folder after receiving these_items
5 --log_arguments(this_folder, these_items)
6
7 delay 30 -- wait for everything to transfer from the phone
8
9 ---XXX if count of these_items is zero, do nothing
10 if (count of these_items) is 0 then
11 set count to 0 --null statement
12 else
13 select_iphoto_library(myLibrary) (* ensure we have the right library *)
14
15 set done_folder to get_destination_folder(this_folder)
16
17 import_photos(this_folder)
18
19 delay 60 -- wait for the import to finish
20
21 move_files(this_folder, done_folder)
22
23 close_bluetooth_file_exchange()
24 end if
25 end adding folder items to
26
27 on select_iphoto_library(library_name)
28 tell application "iPhoto" to quit
29 tell application "iPhoto Library Manager"
30 set current of item 1 of (get every library whose name is library_name) to true
31 quit
32 end tell
33
34 tell application "iPhoto" to activate
35 end select_iphoto_library
36
37 on get_destination_folder(this_folder)
38 tell application "Finder"
39 set my_parent to (container of this_folder)
40 set my_base to (name of this_folder) as text
41
42 set done_folder_name to my_base & "-" & extension
43 set done_folder_path to (my_parent as text) & done_folder_name
44
45 if (exists folder done_folder_path) then
46 set done_folder to done_folder_path
47 else
48 set done_folder to ¬
49 (make new folder in (my_parent) ¬
50 with properties {name:done_folder_name})
51 end if
52 end tell
53
54 return done_folder
55 end get_destination_folder
56
57 on import_photos(this_folder)
58 tell application "System Events"
59 tell process "iPhoto"
60 keystroke "I" using {command down, shift down}
61 delay 1
62 keystroke "/" using {control down}
63 tell window "Import Photos"
64 tell sheet 1
65 set value of text field 1 to POSIX path of this_folder
66 click button 1
67 end tell
68 click button 1
69 end tell
70 end tell
71 end tell
72 end import_photos
73
74 on move_files(this_folder, that_folder)
75 tell application "Finder"
76 set the_files to every file in folder (this_folder as string)
77 repeat with i from (count of the_files) to 1 by -1
78 move item i of the_files to folder that_folder with replacing
79 end repeat
80 end tell
81 end move_files
82
83 on close_bluetooth_file_exchange()
84 tell application "System Events"
85 tell process "OBEXAgent" -- that's really Bluetooth File Exchange
86 tell window "Incoming File Transfer"
87 tell button 1
88 click
89 end tell
90 end tell
91 end tell
92 end tell
93 end close_bluetooth_file_exchange
94
95 (* ################################################# *)
96 on log_arguments(this_folder, these_items)
97 set args to {posix_path(this_folder)}
98 repeat with this_item in these_items
99 set args to args & posix_path(this_item)
100 end repeat
101
102 --do shell script "show_args" & " " & join_args(args)
103 do shell script "/bin/false"
104 end log_arguments
105
106 on posix_path(this_alias)
107 tell application "Finder"
108 set this_path to POSIX path of this_alias
109 end tell
110 return replace_chars(this_path, " ", "\\ ")
111 end posix_path
112
113 on join_args(this_list)
114 set old_delimiter to AppleScript's text item delimiters
115 set AppleScript's text item delimiters to " "
116 set this_text to this_list as string
117 set AppleScript's text item delimiters to old_delimiter
118 return this_text
119 end join_args
120
121 on replace_chars(this_text, search_string, replacement_string)
122 set old_delimiter to AppleScript's text item delimiters
123 set AppleScript's text item delimiters to the search_string
124 set the item_list to every text item of this_text
125 set AppleScript's text item delimiters to the replacement_string
126 set this_text to the item_list as string
127 set AppleScript's text item delimiters to old_delimiter
128 return this_text
129 end replace_chars
brian d foy is a Perl trainer for Stonehenge Consulting Services and is the publisher of The Perl Review.
Return to digitalmedia.oreilly.com