How to mute Spotify adverts automatically
latest update: Part Two of this series shows you how to mute adverts in new releases of Spotify that pause adverts when the system volume is muted.
I’ve been using Spotify for about 2 months now, and as I’ve mentioned before, it rocks the part-A!
Literally, as it turns out. I’ve been using Spotify to DJ several parties for friends over the last few weeks, including our fab New Years Eve house party in London. In the good ol’ days I’d spend several hours beforehand building an mp3 playlist and copying all the tunes I wanted to play that night onto a removable disk. Now I just build a playlist for the first hour or so, then carry on picking tracks on the fly as the party progresses. On NYE I spun a couple of hideous 80’s classics to get people dancin’, before busting out some dirty, rubbery, spastic electro. But that’s a story for a different post ;-)
However, parties and Spotify adverts don’t mix. So here’s my very quick 'n dirty solution to remove the Spotify adverts on the fly. The essence of it is that I detect the advert based on the growl notification that Spotify issues, and then mute the system volume using a snippet of AppleScript. But before I go any further, there are some caveats which you should know about:
- OSX only
- Doesn’t work when Growl is running, so you have temporarily stop your Growl daemon
- It’s not removing the adverts, only muting the volume for the duration of the advert – so be prepared for 30 secs of silence. (For DJing I just use the ultra-handy LineIn tool from Rogue Amoeba and crossfade to a track off my iPod when the advert starts.
Spotify issues a Growl alert every time a new track starts. In the case of adverts, the alert contains the name of the advert or product, and in most cases a URL to the brand or product. This is great, because all you have to do is intercept the growl notification, make a decision about the “advertyness” of the track and mute the volume if needed. Conversely, if the volume is already muted due to an advert then unmute if the next track doesn’t look like an ad. Simple.
The way to intercept the Growl notifications is to attach a listener to the NSDistributedNotificationCenter in OSX, which is the broadcast channel via which messages are passed between applications. I chose MacRuby for implementing my script because it’s a port of the Ruby language written in Object C (i.e. the root object is NSObject) which means I get full access to the underlying system in OSX.
However, the problem mentioned before is that attaching a notification listener seems to stop all notifications with name “GrowlNotification” from getting through to growl. I may be doin it rong, so if you figure it out then please give me a buzz. [In any event, music is way more important than growl, right?]
To make things even easier I used a fantastic little MacRuby library called HotCocoa, which provides templates to programmatically construct common interface components. This means that attaching a listener to NSDistributedNotificationCenter becomes one line of Ruby. Schweet! This post to the HotCocoa dev forums explains how to use the recently-added on_notification method to intercept alerts.
[Note: when I installed MacRuby from dmg, the version of HotCocoa supplied didn’t have on_notification yet, so I ended up building MacRuby from source. I dunno if this is still the situation.]
So, all that remains is to share the code! Feel free to give me a shout if you have any questions or know how to get Growl and the script to play together.
Oh and Sven, here’s your code post, so stop whining already ;-) And no, I’m not going to open-source the engine until it has pagination, SEO urls a captcha to stop the goddamn comment spammers. Wanna help?
P.
#!/usr/bin/env macrubyrequire 'hotcocoa’
include HotCocoadef get_output_volume `osascript -e 'get volume settings’`.match(/output volume\:([0-9]+)\,/)[1].to_i
enddef is_ad(desc) return true if desc =~ /http\:\/\// || desc =~ /www\..\.co/ || desc =~ /spotify\:album\:./ || desc =~ /Smoke Alarm/ || desc =~/FCO/ false
endmute = false
ad = false
volume = get_output_volume
semaphore = Mutex.newapplication do on_notification :distributed=>true, :named=> 'GrowlNotification’, :when_suspended=>:coalesce do |note| if note.userInfo[“ApplicationName”] "Spotify" && note.userInfo["NotificationName"] “Currently playing track” # wrap the logic in a mutex otherwise two simultaneous notifications could put # the volume and mute variables into inconsistent state. semaphore.synchronize { ad = is_ad(note.userInfo[“NotificationDescription”]) if ad && !mute puts “ad detected, muting volume” current_volume = get_output_volume unless current_volume == 0 volume = current_volume `osascript -e 'set volume output volume 0’` end mute = true elsif ad && mute puts “ad detected, already mute, doing nothing” elsif !ad && mute puts “track doesn’t look like an ad, unmuting to volume=#{volume}” `osascript -e 'set volume output volume #{volume}’` unless get_output_volume > 0 mute = false elsif !ad && !mute puts “not an ad, not mute, doing nothing” end } end end
end
-
David 09:15 on 12 January 2009
Doesn’t work.
-
Peter 17:51 on 12 January 2009
Hi David, it does work cos I’m using it right now :-) If I can help then drop me a line.
-
Tobias 21:46 on 14 January 2009
This is an alternative I just wrote, before I found this post. It uses the feature that growl can log to system.log. Just proof of concept so far.
Small ruby script:
- Enable growl logging with this, and restart growl.
- defaults write com.Growl.GrowlHelperApp GrowlLoggingEnabled -int 1
IO.popen('tail -f /var/log/system.log’) do|f| while line = f.gets if line =~ /Spotify:/ puts “SPOTIFY #{line}” if line =~ /bredbandsbolaget/ # och andra regexpar för reklam `echo “set volume 1”|/usr/bin/osascript` else `echo “set volume 7”|/usr/bin/osascript` end end end
end -
Peter 21:59 on 14 January 2009
@Tobias: Thanks for your input!
I also considered this approach when I first set out. Spotify itself logs verbosely to stdout, which makes it pretty simple to determine when the next track is going to be an advert. Look in /var/log/system.log for entries like “Entering ad break”.
However, (for obvious reasons) the system log file writer buffers the writes, so you have no guarantee as to when your log entries will actually appear in the file. Most of the time the log entries only appear in system.log several seconds after the event.
This rules out monitoring of stdout, since you need to detect the advert immediately or the muter is of no use.
-
Petter 13:29 on 15 January 2009
How come you didn’t just get a dirt cheap day pass for your party!? costs like.. a beer?
-
chris 16:38 on 16 January 2009
Hey there, tried to implement your code, but I’m struggling. I’m not much of a coder, any help you could give me would be appreciated. thanks
