Muffinresearch Labs by Stuart Colville

Spotify ScreenSaver Toggle with D-bus | Comments (10)

Posted in Linux/Unix, Music on 3rd November 2010, 12:10 am by

UPDATE! – see This post for the latest version of the spotify screensaver toggle

The latest version of the spotify client for linux (0.4.8) has added partial mpris support.

This is great as it means controlling spotify via D-bus is far easier than the previous hacks using wmctrl and xvkbd.

I’ve updated the screensaver toggle script to use spotify’s D-bus interface to play/pause spotify. This also means that I’ve been able to de-couple the script from having to run the screensaver which a big improvement on the old version.

To run it just add the script to your start-up items.

Edit Startup Program Dialogue

Here’s the code, which is a lot more concise compared to the previous version.

Updated to use Mpris2 as of revision 9 for v0.4.8.306…

#!/usr/bin/env python

"""
Title: Spotify Screensaver Toggle
Author: Stuart Colville, http://muffinresearch.co.uk/
License: BSD

Requires Spotify Linux Preview.

Usage:

 * Save the script
 * Ensure it is executable: chmod +x /path/to/script
 * Add the script to your start-up items. 

"""

import dbus
import gobject
from dbus.mainloop.glib import DBusGMainLoop

class SpotifyScreenSaverPause(object):
    """This pauses and plays spotify when the screensaver is activated"""

    def __init__(self):
        """init class."""
        dbus_loop = DBusGMainLoop(set_as_default=True)
        self.bus = dbus.SessionBus(mainloop = dbus_loop)
        self.loop = gobject.MainLoop()
        self.start_listen()
        self.was_playing = 0
        self.loop.run()

    def start_listen(self):
        """Listen to screensaver ActiveChanged events from dbus."""
        screensaver = self.bus.get_object('org.gnome.ScreenSaver',
                                                  '/org/gnome/ScreenSaver')
        screensaver.connect_to_signal("ActiveChanged", self.play_pause)

    def play_pause(self, *args, **kwargs):
        """Toggle play/pause."""
        try:
            spotify = self.bus.get_object("org.mpris.MediaPlayer2.spotify", "/")
            spotify_iface = dbus.Interface(spotify, dbus_interface="org.freedesktop.MediaPlayer2")
            if self.was_playing:
                spotify_iface.Play()
                self.was_playing = 0
            else:
                if spotify_iface.GetMetadata():
                    self.was_playing = 1
                spotify_iface.Pause()
        except dbus.exceptions.DBusException, e:
            # Ignore exception caused by spotify not being started.
            if e.get_dbus_name() != 'org.freedesktop.DBus.Error.ServiceUnknown':
                raise

if __name__ == "__main__":
    ss = SpotifyScreenSaverPause()

See the latest version of the code here Spotify Screensaver Toggle

If you want to investigate what’s possible with D-bus and spotify, D-feet is an excellent tool for investigating D-bus:

D-feet D-bus DebuggerClick for a larger version.

Further Examples

Here’s an example to get the metadata from a track currently playing. (Note: if Spotify isn’t playing this will be an empty dictionary.)

>>> import dbus
>>> bus = dbus.SessionBus()
>>> spotify = bus.get_object("org.mpris.spotify", "/")
>>> spotify_iface = dbus.Interface(spotify, dbus_interface="org.freedesktop.MediaPlayer")
>>> spotify_iface.GetMetadata()
dbus.Dictionary({dbus.String(u'album'): dbus.String(u'Getz/Gilberto #2', variant_level=1), dbus.String(u'title'): dbus.String(u"Here's That Rainy Day", variant_level=1), dbus.String(u'artist'): dbus.String(u'Stan Getz', variant_level=1), dbus.String(u'year'): dbus.Int32(1964, variant_level=1), dbus.String(u'location'): dbus.String(u'spotify:track:1RIfT0Y0TwP2M38pLLfDeJ', variant_level=1), dbus.String(u'time'): dbus.UInt32(242L, variant_level=1), dbus.String(u'tracknumber'): dbus.Int32(4, variant_level=1)}, signature=dbus.Signature('sv'))
>>> print spotify_iface.GetMetadata().get("title")
Here's That Rainy Day
>>> print spotify_iface.GetMetadata().get("location")
spotify:track:1BTO5gV9xSAB7EOBKSlcFY
>>>

