[rhythmbox] Update Coherence UPNP plugin



commit 82672e4aff9b710d382eb405d0ebaaa9272b8e50
Author: Bastien Nocera <hadess hadess net>
Date:   Tue Sep 8 11:13:14 2009 +0100

    Update Coherence UPNP plugin
    
    from the upstream SVN

 plugins/coherence/upnp_coherence/MediaPlayer.py |  131 ++++++++++---
 plugins/coherence/upnp_coherence/MediaStore.py  |   91 +++++++--
 plugins/coherence/upnp_coherence/__init__.py    |  249 ++++++++++++++++++-----
 3 files changed, 369 insertions(+), 102 deletions(-)
---
diff --git a/plugins/coherence/upnp_coherence/MediaPlayer.py b/plugins/coherence/upnp_coherence/MediaPlayer.py
index 0207421..d90b5f4 100644
--- a/plugins/coherence/upnp_coherence/MediaPlayer.py
+++ b/plugins/coherence/upnp_coherence/MediaPlayer.py
@@ -3,14 +3,17 @@
 
 # Copyright 2008, Frank Scholz <coherence beebits net>
 
+import os.path
 import urllib
 
+from twisted.python import failure
+
 import rhythmdb
 
 from coherence.upnp.core.soap_service import errorCode
 from coherence.upnp.core import DIDLLite
 
-import coherence.extern.louie as louie
+import louie
 
 from coherence.extern.simple_plugin import Plugin
 
@@ -27,17 +30,22 @@ class RhythmboxPlayer(log.Loggable):
 
     implements = ['MediaRenderer']
     vendor_value_defaults = {'RenderingControl': {'A_ARG_TYPE_Channel':'Master'},
-                             'AVTransport': {'A_ARG_TYPE_SeekMode':('ABS_TIME','REL_TIME')}}
+                             'AVTransport': {'A_ARG_TYPE_SeekMode':('ABS_TIME','REL_TIME','TRACK_NR')}}
     vendor_range_defaults = {'RenderingControl': {'Volume': {'maximum':100}}}
 
     def __init__(self, device, **kwargs):
         self.warning("__init__ RhythmboxPlayer %r", kwargs)
         self.shell = kwargs['shell']
         self.server = device
+        self.rb_mediaserver = kwargs['rb_mediaserver']
 
         self.player = None
+        self.entry = None
         self.metadata = None
-        self.name = "Rhythmbox on %s" % self.server.coherence.hostname
+        try:
+            self.name = kwargs['name']
+        except KeyError:
+            self.name = "Rhythmbox on %s" % self.server.coherence.hostname
 
         self.player = self.shell.get_player()
         louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self)
@@ -55,11 +63,13 @@ class RhythmboxPlayer(log.Loggable):
 
     def volume_changed(self, player, parameter):
         self.volume = self.player.props.volume
+        self.info('volume_changed to %r', self.volume)
         if self.volume > 0:
             rcs_id = self.server.connection_manager_server.lookup_rcs_id(self.current_connection_id)
             self.server.rendering_control_server.set_variable(rcs_id, 'Volume', self.volume*100)
 
     def playing_song_changed(self, player, entry):
+        self.info("playing_song_changed %r", entry)
         if self.server != None:
             connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id)
         if entry == None:
@@ -69,7 +79,7 @@ class RhythmboxPlayer(log.Loggable):
             self.metadata = None
             self.duration = None
         else:
-            self.id = self.shell.props.db.entry_get (entry, rhythmdb.PROP_ENTRY_ID)
+            id = self.shell.props.db.entry_get (entry, rhythmdb.PROP_ENTRY_ID)
             bitrate = self.shell.props.db.entry_get(entry, rhythmdb.PROP_BITRATE) * 1024 / 8
             # Duration is in HH:MM:SS format
             seconds = self.shell.props.db.entry_get(entry, rhythmdb.PROP_DURATION)
@@ -86,41 +96,62 @@ class RhythmboxPlayer(log.Loggable):
             size = self.shell.props.db.entry_get(entry, rhythmdb.PROP_FILE_SIZE)
 
             # create item
-            item = DIDLLite.MusicTrack(self.id + TRACK_COUNT)
+            item = DIDLLite.MusicTrack(id + TRACK_COUNT,'101')
             item.album = self.shell.props.db.entry_get(entry, rhythmdb.PROP_ALBUM)
             item.artist = self.shell.props.db.entry_get(entry, rhythmdb.PROP_ARTIST)
             item.genre = self.shell.props.db.entry_get(entry, rhythmdb.PROP_GENRE)
             item.originalTrackNumber = str(self.shell.props.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER))
             item.title = self.shell.props.db.entry_get(entry, rhythmdb.PROP_TITLE) # much nicer if it was entry.title
 
