conduit r1391 - in trunk: . conduit/modules/RhythmboxDBusModule
- From: jstowers svn gnome org
- To: svn-commits-list gnome org
- Subject: conduit r1391 - in trunk: . conduit/modules/RhythmboxDBusModule
- Date: Wed, 26 Mar 2008 08:31:51 +0000 (GMT)
Author: jstowers
Date: Wed Mar 26 08:31:51 2008
New Revision: 1391
URL: http://svn.gnome.org/viewvc/conduit?rev=1391&view=rev
Log:
2008-03-26 John Stowers <john stowers gmail com>
* conduit/modules/RhythmboxDBusModule/Makefile.am:
* conduit/modules/RhythmboxDBusModule/RhythmboxDBusModule.py:
* conduit/modules/RhythmboxDBusModule/config.glade: Add experimental
Rhythmbox (via DBus) music dataprovider. Fixes #510127 (Alexandre Rosenfeld)
Added:
trunk/conduit/modules/RhythmboxDBusModule/
trunk/conduit/modules/RhythmboxDBusModule/Makefile.am
trunk/conduit/modules/RhythmboxDBusModule/RhythmboxDBusModule.py
trunk/conduit/modules/RhythmboxDBusModule/config.glade
Modified:
trunk/ChangeLog
Added: trunk/conduit/modules/RhythmboxDBusModule/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/conduit/modules/RhythmboxDBusModule/Makefile.am Wed Mar 26 08:31:51 2008
@@ -0,0 +1,8 @@
+conduit_handlersdir = $(libdir)/conduit/modules/RhythmboxDBusModule
+conduit_handlers_PYTHON = RhythmboxDBusModule.py
+
+conduit_handlers_DATA = config.glade
+EXTRA_DIST = config.glade
+
+clean-local:
+ rm -rf *.pyc *.pyo
Added: trunk/conduit/modules/RhythmboxDBusModule/RhythmboxDBusModule.py
==============================================================================
--- (empty file)
+++ trunk/conduit/modules/RhythmboxDBusModule/RhythmboxDBusModule.py Wed Mar 26 08:31:51 2008
@@ -0,0 +1,257 @@
+"""
+Connects to Rhythmbox with the DBus plugin
+
+Based upon code from
+
+Copyright 2007: John Stowers
+Copyright 2008: Alexandre Rosenfeld
+License: GPLv2
+"""
+import urllib
+import os
+import logging
+log = logging.getLogger("modules.RhythmboxDBus")
+
+import conduit
+import conduit.Exceptions as Exceptions
+import conduit.dataproviders.DataProvider as DataProvider
+import conduit.Utils as Utils
+import conduit.datatypes.Audio as Audio
+import dbus
+try:
+ import dbus.glib
+except ImportError:
+ from dbus.mainloop.glib import DBusGMainLoop
+ DBusGMainLoop(set_as_default=True)
+
+from gettext import gettext as _
+
+MODULES = {
+ "RhythmboxDBusSource" : { "type": "dataprovider" },
+}
+
+#list store column define
+NAME_IDX=0
+CHECK_IDX=1
+
+DBusPath = "com.googecode.airmindprojects.Rhythmbox"
+
+class DBusClosedConnectionError(Exception):
+ '''
+ Raised when a connection to Rhythmbox doesnt exist.
+ '''
+ pass
+
+class DBusConnection:
+ """
+ Shared connection to Rhythmbox DBus. Detects when Rhythmbox is closed and
+ opened.
+ """
+
+ # Shared Rhythmbox interface
+ __dbus_connection = None
+ # Shared DBus Session Bus
+ __session_bus = None
+ # True when the DBus connection is being watched. Made to true on the first
+ # instance created.
+ __watch = False
+
+ def _dbus_connect(self):
+ """
+ Connect to the Rhythmbox DBus object. Only availiable when Rhythmbox
+ is running with the DBus plugin activated.
+ """
+ rb_obj = DBusConnection.__session_bus.get_object(DBusPath, "/")
+ return dbus.Interface(rb_obj, DBusPath)
+
+ def __init__(self):
+ if not DBusConnection.__watch:
+ def watch(name):
+ '''
+ Watch when the DBus connection is availiable, and connects or
+ disconnects accordingly.
+ '''
+ if name:
+ DBusConnection.__dbus_connection = self._dbus_connect()
+ else:
+ DBusConnection.__dbus_connection = None
+ DBusConnection.__session_bus = dbus.SessionBus()
+ # The owner changes when the plugin is activated or deactivated,
+ # including when Rhythmbox starts or shutdown with the plugin active.
+ DBusConnection.__session_bus.watch_name_owner(DBusPath, watch)
+ DBusConnection.__watch = True
+
+
+ def __getattr__(self, attr):
+ """ Delegate access to DBus interface """
+ if not DBusConnection:
+ raise DBusClosedConnectionError()
+ else:
+ return getattr(DBusConnection.__dbus_connection, attr)
+
+ def __setattr__(self, attr, value):
+ """ Delegate access to DBus interface """
+ if not DBusConnection:
+ raise DBusClosedConnectionError()
+ else:
+ return setattr(DBusConnection.__dbus_connection, attr, value)
+
+class RhythmboxDBusSource(DataProvider.DataSource):
+
+ _name_ = _("Rhythmbox Music (DBus)")
+ _description_ = _("Sync songs from your Rhythmbox playlists")
+ _category_ = conduit.dataproviders.CATEGORY_MEDIA
+ _module_type_ = "source"
+ _in_type_ = "file/audio"
+ _out_type_ = "file/audio"
+ _icon_ = "rhythmbox"
+
+ def __init__(self, *args):
+ DataProvider.DataSource.__init__(self)
+ self.dbus = DBusConnection()
+ #Names of the playlists we know
+ self.allPlaylists = []
+ #Names we wish to sync
+ self.playlists = []
+
+ def _get_playlists(self):
+ return [str(p) for p in self.dbus.GetPlaylists()]
+
+ def configure(self, window):
+ import gtk
+ import gobject
+ def col1_toggled_cb(cell, path, model ):
+ #not because we get this cb before change state
+ checked = not cell.get_active()
+ model[path][CHECK_IDX] = checked
+ val = model[path][NAME_IDX]
+ if checked and val not in self.playlists:
+ self.playlists.append(val)
+ elif not checked and val in self.playlists:
+ self.playlists.remove(val)
+
+ log.debug("Toggle '%s' to: %s" % (val, checked))
+ return
+
+ self.allPlaylists = self._get_playlists()
+ tree = Utils.dataprovider_glade_get_widget(
+ __file__,
+ "config.glade",
+ "RBConfigDialog")
+ tagtreeview = tree.get_widget("tagtreeview")
+ #Build a list of all the playlists
+ list_store = gtk.ListStore( gobject.TYPE_STRING, #name
+ gobject.TYPE_BOOLEAN, #active
+ )
+ #Fill the list store, preselect some playlists
+ for p in self.allPlaylists:
+ list_store.append( (p, p in self.playlists) )
+ #Set up the treeview
+ tagtreeview.set_model(list_store)
+ #column 1 is the tag name
+ tagtreeview.append_column( gtk.TreeViewColumn(_("Tag Name"),
+ gtk.CellRendererText(),
+ text=NAME_IDX)
+ )
+ #column 2 is a checkbox for selecting the tag to sync
+ renderer1 = gtk.CellRendererToggle()
+ renderer1.set_property('activatable', True)
+ renderer1.connect( 'toggled', col1_toggled_cb, list_store )
+ tagtreeview.append_column( gtk.TreeViewColumn(_("Enabled"),
+ renderer1,
+ active=CHECK_IDX)
+ )
+
+ dlg = tree.get_widget("RBConfigDialog")
+
+ response = Utils.run_dialog (dlg, window)
+ dlg.destroy()
+
+ def refresh(self):
+ DataProvider.DataSource.refresh(self)
+ try:
+ self.allPlaylists = self._get_playlists()
+ except DBusClosedConnectionError:
+ raise Exceptions.RefreshError("Could not connect to Rhythmbox")
+
+ def get_all(self):
+ DataProvider.DataSource.get_all(self)
+ try:
+ # LUID is now a Rhythmbox ID, that only makes sense to Rhythmbox
+ songs = []
+ #only consider enabled playlists
+ for playlist in [p for p in self.allPlaylists if p in self.playlists]:
+ for song in self.dbus.GetPlaylistTracks(playlist):
+ songs.append(str(song))
+ return songs
+ except DBusClosedConnectionError:
+ raise Exceptions.SyncronizeFatalError()
+
+ def get(self, LUID):
+ try:
+ DataProvider.DataSource.get(self, LUID)
+ songuri = str(self.dbus.GetTrackInfo(LUID, 'location'))
+ f = RhythmboxAudio(URI=songuri)
+ f.set_UID(LUID)
+ f.set_open_URI(songuri)
+
+ return f
+ except DBusClosedConnectionError:
+ raise Exceptions.SyncronizeFatalError()
+
+ def get_configuration(self):
+ return { "playlists" : self.playlists }
+
+ def get_UID(self):
+ return ""
+
+class RhythmboxAudio(Audio.Audio):
+
+ COVERS_PATH = os.path.expanduser("~/.gnome2/rhythmbox/covers/")
+
+ def __init__(self, URI, **kwargs):
+ Audio.Audio.__init__(self, URI, **kwargs)
+ self.dbus = DBusConnection()
+
+ def _get_info(self, info):
+ try:
+ return self.dbus.GetTrackInfo(self.get_UID(), info)
+ except DBusClosedConnectionError:
+ raise Exceptions.SyncronizeFatalError()
+
+ def get_audio_artist(self):
+ return self._get_info('artist')
+
+ def get_audio_album(self):
+ return self._get_info('album')
+
+ def get_audio_title(self):
+ return self._get_info('title')
+
+ def get_audio_duration(self):
+ '''
+ Returns the audio duration in seconds
+ '''
+ return int(self._get_info('duration'))
+
+ def get_audio_rating(self):
+ '''
+ Returns an rating from 0.0 to 5.0
+ '''
+ return float(self._get_info('rating'))
+
+ # This code has been disabled in the DBus plugin
+ #def set_audio_rating(self, value):
+ # self.dbus.SetTrackInfo(self.get_UID(), 'rating', value)
+
+ def get_audio_cover_location(self):
+ '''
+ Returns a valid cover location or None if not availiable
+ '''
+ artist = self.get_audio_artist()
+ album = self.get_audio_album()
+ filename = str(os.path.join(RhythmboxAudio.COVERS_PATH, "%s - %s.jpg" % (artist, album)))
+ if os.path.exists(filename):
+ return filename
+ else:
+ return None
Added: trunk/conduit/modules/RhythmboxDBusModule/config.glade
==============================================================================
--- (empty file)
+++ trunk/conduit/modules/RhythmboxDBusModule/config.glade Wed Mar 26 08:31:51 2008
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+ <widget class="GtkDialog" id="RBConfigDialog">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Rhythmbox Playlists</property>
+ <property name="default_width">250</property>
+ <property name="default_height">350</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="vbox26">
+ <property name="visible">True</property>
+ <property name="spacing">4</property>
+ <child>
+ <widget class="GtkVBox" id="vbox27">
+ <property name="visible">True</property>
+ <property name="border_width">4</property>
+ <property name="spacing">5</property>
+ <child>
+ <widget class="GtkLabel" id="label64">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Synchronize the Following Playlists</b></property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="tagscrolledwindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <widget class="GtkTreeView" id="tagtreeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="hbuttonbox11">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="button28">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="button29">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]