Thursday, January 21, 2010

Message Bus for Gnome Object based Python projects

Whilst developing my latest software project (Rhythmbox plugin Last.fm sync), I have crafted a "Message Bus" based on Gnome GObject. Since I didn't have any experience at all with Gnome GObject previously, it took me sometimes to work out the details of the architecture.  For an example usage, please refer to my Git Repository.

""" 
    GObject based "Message Bus"
    
    @author: Jean-Lou Dupont
"""

import gobject #@UnresolvedImport

class Signals(gobject.GObject):
    """
    List of the application level signals
    """
    __gsignals__ = {

         ## NOTE: customization required here
         ## =================================
         ## Announces changes in the user's Last.fm properties
        "lastfm_username_changed":  (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)) 
        ,'lastfm_password_changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,))

        ## Used to signal a change in the currently playing track
        ,"playing_song_changed":    (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))
        
        ## Used to report a failure when accessing Last.fm web service
        ,'lastfm_request_failed':   (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
        
        ## Used for distributing the results of the query against the Last.fm web service
        ,"user_track_info":         (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))
        
        ## Used to pass around the "shell" global object
        ,"rb_shell":                (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))              
    }

    def __init__(self):
        gobject.GObject.__init__(self)
        
        
class Bus(object):
    """
    Message Bus
    
    Borg Pattern
    """
    _signals=Signals()

    @classmethod
    def emit(cls, name, *pa, **kwa):
        cls._signals.emit(name, *pa, **kwa)

    @classmethod
    def add_emission_hook(cls, name, callback):
        gobject.add_emission_hook(cls._signals, name, callback)
    
mbus=Bus()

## =============================================================== Tests

if __name__=="__main__":

    def callback(signal, data):
        print "callback: signal=%s, data=%s" % (signal, data)
    
    Bus.add_emission_hook("lastfm_username_changed", callback)
    
    Bus.emit("lastfm_username_changed", "jldupont")

Relevant Stackoverflow posts
Here are two Stackoverflow posts in relation with my exercise: question 1, question 2.

Rhythmbox plugin - Syncing with Last.fm

In my quest to manage my music collection seamlessly between media players, I've decided to use Last.Fm as my main database. Last.Fm provides ways to store:

  • History of played tracks
  • Playcount associated with played tracks
  • Tags associated with tracks
  • Playlists
  • etc.
Through their API (see mindmap), it is possible to have access to all this information (and much more).

Rhythmbox 
My music collection is currently organized through Rhythmbox - it wasn't my first choice at the time I've moved from my Windows desktop to my Linux Ubuntu one but it has proven very useful. RB can be extended with Python scripts quite painlessly.
Last.Fm Syncing
The plugin I have developed this time supports the following features:
  • When a track is played, it downloads the corresponding information from the user's Last.Fm account and updates the following fields of the Rhythmbox database:
    • Playcount: if the "last.fm playcount" associated with the track is greater than 0, the local playcount of RB is updated with it
    • Rating: if the "last.fm love" field is set for the track, the local "rating" field of the track is set to 5.0 (5 stars)
With this functionality, it is easier to update a local Rhythmbox media player database with information stored on a user's account on Last.fm.  The standard Last.Fm plugin of Rhythmbox must be enabled and configured correctly.
Installation
The plugin can be installed by following the procedure found at the project's home page. A direct link to the plugin Debian/Ubuntu repository is provided here.
Future
The project is far from being completed - I have many other features in the pipeline. Stay tuned!
Related project
There is a related project to this one: Rhythmbox - Last.Fm Desktop Client plugin.

Feedback is very welcome!

UPDATE: see here for details on the new release.