With the latest Spotify update to use MPRIS2 (0.4.8.306.xxx) – the TrackChanged signal has gone and there’s no PropertiesChanged signal as per MPRIS2 spec http://www.mpris.org/2.0/spec/ I’ll update the notification example script below as soon as that is rectified. It was there all along. I’ve updated the code to reflect the use of PropertiesChanged

To take that example further here’s a demo that listens to the TrackChangedPropertiesChanged event. It uses the notification id so we can replace the notification details as you move through the tracks. This avoids waiting for the timeout before displaying the current track information.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Title: Spotify Notification Demo
Author: Stuart Colville, http://muffinresearch.co.uk
License: BSD

"""

import dbus
import gobject
from dbus.mainloop.glib import DBusGMainLoop
import pynotify

class SpotifyNotifier(object):

    def __init__(self):
        """initialise."""
        bus_loop = DBusGMainLoop(set_as_default=True)
        bus = dbus.SessionBus(mainloop=bus_loop)
        loop = gobject.MainLoop()
        self.spotify = bus.get_object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
        self.spotify.connect_to_signal("PropertiesChanged", self.track_changed)
        self.notify_id = None
        loop.run()

    def track_changed(self, interface, changed_props, invalidated_props):
        """Handle track changes."""
        metadata = changed_props.get("Metadata", {})
        if metadata:
            if pynotify.init("Spotify Notifier Demo"):

                title = unicode(metadata.get("xesam:title").encode("latin1"))
                album = unicode(metadata.get("xesam:album").encode("latin1"))
                artist = unicode(metadata.get("xesam:artist").encode("latin1"))

                alert = pynotify.Notification(title,
                                 "by %s from %s" % (artist, album))
                if self.notify_id:
                    alert.props.id = self.notify_id
                alert.set_urgency (pynotify.URGENCY_NORMAL)
                alert.show()
                self.notify_id = alert.props.id

if __name__ == "__main__":
    SpotifyNotifier()

Note this notification demo assumes spotify is running to set-up the listener, so it’s not something you can just set running at start-up unless you use it to wrap the start-up of spotify.

Spotify Notifications of Track Changes

Both scripts are BSD licensed so feel free to use and adapt to your own purposes.

Post Tools

Comments: Add yours

1. On November 3rd, 2010 at 11:10 am Ubuntu: Spotify Screensaver Toggle by Stuart Colville said:

[...] version: I’ve updated the script to use D-bus with spotify for Linux version 0.4.8 see Spotify Screensaver Toggle with D-bus for further [...]

2. On November 4th, 2010 at 9:11 am Stuart Colville said:

Update: Fixed the basis on which the flag is set for was_playing.

3. On November 5th, 2010 at 10:49 am Steve said:

By modifying the relevant section as so:

def start_listen(self):
        """Listen to screensaver ActiveChanged events from dbus."""
        screensaver = self.bus.get_object('org.kde.screensaver',
                                                  '/ScreenSaver')
        screensaver.connect_to_signal("ActiveChanged", self.play_pause)

Then it works with KDE too.

Cheers,

Steve

4. On November 5th, 2010 at 11:36 am Steve said:

I’ve modified your notification script for KDE too:


#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Title: Spotify Notification Demo
Author: Stuart Colville, http://muffinresearch.co.uk
License: BSD

"""

import dbus
import gobject
from dbus.mainloop.glib import DBusGMainLoop
import pynotify