-            item.res = []
+            cover = self.shell.props.db.entry_request_extra_metadata(entry, "rb:coverArt-uri")
+            if cover != None:
+                _,ext =  os.path.splitext(cover)
+                item.albumArtURI = ''.join((self.server.coherence.urlbase+str(self.rb_mediaserver.uuid)[5:]+'/'+ str(int(id) + TRACK_COUNT),'?cover',ext))
 
-            uri = self.shell.props.db.entry_get(entry, rhythmdb.PROP_LOCATION)
-            if uri.startswith("file://"):
-                location = unicode(urllib.unquote(uri[len("file://"):]))
+            item.res = []
 
-                # add a fake resource for the moment
-                res = DIDLLite.Resource(location, 'http-get:*:%s:*' % mimetype)
-                if size > 0:
-                    res.size = size
-                if self.duration > 0:
-                    res.duration = self.duration
-                if bitrate > 0:
-                    res.bitrate = str(bitrate)
-                item.res.append(res)
+            location = self.shell.props.db.entry_get(entry, rhythmdb.PROP_LOCATION)
+            if location.startswith("file://"):
+                location = unicode(urllib.unquote(location[len("file://"):]))
+
+            uri = ''.join((self.server.coherence.urlbase+str(self.rb_mediaserver.uuid)[5:]+'/'+ str(int(id) + TRACK_COUNT)))
+
+            res = DIDLLite.Resource(uri, 'http-get:*:%s:*' % mimetype)
+            if size > 0:
+                res.size = size
+            if self.duration > 0:
+                res.duration = self.duration
+            if bitrate > 0:
+                res.bitrate = str(bitrate)
+            item.res.append(res)
+
+            # add internal resource
+            res = DIDLLite.Resource('track-%d' % id, 'rhythmbox:%s:%s:*' % (self.server.coherence.hostname, mimetype))
+            if size > 0:
+                res.size = size
+            if self.duration > 0:
+                res.duration = str(self.duration)
+            if bitrate > 0:
+                res.bitrate = str(bitrate)
+            item.res.append(res)
 
             elt = DIDLLite.DIDLElement()
             elt.addItem(item)
             self.metadata = elt.toString()
             self.entry = entry
             if self.server != None:
+                self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackURI',uri)
+                self.server.av_transport_server.set_variable(connection_id, 'AVTransportURI',uri)
                 self.server.av_transport_server.set_variable(connection_id, 'AVTransportURIMetaData',self.metadata)
                 self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackMetaData',self.metadata)
+            self.info("playing_song_changed %r", self.metadata)
         if self.server != None:
+            self.server.av_transport_server.set_variable(connection_id, 'CurrentTransportActions','PLAY,STOP,PAUSE,SEEK,NEXT,PREVIOUS')
             self.server.av_transport_server.set_variable(connection_id, 'RelativeTimePosition', '00:00:00')
             self.server.av_transport_server.set_variable(connection_id, 'AbsoluteTimePosition', '00:00:00')
 
     def playing_changed(self, player, state):
+        self.info("playing_changed", state)
         if state is True:
             transport_state = 'PLAYING'
         else:
@@ -138,8 +169,10 @@ class RhythmboxPlayer(log.Loggable):
         except:
             duration = None
         self.update_position(position,duration)
+        self.info("playing_changed %r %r ", position, duration)
 
     def elapsed_changed(self, player, time):
+        self.info("elapsed_changed %r %r", player, time)
         try:
             duration = player.get_playing_song_duration()
         except:
@@ -148,6 +181,8 @@ class RhythmboxPlayer(log.Loggable):
 
     def update(self, state):
 
+        self.info("update %r", state)
+
         if state in ('STOPPED','READY'):
             transport_state = 'STOPPED'
         if state == 'PLAYING':
@@ -165,9 +200,11 @@ class RhythmboxPlayer(log.Loggable):
 
 
     def update_position(self, position,duration):
+        self.info("update_position %r %r", position,duration)
+
         if self.server != None:
             connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id)
-            self.server.av_transport_server.set_variable(connection_id, 'CurrentTrack', 0)
+            self.server.av_transport_server.set_variable(connection_id, 'CurrentTrack', 1)
 
         if position is not None:
             m,s = divmod( position, 60)
@@ -189,6 +226,7 @@ class RhythmboxPlayer(log.Loggable):
 
             if self.duration is None:
                 if self.metadata is not None:
