I love iTunes, but it has a number of rough edges that leave me constantly praying that a major revision will come along one day soon. Most of my complaints have to do with it being simultaneously the “only way to manage music and podcasts on the Mac” and yet being frustratingly feature-incomplete in some important ways. For instance, it does a great job of downloading my podcasts, but neglects to put them on my iPod until I either unplug and replug it, or else manually select “Sync.” Inevitably, I end up thinking that since iTunes and my iPod have been making out all weekend, surely those new podcasts are on the iPod. It’s not until I’m at the gym with nothing to listen to that I realize I’ve been sync-jacked!
But that’s a complaint for another time. Today I’d like to highlight an infuriating behavior that iTunes has, of blocking the processing of all incoming AppleEvents while its preferences dialog is being displayed. What does this mean in most practical terms? iTunes can’t respond to AppleScript while the Preferences window is open. So if you run some script that asks for the name of the current track, for instance, it will hang and give the running app a spinning beachball while you wonder what the heck is going on. Finally you switch to iTunes and realize that you were fiddling with preferences yesterday when you got distracted and switched to another application. Close the dialog, and life goes on as usual.
This is bad enough when it’s inflicted on yourself as the script author and user, but it’s worse when your script is either shared with other users, or included as part of the functional infrastructure for an application. With iTunes integration popping up everywhere, it stands to reason that any developer who uses AppleScript to achieve the integration runs the risk of answering support call from users who have run into this iTunes freeze-out behavior.
The solution? Apple should fix iTunes so it doesn’t freeze-out AppleEvent processing while displaying the Preferences dialog.
The solution for people who live in the real world? We have a few techniques to choose from.
Time Out Gracefully
The first is to use AppleScript’s “with timeout” clause to seriously shorten the length of time it will hang out waiting for iTunes to wake up. We can pick a relatively short period of time in seconds that still constitutes an eternity in scripting time:
try
with timeout of 2 seconds
activate application "iTunes"
end timeout
on error
display dialog "iTunes is Frozen, try closing the Preferences window!"
end try
Go ahead, try it with the iTunes Preferences window open. This eliminates the outright freeze that users would experience otherwise.
But it feels risky to estimate with confidence the precise number of seconds that is “long enough, but not too long.” Maybe the user just has a really slow Mac, or something unusual happens to slow things down one out of ten times. For many iTunes scripting purposes, activating iTunes and bringing it to the front is part of the procedure. If iTunes is frontmost, then the user will see that the Preferences dialog is open and (hopefully) intuit that they must dismiss the dialog to let the scripted action carry on. The problem is, merely activating iTunes with AppleScript requires that it respond to AppleEvents! As the example script above shows, we can’t even bring it forward when it’s frozen. Or can we?
Blame It On iTunes
We know the user can bring it forward, so how do we simulate that from a script? If we keep iTunes itself out of the scripting loop, we can enlist the services of the system to change the frontmost application:
tell application "System Events"
set frontmost of application process "iTunes" to true
end tell
Try this script with iTunes Preferences open and you’ll see that it zips to the fore, regardless of the frozen state. By putting this script command first in your iTunes-activating script, you can ensure that users will at least be confronted by the offending Preferences window, instead of staring at a spinning beachball in your application.
Not Give A Damn
Finally, we have the option of sort of throwing our scripting requests out to the world with little regard for their success or failure. For programmer types you can think of this as roughly equivalent to weak-linking a particular system API. If something neat can be done, fine we want it. Otherwise, we’ll keep running business as usual. The “ignoring application responses” clause tells AppleScript to send events but to disregard responses. For instance, the following script asks iTunes to skip to the next track, but shed no tears if that can’t be done right this second:
ignoring application responses
tell application "iTunes" to next track
end ignoring
The big negative here of course is the script gives no feedback to the user about the success or failure of the intended action. It also silently queues up an action that might disturb the user later. For instance, when she switches to iTunes and closes the Preferences dialog, suddenly her favorite song gets skipped over.
In Closing
As I said, the solution here is for iTunes to grow up and be a real 21st Century application. At least in terms of modelessness. But for anybody trying to work around the failings of iTunes or other event-blocking applications, I hope these tips will come in handy.