Every now and then I get this urge to just post a #nowplaying tweet, and at such a time it’s always such a drag to type it in manually. So I thought I’d make a menu item in the Ubuntu sound menu that sends such a tweet automatically. It turned out not to be all so easy at all…
First, how do you add something to the sound menu? The only way I know of at this point is by making your little applet (in this case the #nowplaying button) to pose as a media player. The first step is to write the code itself of course, and mark it executable. (More on this in the latter half of the post.) For now let’s focus on adding something to the sound menu.
There are two steps: make a .desktop file for your script, and add it to the “interested media players” key in dconf. As for the first part: .desktop files are textfiles that function as shortcuts to applications. In the case of my script its contents are
Save it with .desktop extension (in my case nowplaying.desktop) and copy it to the ~/.local/share/applications/ folder.
That done, fire up dconf-editor (from a terminal if it doesn’t show up otherwise). Look for /com/canonical/indicator/sound, and add your .desktop file’s name to the interested-media-players list. In my case, it looks like this: [‘nowplaying.desktop’, ‘banshee.desktop’] (I know it says it should be without the .desktop, but it was like that for me in the first place, so I didn’t feel like changing it.)
If you didn’t mess up anything, your new menu item should immediately show up in the sound menu. I know that if you do it like this, you’ll get the previous/play/next controls showing up for your applet too… To this moment I have no idea how to make them disappear.
With the sound menu item showing up, I set out to write a script to actually send the #nowplaying tweet. A short googling gave me a bunch of simple bash scripts using curl that were supposed to do the trick – except none of them worked. As it turns out, Twitter migrated to a new API that requires damn tedious authentication.
First, I had to log in to dev.twitter.com, “create a new application,” give it read and write permission (since I wanted it to post tweets), and get an access key just for my profile (since I made it for myself).
With those four keys (consumer key, consumer secret, access token, access token secret) in my hands, I started to put together a Python script to do what I wanted. Picked Python, because when I found out about the three-leg authentication required, I realized that there’s no way I was gonna write it myself – ergo I needed a module to do the job for me, and Python had tweepy. I also looked it up if there are any #nowplaying scripts for Python out there, and I found one.
But since my script only needs one very specific function, that is to post a tweet of what’s playing in Banshee when I press the button, I slimmed down the script to the bare minimum.
#!/usr/bin/env python #coding=utf-8 import sys, tweepy, urllib, dbus, pynotify bus = dbus.SessionBus() def _get_dbus_object(path): return bus.get_object("org.bansheeproject.Banshee", path) try: obj = _get_dbus_object("/org/bansheeproject/Banshee/PlayerEngine") if(str(obj.GetCurrentState()) != "playing"): print "died" sys.exit(1) except: sys.exit(1) try: obj = _get_dbus_object("/org/bansheeproject/Banshee/PlayerEngine") data = obj.GetCurrentTrack() except: sys.exit(1) if not "artist" in data.keys() or not "name" in data.keys() or not "album" in data.keys(): sys.exit(1) str = u'#nowplaying '+unicode(data["artist"])+u' - '+unicode(data["name"])+' ('+unicode(data["album"])+')' str = str.encode('utf8') auth = tweepy.OAuthHandler('consumer key', 'consumer secret') auth.set_access_token('access token','access token secret') api = tweepy.API(auth) api.update_status(str) pynotify.init('#nowplaying') pynotify.Notification('#nowplaying','#nowplaying tweet sent').show() sys.exit(0)
Of course, replace the keys and tokens for tweepy.OAuthHandler() and set_access_token() with your own keys.
As you can see, this also displays a notification popup about the tweet. Just remove the pynotify lines if you don’t want that.
Since a good percent of my library is non-English, I needed it to support UTF-8 too. It definitely worked so far.
The str variable declaration (where the text is composed) can be done with str.format() or the %-trickery too, but I realized that just now and I’m lazy to change.