+                    self.info("update_position %r", self.metadata)
                     elt = DIDLLite.DIDLElement.fromString(self.metadata)
                     for item in elt:
                         for res in item.findall('res'):
@@ -202,6 +240,7 @@ class RhythmboxPlayer(log.Loggable):
                 self.duration = duration
 
     def load( self, uri, metadata):
+        self.info("player load %r %r", uri, metadata)
         #self.shell.load_uri(uri,play=False)
         self.duration = None
         self.metadata = metadata
@@ -221,13 +260,16 @@ class RhythmboxPlayer(log.Loggable):
                     self.entry = self.shell.props.db.entry_lookup_by_id(int(uri[6:]))
                 else:
                     self.entry = self.shell.props.db.entry_lookup_by_location(uri)
+                self.info("check for entry %r %r %r", self.entry,item.server_uuid,uri)
                 if self.entry == None:
                     if item.server_uuid is not None:
                         entry_type = self.shell.props.db.entry_register_type("CoherenceUpnp:" + item.server_uuid)
                         self.entry = self.shell.props.db.entry_new(entry_type, uri)
+                        self.info("create new entry %r", self.entry)
                     else:
                         entry_type = self.shell.props.db.entry_register_type("CoherencePlayer")
                         self.entry = self.shell.props.db.entry_new(entry_type, uri)
+                        self.info("load and check for entry %r", self.entry)
 
                 duration = None
                 size = None
@@ -281,7 +323,7 @@ class RhythmboxPlayer(log.Loggable):
         self.metadata = metadata
 
         connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id)
-        self.server.av_transport_server.set_variable(connection_id, 'CurrentTransportActions','Play,Stop,Pause,Seek')
+        self.server.av_transport_server.set_variable(connection_id, 'CurrentTransportActions','PLAY,STOP,PAUSE,SEEK,NEXT,PREVIOUS')
         self.server.av_transport_server.set_variable(connection_id, 'NumberOfTracks',1)
         self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackURI',uri)
         self.server.av_transport_server.set_variable(connection_id, 'AVTransportURI',uri)
@@ -297,6 +339,8 @@ class RhythmboxPlayer(log.Loggable):
         self.play()
 
     def stop(self):
+        self.info("player stop")
+
         self.player.stop()
         self.playing = False
         #self.server.av_transport_server.set_variable( \
@@ -304,8 +348,13 @@ class RhythmboxPlayer(log.Loggable):
         #                     'TransportState', 'STOPPED')
 
     def play(self):
+        self.info("player play")
+
         if self.playing == False:
-            self.player.play_entry(self.entry)
+            if self.entry:
+               self.player.play_entry(self.entry)
+            else:
+               self.player.playpause()
             self.playing = True
         else:
             self.player.playpause()
@@ -321,10 +370,10 @@ class RhythmboxPlayer(log.Loggable):
 
     def seek(self, location, old_state):
         """
-        @param location:    simple number = time to seek to, in seconds
-                            +nL = relative seek forward n seconds
+        @param location:    +nL = relative seek forward n seconds
                             -nL = relative seek backwards n seconds
         """
+        self.info("player seek %r", location)
         self.player.seek(location)
         self.server.av_transport_server.set_variable(0, 'TransportState', old_state)
 
@@ -347,9 +396,11 @@ class RhythmboxPlayer(log.Loggable):
 
     def get_volume(self):
         self.volume = self.player.get_volume()
+        self.info("get_volume %r", self.volume)
         return self.volume * 100
 
     def set_volume(self, volume):
+        self.info("set_volume %r", volume)
         volume = int(volume)
         if volume < 0:
             volume=0
@@ -385,6 +436,16 @@ class RhythmboxPlayer(log.Loggable):
         self.play()
         return {}
 
+    def upnp_Previous(self, *args, **kwargs):
+        InstanceID = int(kwargs['InstanceID'])
+        self.player.do_previous()
+        return {}
+
+    def upnp_Next(self, *args, **kwargs):
+        InstanceID = int(kwargs['InstanceID'])
+        self.player.do_next()
+        return {}
+
     def upnp_Pause(self, *args, **kwargs):
         InstanceID = int(kwargs['InstanceID'])
         self.pause()
@@ -400,11 +461,31 @@ class RhythmboxPlayer(log.Loggable):
         Unit = kwargs['Unit']
         Target = kwargs['Target']
         if Unit in ['ABS_TIME','REL_TIME']:
-            old_state = self.server.av_transport_server.get_variable(0, 'TransportState')
+            old_state = self.server.av_transport_server.get_variable('TransportState').value
             self.server.av_transport_server.set_variable(0, 'TransportState', 'TRANSITIONING')
