Usually I intro with some inane comment like:

There are two types of people - those who love Amarok, and those that don't matter.

But now I get to say:

I use Amarok, as recommended (and generally given fan-boy loving) by Wil Wheaton.

Anyway, I use Amarok (formerly amaroK), and I love that it makes exploring my music fun.  My noisy work environment (grr!) means that I'm spending almost all my time listening to music, which has certainly made me appreciate Amarok more.  But occasionally I'm summoned from my other, productive world by real-world "needs" like food, drink, the toilet, and having to find out what someone means when there's no spec to consult (grr!).

Being a former systems administrator (and, indeed, a former card-carrying security specialist - the card is now a bookmark...), I lock my console for even the smallest interruption.  After the first few hundred interruptions (ie, the first two or three days), I got irritated by not having paused my music and having locked my screen and having to unlock it, pause my music, and lock again.  So, I wrote something to automatically pause when I lock my screen - I'm using GNOME's screensaver (aka gnome-screensaver) on Ubuntu.

Unlike xscreensaver, it doesn't have a -watch option - you have to listen to dbus events.  Hint to gnome-screensaver people - dbus is a nice behind-the-scenes way of doing things, but sometimes it is nice to have a specific way to watch for things.  Even if it just runs dbus-monitor with the right commands for you.  Let's not forget our Unix heritage...

Getting the pausing working from dbus messages was actually quite simple - I just combined a Perl regex from one source, and Amarok command line options from another, in a simple Python program:

#!/usr/bin/env python

import subprocess
import re

DBUS_MONITOR = ["dbus-monitor", "--session",
    "type='signal',interface='org.gnome.ScreenSaver',member='SessionIdleChanged'"]
PAUSE_AMAROK = ["amarok", "--pause"]
PLAY_AMAROK = ["amarok", "--play-pause"]

screensaver_on = re.compile("boolean true")
screensaver_off = re.compile("boolean false")

def main():
    a = subprocess.Popen(DBUS_MONITOR, bufsize=1,
        stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
        close_fds=True)
    out = a.stdout

    while a.poll() is None:
        line = out.readline()
        if screensaver_on.search(line):
            subprocess.Popen(PAUSE_AMAROK).communicate()
        if screensaver_off.search(line):
            subprocess.Popen(PLAY_AMAROK).communicate()

Simply, dbus-monitor watches the dbus events and delivers the events that are asked for (otherwise all of them), and send them to stdout.  When the screen saver turns on, I tell Amarok to pause.  When it turns back off, I tell Amarok to unpause.  To be utterly random, I used the subprocess module to call dbus-monitor and Amarok's command line.

Amarok also offers a DCOP interface to tell it what to do and find out what it is doing.  Between the dbus and dcop Python modules, we could get rid of all the silly command line stuff.  But it works fine now.  (And since dbus is replacing DCOP in KDE4, there will almost certainly be a Amarok plugin built-in to do this.)

I also added simple Python daemonising code, stolen from the ActiveState Python Cookbook, so that I can just fire-and-forget it:

def daemonize(func):
    import os
    import sys
    try: 
        pid = os.fork() 
        if pid > 0:
            # exit first parent
            sys.exit(0) 
    except OSError, e: 
        print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror) 
        sys.exit(1)

    # decouple from parent environment
    os.chdir("/") 
    os.setsid() 
    os.umask(0) 

    # do second fork
    try: 
        pid = os.fork() 
        if pid > 0:
            # exit from second parent, print eventual PID before
            print "Daemon PID %d" % pid 
            sys.exit(0) 
    except OSError, e: 
        print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror) 
        sys.exit(1) 

    # start the daemon main loop
    func()

if __name__ == "__main__":
    daemonize(main)

4 old-style comments

  1. wjvMarch 20, 2007 at 04:33 PM.

    Actually, there are two kinds of people: Those who love Amarok, and those who use systems on which iTunes runs natively. :-) The general concept of "type managers" (http://www.icefox.net/articles/typemanager.php) is very interesting, though. I think we'll see more of their kind as we head off into metadata-indexed filesystems on ever-bigger hard drives. Oh, yes, and I love Amarok too. (The album, not the software package.)
  2. Christopher ArndtMarch 20, 2007 at 04:46 PM.

    Why do you use regular expressions for simple substring matching?

    This could be easily done with the "in" operator as well:

    if "boolean true" in line:  
    pause()
    elif "boolean false" in line:
    play()

    Also, did you know that there are Python bindings for dbus (though they are probably overkill for the taks at hand):

    http://www.freedesktop.org/wiki/Software_2fDBusBindings
    Chris 
    BTW: WYSIWYG HTML editors suck
  3. Samat JainMarch 20, 2007 at 08:34 PM.

    Another why question: why use subprocess and call dbus-monitor instead of python's D-BUS bindings?
  4. Bradley WhittingtonApril 04, 2007 at 01:21 PM.

    A much neater solution (for rhythmbox, of course):

    import dbus
    import dbus.glib

    def RBconnect(session_bus):
    proxy_obj = session_bus.get_object(
    'org.gnome.Rhythmbox', '/org/gnome/Rhythmbox/Player')
    player = dbus.Interface(proxy_obj, 'org.gnome.Rhythmbox.Player')
    return player

    def screensaver_changed(state):
    """This method is called when the screensaver starts/stops """
    player.playPause(state)

    session_bus = dbus.SessionBus()
    player = RBconnect(session_bus)
    session_bus.add_signal_receiver(screensaver_changed,'SessionIdleChanged','org.gnome.ScreenSaver')


    def main():
    import gobject
    loop = gobject.MainLoop()
    loop.run()
blog comments powered by Disqus