It didn't take long to install RubyOSA on my MacBook and find out how to set up remote scripting on the iBook. I soon set to manipulating my music player remotely in irb:
$ irb -r rubygems -r rbosaAt this point I get an authentication window, and entered the name and password of my user on the iBook. This works, but it would be nice to authenticate automatically. I've tried passing :username and :password to OSA.app, but haven't had much luck with it. If you get stuck with repeated authentication errors, try opening up Keychain Access and removing the assocated entries before you try again. Also, even though I'm not the user controlling this running instance of iTunes, all of these commands work anyway. I guess it's because my user is an administrator.
> itunes = OSA.app('iTunes', :machine=>'casper.local')
> itunes.sourcesJust inspecting the sources isn't going to do it, I need to collect the names to get a meaningful list. Now I can see the sources available in this remote instance of iTunes. Unfortunately, the music server isn't available until I go to the iBook and access the music server manually. Once that's done:
=> #<OSA::ObjectSpecifierList:0x1104888 desired_class=OSA::ITunes::Source>
> itunes.sources.collect{|x|x.name}
=> ["Library", "Radio"]
> itunes.sources.collect{|x|x.name}Now the music server is listed! Let's play with it!
=> ["Library", "Radio", "Music Server"]
> music_server = itunes.sources[2].playlists.firstAha! We can get the playlist from the music server, and search it to find tracks. After that we can just get the first track and call the method play to switch to that track in iTunes. After that track is over, the next song in the global playlist plays, not the next one in the search results. This is a bit disappointing, I wonder if we can do better. Now is probably a good time to examine the documentation:
> music_server.search('bill cosby').collect{|x|x.name}
=> ["200 MPH Car", "Dogs", "My Brother Russell", "My Father", "Snakes and Alligators", "Kindergarten", "Personal Hygiene", "Shop", "Baby", "Driving In San Francisco", "-75 Car", "The Toothache", "Hofstra", "Tonsils", "The Playground", "Lumps", "Go Carts", "Chicken Heart", "Shop", "Special Class", "Niagara Falls"]
> music_server.search('bill cosby').first.play
> quitLooking through the API documentation gives me a little better idea what I can do with this facility. One approach to a temporary playlist might be:
$ rdoc-osa --name iTunes
$ open doc/index.html
$ irb -r rubygems -r rbosaTechnically, this will play the search results sequentially sorted by duration, although it seems pretty heavy-handed. Also, it doesn't return right away. There are doubtless better ways to do this, but I'm not much of a Ruby (or AppleScript) programmer yet.
> t = OSA.app('iTunes', :machine=>'casper.local')
> music_server = t.sources[2].playlists.first
> music_server.search('bill cosby').sort {|a,b| a.duration<=>b.duration}.each {|x| x.play;sleep x.duration}
4 comments:
Nice article :-)
Note that instead of writing
itunes.sources.collect{|x|x.name}
You can write
itunes.sources.every(:name)
Which is both a convenience and a faster shortcut.
Hey that's handy! How did you discover this? I was surprised to learn that this method isn't in the Array or Enumerable API for Ruby, but in OSA::ObjectSpecifierList.
I did discover that Rails has a different expression of the same idiom:
> array.collect &:name
Unfortunately I don't see such a shortcut anywhere in Ruby itself. Not that it would be difficult to add...
Thanks for the tip!
If you're needing a remote iTunes controller, there's several options already available (it's a not uncommon request). Just Google for "itunes remote". netTunes looks particularly nice, although I've not used it myself. Or, if you'd prefer a command line one, feel free to check out my own contribution.
HTH
Nice article, Zack!
On a related note... for those of us stuck on Windows, you can automate and manage iTunes via the COM interface, as explained here.
David
http://rubyonwindows.blogspot.com
Post a Comment