+
+            sign = 1
+            if Target[0] == '+':
+                Target = Target[1:]
+            if Target[0] == '-':
+                Target = Target[1:]
+                sign = -1
             h,m,s = Target.split(':')
             seconds = int(h)*3600 + int(m)*60 + int(s)
-            self.seek(seconds, old_state)
+
+            if Unit == 'ABS_TIME':
+                position = self.player.get_playing_time()
+                self.seek(seconds-position, old_state)
+            elif Unit == 'REL_TIME':
+                self.seek(seconds*sign, old_state)
+        return {}
+
+    def upnp_Next(self,*args,**kwargs):
+        self.player.do_next()
+        return {}
+
+    def upnp_Previous(self,*args,**kwargs):
+        self.player.do_previous()
         return {}
 
     def upnp_SetAVTransportURI(self, *args, **kwargs):
diff --git a/plugins/coherence/upnp_coherence/MediaStore.py b/plugins/coherence/upnp_coherence/MediaStore.py
index 959fa49..0915f5d 100644
--- a/plugins/coherence/upnp_coherence/MediaStore.py
+++ b/plugins/coherence/upnp_coherence/MediaStore.py
@@ -4,9 +4,13 @@
 # Copyright 2007, James Livingston  <doclivingston gmail com>
 # Copyright 2007, Frank Scholz <coherence beebits net>
 
+import os.path
 import rhythmdb
-import coherence.extern.louie as louie
+import louie
 import urllib
+
+from coherence import __version_info__
+
 from coherence.upnp.core import DIDLLite
 
 from coherence.backend import BackendItem, BackendStore
@@ -27,14 +31,14 @@ class Container(BackendItem):
 
     logCategory = 'rb_media_store'
 
-    def __init__(self, id, parent_id, name, children_callback=None):
+    def __init__(self, id, parent_id, name, children_callback=None,store=None,play_container=False):
         self.id = id
         self.parent_id = parent_id
         self.name = name
         self.mimetype = 'directory'
-        self.item = DIDLLite.Container(id, parent_id,self.name)
+        self.store = store
+        self.play_container = play_container
         self.update_id = 0
-        self.item.childCount = 0
         if children_callback != None:
             self.children = children_callback
         else:
@@ -42,7 +46,6 @@ class Container(BackendItem):
 
     def add_child(self, child):
         self.children.append(child)
-        self.item.childCount += 1
 
     def get_children(self,start=0,request_count=0):
         if callable(self.children):
@@ -60,8 +63,13 @@ class Container(BackendItem):
         return len(self.get_children())
 
     def get_item(self, parent_id=None):
-        self.item.childCount = self.get_child_count()
-        return self.item
+        item = DIDLLite.Container(self.id,self.parent_id,self.name)
+        item.childCount = self.get_child_count()
+        if self.store and self.play_container == True:
+            if item.childCount > 0:
+                res = DIDLLite.PlayContainerResource(self.store.server.uuid,cid=self.get_id(),fid=str(TRACK_COUNT + int(self.get_children()[0].get_id())))
+                item.res.append(res)
+        return item
 
     def get_name(self):
         return self.name
@@ -115,6 +123,11 @@ class Album(BackendItem):
 
     def get_item(self, parent_id = AUDIO_ALBUM_CONTAINER_ID):
         item = DIDLLite.MusicAlbum(self.id, parent_id, self.title)
+
+        if __version_info__ >= (0,6,4):
+            if self.get_child_count() > 0:
+                res = DIDLLite.PlayContainerResource(self.store.server.uuid,cid=self.get_id(),fid=str(TRACK_COUNT+int(self.get_children()[0].get_id())))
+                item.res.append(res)
         return item
 
     def get_id(self):
@@ -139,11 +152,22 @@ class Artist(BackendItem):
         query = self.store.db.query_new()
         self.store.db.query_append(query,[rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_TYPE, self.store.db.entry_type_get_by_name('song')],
                                       [rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_ARTIST, self.name])
-        qm = self.store.db.query_model_new(query)
-        self.store.db.do_full_query_async_parsed(qm, query)
+        self.tracks_per_artist_query = self.store.db.query_model_new(query)
+        self.store.db.do_full_query_async_parsed(self.tracks_per_artist_query, query)
 
         self.albums_per_artist_query = self.store.db.property_model_new(rhythmdb.PROP_ALBUM)
