Growling the Current Song in a Mix

Using AppleScript, Osaka, iTunes, and Growl

Every week, I listen to Uplifting Only on SoundCloud. It is a two-hour mix of Orchestral Uplifting and Uplifting Trance music.

Uplifting Only on SoundCloud

Recently, I have been able to get this SoundCloud feed into iTunes, so new episodes will be downloaded automatically, and I can listen while offline.

Since iTunes can be controlled via AppleScript, I want to create a script that notifies me when the song changes, so I don’t have to manually check for the playing song in iTunes.


The AppleScript

Apple provides an AppleScript editor, which can be used to run and debug AppleScripts.

One thing I like about AppleScript is that it sometimes feels like writing plain English.

However, it is only good for simple automation tasks. It lacks many features crucial for computation or text processing…

Anyway, to get the current song position in iTunes, we can use this script:

tell application "iTunes" to get the player position

It will return a real number in seconds. See how it feels like plain English.

Now, to get the current track’s long description, including the tracklist:

tell application "iTunes" to get the current track’s long description

You can see the example result in the screenshot above.


Welcome to Osaka

Now there is a gem called osaka that lets you run AppleScript from Ruby. Another gem, growl, lets you send a Growl notification. So let’s install them!

gem install osaka growl

Here is some code to get the player position and the track’s description from iTunes in Ruby. It’s quite straightforward:

Parsing the Track’s Description

The track’s description looks like this:

[…] To sign up for weekly tracklist emails, go to upliftingonly.com/#/signup .
Each week, each Wednesday, a new episode airs on DI.fm (Epic Trance channel) at 1200 EST/EDT, which usually corresponds to 1800 CET / 2130 IST. It is posted to Soundcloud, Mixcloud, & iTunes as a podcast soon thereafter.
TRACKLIST:
1. [0:00:31]: Sebastian Brushwood feat. Anthya — Lead Me On (Dub Mix) [Beyond the Stars]
2. [0:03:49]: PRE-RELEASE PICK: Michael Flint — Divination [Beyond the Stars]
3. [0:09:45]: Emanuele Congeddu & ARCZI — Taliyah [Blue Soho]
[…]

The Plan

Note that each song in track list has its own line. It would be convenient to process the description line-by-line.

Each track has a timestamp in form of [H:MM:SS]. I’ll use a Regexp to match for the timestamp against each line, resulting in an array of [<the original text>, <the MatchData>] like this:

I’ll then reject the lines with no MatchData.

Finally, I’ll turn each element into a hash with :time and :text keys, where :time is the number of seconds into the mix.

The Code

Here is the code to accomplish it:

Detecting Song Changes

To detect the song changes, we can periodically get the current player position, while keeping track of the last recorded position.

We can detect for a song change when there is some song whose starting position lies between the last recorded position and the current position.

A song change can be detected if there is a song starting before the current position, but after the last recorded position.

So I created an infinite loop to keep track of the last recorded position and the current position. Each iteration waits for 3 seconds.

If a song change is detected, display a Growl notification and print some log message:

That’s It!

Now each time the song changes, I get a Growl notification.

And here is the whole script.rb in less than 40 lines.