class SpotifyNotifier(object):

    def __init__(self):
        """initialise."""
        bus_loop = DBusGMainLoop(set_as_default=True)
        bus = dbus.SessionBus(mainloop=bus_loop)
        loop = gobject.MainLoop()
        self.spotify = bus.get_object("org.mpris.spotify", "/")
        self.spotify.connect_to_signal("TrackChange", self.track_changed)
        self.notify_id = None
        loop.run()

    def track_changed(self, metadata):
        """Handle track changes."""
        if metadata:
            if pynotify.init("Spotify Notifier Demo"):

                title = unicode(metadata.get("title").encode("latin-1"), "utf-8")
                album = unicode(metadata.get("album").encode("latin-1"), "utf-8")
                artist = unicode(metadata.get("artist").encode("latin-1"), "utf-8")

                knotify = dbus.SessionBus().get_object("org.kde.knotify", "/Notify")

                knotify.event("warning", "kde", [], title, u"by %s from %s" % (artist, album), [], [], 0, 0, dbus_interface="org.kde.KNotify")

if __name__ == "__main__":
    SpotifyNotifier()

Cheers,

Steve

5. On November 5th, 2010 at 12:53 pm Stuart Colville said:

@Steve: Nice work, thanks! Seems it would be nice to be able to handle KDE or Gnome in a single script. I’ll look into doing that.

Re: the notification example, if you’re notifying via D-bus then the pynotify parts are redundant. In fact I should change the example to use D-bus nofications and avoid the extra dependency.

6. On November 5th, 2010 at 3:45 pm Steve said:

aye, I realized that after I had posted the script. I’ve gone through and removed them here, but hadn’t reposted as it was only redundancy.

One problem is that the notifications stay in the KNotify area indefinitely. They’re not on show, but if you click the ! then they are all there. Looking at using knotify.update to fix, will post here if I get anywhere.

I’d have modified it to do both gnome and KDE, but my knowledge is very basic. This is the first time I’ve really delved into the D-bus arena!

7. On November 11th, 2010 at 11:38 am Stuart Colville said:

Scripts updated for MPRIS2 (rev9) with the release of Spotify v0.4.8.306.xxx

In a nutshell, there’s some changes to the namespaces used.
TrackChanged is gone (was part of the partial MPRIS 1 support) and hasn’t been replaced with PropertiesChanged as per http://www.mpris.org/2.0/spec/

Use rev 8 if you are still using the older version (0.4.8), in which case the notification example will still work.

8. On March 22nd, 2011 at 8:57 pm Stuart Colville said:

As noted above the track change notify example has been updated to use the PropertiesChanged signal which is there, it’s just not visible in D-feet. :/

This now works for the current version of the linux spotify client.

9. On March 23rd, 2011 at 1:55 am Linux: Spotify Track Notifier with added D-Bus love by Stuart Colville said:

[...] recently updated the spotify notifier example I’d previously posted as an example along with my spotify screensaver script. First, I made an update to have it listen to the PropertiesChanged signal. Annoyingly for some [...]

10. On October 27th, 2011 at 10:07 pm Linux: Spotify Screensaver D-Bus Script update by Stuart Colville said:

[...] on from my previous post about a script to Pause and Play tracks on spotify based on the status of the screensaver as [...]







XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



GNU screen: open tab in current working directory|(1)

A nice trick for having screen open a new tab in the same directory as the one you’re currently in. To use it add it to your .screenrc

# Open new window in current dir.
bind c stuff "screen -X chdir \$PWD;screen^M"
bind ^c stuff "screen -X chdir \$PWD;screen^M"

Hat tip: mteckert on SuperUser.com

Ubuntu: add-apt-repository: command not found|(2)

When you’re using a minimal Ubuntu install if you find the ‘add-apt-repository’ command is missing (it’s useful for adding PPAs and other repositories), then simply run:

sudo apt-get install python-software-properties

Photos on Flickr

© Copyright 2004-12 Stuart Colville, all rights reserved. May contain traces of Muffin. Powered by WordPress. Hosting by Slicehost.com This page was baked in 0.523s.