-        self.albums_per_artist_query.props.query_model = qm
+        self.albums_per_artist_query.props.query_model = self.tracks_per_artist_query
+
+    def get_artist_all_tracks(self,id):
+        children = []
+
+        def collate (model, path, iter):
+            id = model.get(iter, 0)[0]
+            print id
+            children.append(Track(self.store,id,self.id))
+
+        self.tracks_per_artist_query.foreach(collate)
+        return children
 
     def get_children(self,start=0,request_count=0):
         children = []
@@ -161,6 +185,16 @@ class Artist(BackendItem):
 
         self.albums_per_artist_query.foreach(collate)
 
+        if len(children):
+            all_id = 'artist_all_tracks_%d' % (self.id)
+            if all_id not in self.store.containers:
+                self.store.containers[all_id] = \
+                    Container( all_id, self.id, 'All tracks of %s' % self.name,
+                              children_callback=self.get_artist_all_tracks,
+                              store=self.store,play_container=True)
+
+            children.insert(0,self.store.containers[all_id])
+
         if request_count == 0:
             return children[start:]
         else:
@@ -240,9 +274,12 @@ class Track(BackendItem):
         item.originalTrackNumber = str(self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER))
         item.title = self.store.db.entry_get(entry, rhythmdb.PROP_TITLE) # much nicer if it was entry.title
 
-        #cover = self.store.db.entry_request_extra_metadata(entry, "rb:coverArt")
+        cover = self.store.db.entry_request_extra_metadata(entry, "rb:coverArt-uri")
         #self.warning("cover for %r is %r", item.title, cover)
-        #item.albumArtURI = ## can we somehow store art in the upnp share??
+        if cover != None:
+            _,ext =  os.path.splitext(cover)
+            item.albumArtURI = ''.join((self.get_url(),'?cover',ext))
+
 
         # add http resource
         res = DIDLLite.Resource(self.get_url(), 'http-get:*:%s:*' % mimetype)
@@ -280,22 +317,28 @@ class Track(BackendItem):
         if entry is None:
             entry = self.store.db.entry_lookup_by_id (self.id)
         uri = self.store.db.entry_get(entry, rhythmdb.PROP_LOCATION)
-        self.warning("Track get_path uri = %r", uri)
+        self.info("Track get_path uri = %r", uri)
         location = None
         if uri.startswith("file://"):
             location = unicode(urllib.unquote(uri[len("file://"):]))
-            self.warning("Track get_path location = %r", location)
+            self.info("Track get_path location = %r", location)
 
         return location
 
+    def get_cover(self):
+        entry = self.store.db.entry_lookup_by_id(self.id)
+        cover = self.store.db.entry_request_extra_metadata(entry, "rb:coverArt-uri")
+        return cover
+
+
 class MediaStore(BackendStore):
 
     logCategory = 'rb_media_store'
     implements = ['MediaServer']
 
     def __init__(self, server, **kwargs):
+        BackendStore.__init__(self,server,**kwargs)
         self.warning("__init__ MediaStore %r", kwargs)
-        self.server = server
         self.db = kwargs['db']
         self.plugin = kwargs['plugin']
 
@@ -304,8 +347,6 @@ class MediaStore(BackendStore):
                                  '6': lambda : self.get_by_id(AUDIO_ARTIST_CONTAINER_ID),    # all artists
                                 })
 
-        self.update_id = 0
-
         self.next_id = CONTAINER_COUNT
         self.albums = None
         self.artists = None
@@ -315,7 +356,10 @@ class MediaStore(BackendStore):
         if( len(self.urlbase) > 0 and self.urlbase[len(self.urlbase)-1] != '/'):
             self.urlbase += '/'
 
-        self.name = "Rhythmbox on %s" % self.server.coherence.hostname
+        try:
+            self.name = kwargs['name']
+        except KeyError:
+            self.name = "Rhythmbox on %s" % self.server.coherence.hostname
 
         query = self.db.query_new()
         self.info(query)
@@ -335,7 +379,8 @@ class MediaStore(BackendStore):
 
         self.containers[AUDIO_ALL_CONTAINER_ID] = \
                 Container( AUDIO_ALL_CONTAINER_ID,ROOT_CONTAINER_ID, 'All tracks',
-                          children_callback=self.children_tracks)
+                          children_callback=self.children_tracks,
+                          store=self,play_container=True)
         self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALL_CONTAINER_ID])
 
         self.containers[AUDIO_ALBUM_CONTAINER_ID] = \
@@ -353,6 +398,12 @@ class MediaStore(BackendStore):
     def get_by_id(self,id):
 
         self.info("looking for id %r", id)
+        if isinstance(id, basestring) and id.startswith('artist_all_tracks_'):
+            try:
+                return self.containers[id]
+            except:
+                return None
+
         id = id.split('@',1)
         item_id = id[0]
         item_id = int(item_id)
@@ -424,8 +475,6 @@ class MediaStore(BackendStore):
     def children_artists(self,parent_id):
         artists = []
 
-        self.info('children_artists')
-
         def collate (model, path, iter):
             name = model.get(iter, 0)[0]
             priority = model.get(iter, 1)[0]
diff --git a/plugins/coherence/upnp_coherence/__init__.py b/plugins/coherence/upnp_coherence/__init__.py
index 669d69e..ca1d1ba 100644
--- a/plugins/coherence/upnp_coherence/__init__.py
+++ b/plugins/coherence/upnp_coherence/__init__.py
@@ -10,20 +10,52 @@
 import rhythmdb, rb
 import gobject, gtk
 
-import coherence.extern.louie as louie
+import gconf
 
-from coherence import log
+import louie
 
-# For the icon
-import os.path, urllib, gtk.gdk
+from coherence import log
 
-class CoherencePlugin(rb.Plugin,log.Loggable):
+# for the icon
+import os.path, urllib, gnomevfs, gtk.gdk
+
+# the gconf configuration
+gconf_keys = {
+    'port': "/apps/rhythmbox/plugins/coherence/port",
+    'interface': "/apps/rhythmbox/plugins/coherence/interface",
+    # DMS
+    'dms_uuid': "/apps/rhythmbox/plugins/coherence/dms/uuid",
+    'dms_active': "/apps/rhythmbox/plugins/coherence/dms/active",
+    'dms_version': "/apps/rhythmbox/plugins/coherence/dms/version",
+    'dms_name': "/apps/rhythmbox/plugins/coherence/dms/name",
+    # DMR
+    'dmr_uuid': "/apps/rhythmbox/plugins/coherence/dmr/uuid",
+    'dmr_active': "/apps/rhythmbox/plugins/coherence/dmr/active",
+    'dmr_version': "/apps/rhythmbox/plugins/coherence/dmr/version",
+    'dmr_name': "/apps/rhythmbox/plugins/coherence/dmr/name",
+    # DMC
+    'dmc_active': "/apps/rhythmbox/plugins/coherence/dmc/active",
+}
+
+class CoherencePlugin(rb.Plugin, log.Loggable):
 
     logCategory = 'rb_coherence_plugin'
 
     def __init__(self):
         rb.Plugin.__init__(self)
         self.coherence = None
+        self.config = gconf.client_get_default()
+
+        if self.config.get(gconf_keys['dmc_active']) is None:
+            # key not yet found represented by "None"
+            self._set_defaults()
+
+    def _set_defaults(self):
+        for a in ('r', 's'):
+            self.config.set_bool(gconf_keys['dm%s_active' % a], True)
+            self.config.set_int(gconf_keys['dm%s_version' % a], 2)
+
+        self.config.set_bool(gconf_keys['dmc_active'], True)
 
     def activate(self, shell):
         from twisted.internet import gtk2reactor
@@ -47,72 +79,112 @@ class CoherencePlugin(rb.Plugin,log.Loggable):
         face_path = os.path.join(os.path.expanduser('~'), ".face")
         if os.path.exists(face_path):
             url = "file://" + urllib.pathname2url(face_path)
-	    try:
-		    import gio
-		    f = gio.File(url)
-		    fi = f.query_info(gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)
-		    ctype = fi.get_attribute_string(gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)
-		    mimetype = gio.content_type_get_mime_type(ctype)
-	    except:
-		    import gnomevfs
-		    mimetype = gnomevfs.get_mime_type(url)
-
+            mimetype = gnomevfs.get_mime_type(url)
             pixbuf = gtk.gdk.pixbuf_new_from_file(face_path)
             width = "%s" % pixbuf.get_width()
             height = "%s" % pixbuf.get_height()
             depth = '24'
             the_icon = {
-                'url':url,
-                'mimetype':mimetype,
-                'width':width,
-                'height':height,
-                'depth':depth
+                'url': url,
+                'mimetype': mimetype,
+                'width': width,
+                'height': height,
+                'depth': depth
                 }
-        else:
-            the_icon = None
 
-        # create our own media server
-        from coherence.upnp.devices.media_server import MediaServer
-        from MediaStore import MediaStore
-        if the_icon:
-            server = MediaServer(self.coherence, MediaStore, no_thread_needed=True, db=self.shell.props.db, plugin=self, icon=the_icon)
-        else:
-            server = MediaServer(self.coherence, MediaStore, no_thread_needed=True, db=self.shell.props.db, plugin=self)
+        if self.config.get_bool(gconf_keys['dms_active']):
+            # create our own media server
+            from coherence.upnp.devices.media_server import MediaServer
+            from MediaStore import MediaStore
 
-        self.uuid = str(server.uuid)
+            kwargs = {
+                    'version': self.config.get_int(gconf_keys['dms_version']),
+                    'no_thread_needed': True,
+                    'db': self.shell.props.db,
+                    'plugin': self}
+
+            if the_icon:
+                kwargs['icon'] = the_icon
 
-        if self.coherence_version >= (0,5,2):
+            dms_uuid = self.config.get_string(gconf_keys['dms_uuid'])
+            if dms_uuid:
+                kwargs['uuid'] = dms_uuid
+
+            name = self.config.get_string(gconf_keys['dms_name'])
+            if name:
+                kwargs['name'] = name
+
+            self.server = MediaServer(self.coherence, MediaStore, **kwargs)
+
+            if dms_uuid is None:
+                self.config.set_string(gconf_keys['dms_uuid'], str(self.server.uuid))
+
+            self.warning("Media Store available with UUID %s" % str(self.server.uuid))
+
+        if self.config.get_bool(gconf_keys['dmr_active']):
             # create our own media renderer
             # but only if we have a matching Coherence package installed
-            from coherence.upnp.devices.media_renderer import MediaRenderer
-            from MediaPlayer import RhythmboxPlayer
-            if the_icon:
-                MediaRenderer(self.coherence, RhythmboxPlayer, no_thread_needed=True, shell=self.shell, icon=the_icon)
+            if self.coherence_version < (0, 5, 2):
+                print "activation faild. Coherence is older than version 0.5.2"
             else:
-                MediaRenderer(self.coherence, RhythmboxPlayer, no_thread_needed=True, shell=self.shell)
+                from coherence.upnp.devices.media_renderer import MediaRenderer
+                from MediaPlayer import RhythmboxPlayer
+                kwargs = {
+                    "version": self.config.get_int(gconf_keys['dmr_version']),
+                    "no_thread_needed": True,
+                    "shell": self.shell,
+                    'rb_mediaserver': self.server,
+                    }
+
+                if the_icon:
+                    kwargs['icon'] = the_icon
 
-        # watch for media servers
-        louie.connect(self.detected_media_server,
-                'Coherence.UPnP.ControlPoint.MediaServer.detected',
-                louie.Any)
-        louie.connect(self.removed_media_server,
-                'Coherence.UPnP.ControlPoint.MediaServer.removed',
-                louie.Any)
+                dmr_uuid = self.config.get_string(gconf_keys['dmr_uuid'])
+                if dmr_uuid:
+                    kwargs['uuid'] = dmr_uuid
+
+                name = self.config.get_string(gconf_keys['dmr_name'])
+                if name:
+                    kwargs['name'] = name
+
+                self.renderer = MediaRenderer(self.coherence,
+                        RhythmboxPlayer, **kwargs)
+
+                if dmr_uuid is None:
+                    self.config.set_string(gconf_keys['dmr_uuid'], str(self.renderer.uuid))
+
+                self.warning("Media Renderer available with UUID %s" % str(self.renderer.uuid))
+
+        if self.config.get_bool(gconf_keys['dmc_active']):
+            self.warning("start looking for media servers")
+            # watch for media servers
+            louie.connect(self.detected_media_server,
+                    'Coherence.UPnP.ControlPoint.MediaServer.detected',
+                    louie.Any)
+            louie.connect(self.removed_media_server,
+                    'Coherence.UPnP.ControlPoint.MediaServer.removed',
+                    louie.Any)
 
 
     def deactivate(self, shell):
-        print "coherence UPnP plugin deactivated"
+        self.info("Coherence UPnP plugin deactivated")
         if self.coherence is None:
             return
 
         self.coherence.shutdown()
 
-        louie.disconnect(self.detected_media_server,
-                'Coherence.UPnP.ControlPoint.MediaServer.detected',
-                louie.Any)
-        louie.disconnect(self.removed_media_server,
-                'Coherence.UPnP.ControlPoint.MediaServer.removed',
-                louie.Any)
+        try:
+            louie.disconnect(self.detected_media_server,
+                    'Coherence.UPnP.ControlPoint.MediaServer.detected',
+                    louie.Any)
+        except louie.error.DispatcherKeyError:
+            pass
+        try:
+            louie.disconnect(self.removed_media_server,
+                    'Coherence.UPnP.ControlPoint.MediaServer.removed',
+                    louie.Any)
+        except louie.error.DispatcherKeyError:
+            pass
 
         del self.shell
         del self.coherence
@@ -148,26 +220,35 @@ class CoherencePlugin(rb.Plugin,log.Loggable):
             'controlpoint': 'yes',
             'plugins': {},
         }
+
+        serverport = self.config.get_int(gconf_keys['port'])
+        if serverport:
+            coherence_config['serverport'] = serverport
+
+        interface = self.config.get_string(gconf_keys['interface'])
+        if interface:
+            coherence_config['interface'] = interface
+
         coherence_instance = Coherence(coherence_config)
 
         return coherence_instance
 
     def removed_media_server(self, udn):
-        print "upnp server went away %s" % udn
+        self.info("upnp server went away %s" % udn)
         if self.sources.has_key(udn):
             self.sources[udn].delete_thyself()
             del self.sources[udn]
 
     def detected_media_server(self, client, udn):
-        print "found upnp server %s (%s)"  %  (client.device.get_friendly_name(), udn)
-        self.warning("found upnp server %s (%s)"  %  (client.device.get_friendly_name(), udn))
-        if client.device.get_id() == self.uuid:
+        self.info("found upnp server %s (%s)"  %  (client.device.get_friendly_name(), udn))
+        if self.server and client.device.get_id() == str(self.server.uuid):
             """ don't react on our own MediaServer"""
             return
 
         db = self.shell.props.db
-        group = rb.rb_source_group_get_by_name ("shared")
-        entry_type = db.entry_register_type("CoherenceUpnp:" + client.device.get_id()[5:])
+        group = rb.rb_source_group_get_by_name("shared")
+        entry_type = db.entry_register_type("CoherenceUpnp:%s" %
+                 client.device.get_id()[5:])
 
         from UpnpSource import UpnpSource
         source = gobject.new (UpnpSource,
@@ -181,3 +262,59 @@ class CoherencePlugin(rb.Plugin,log.Loggable):
         self.sources[udn] = source
 
         self.shell.append_source (source, None)
+
+    def create_configure_dialog(self, dialog=None):
+        if dialog is None:
+
+            def store_config(dialog,port_spinner,interface_entry):
+                port = port_spinner.get_value_as_int()
+                self.config.set_int(gconf_keys['port'],port)
+                interface = interface_entry.get_text()
+                if len(interface) != 0:
+                    self.config.set_string(gconf_keys['interface'],interface)
+                dialog.hide()
+
+            dialog = gtk.Dialog(title='DLNA/UPnP Configuration',
+                            parent=None,flags=0,buttons=None)
+            dialog.set_default_size(400,350)
+
+            table = gtk.Table(rows=2, columns=2, homogeneous=True)
+            dialog.vbox.pack_start(table, False, False, 0)
+
+            label = gtk.Label("Port :")
+            label.set_alignment(0,0.5)
+            table.attach(label, 0, 1, 0, 1)
+
+            value = 0
+            if self.config.get_int(gconf_keys['port']) != None:
+                value = self.config.get_int(gconf_keys['port'])
+            adj = gtk.Adjustment(value, 0, 65535, 1, 100, 0)
+            port_spinner = gtk.SpinButton(adj, 0, 0)
+            port_spinner.set_wrap(True)
+            port_spinner.set_numeric(True)
+            table.attach(port_spinner, 1, 2, 0, 1,
+                         xoptions=gtk.FILL|gtk.EXPAND,yoptions=gtk.FILL|gtk.EXPAND,xpadding=5,ypadding=5)
+
+            label = gtk.Label("Interface :")
+            label.set_alignment(0,0.5)
+            table.attach(label, 0, 1, 1, 2)
+            interface_entry = gtk.Entry()
+            interface_entry.set_max_length(16)
+            if self.config.get_string(gconf_keys['interface']) != None:
+                interface_entry.set_text(self.config.get_string(gconf_keys['interface']))
+            else:
+                interface_entry.set_text('')
+            table.attach(interface_entry, 1, 2, 1, 2,
+                         xoptions=gtk.FILL|gtk.EXPAND,yoptions=gtk.FILL|gtk.EXPAND,xpadding=5,ypadding=5)
+
+            button = gtk.Button(stock=gtk.STOCK_CANCEL)
+            dialog.action_area.pack_start(button, True, True, 5)
+            button.connect("clicked", lambda w: dialog.hide())
+            button = gtk.Button(stock=gtk.STOCK_OK)
+            button.connect("clicked", lambda w: store_config(dialog,port_spinner,interface_entry))
+            dialog.action_area.pack_start(button, True, True, 5)
+            dialog.show_all()
+
+
+        dialog.present()
+        return dialog



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]