rhythmbox r5563 - in trunk: . plugins/coherence plugins/coherence/upnp_coherence
- From: jrl svn gnome org
- To: svn-commits-list gnome org
- Subject: rhythmbox r5563 - in trunk: . plugins/coherence plugins/coherence/upnp_coherence
- Date: Tue, 5 Feb 2008 12:02:46 +0000 (GMT)
Author: jrl
Date: Tue Feb 5 12:02:46 2008
New Revision: 5563
URL: http://svn.gnome.org/viewvc/rhythmbox?rev=5563&view=rev
Log:
2008-02-05 James Livingston <doclivingston gmail com>
patch by: Frank Scholz <dev netzflocken de>
* plugins/coherence/coherence.rb-plugin.in:
* plugins/coherence/upnp_coherence/Makefile.am:
* plugins/coherence/upnp_coherence/MediaPlayer.py:
* plugins/coherence/upnp_coherence/MediaStore.py:
* plugins/coherence/upnp_coherence/UpnpSource.py:
* plugins/coherence/upnp_coherence/__init__.py: Improved the UPnP plugin
by making us act as a Renderer, exporting artist/album lists and the
like. Closes #512870
Added:
trunk/plugins/coherence/upnp_coherence/MediaPlayer.py
Modified:
trunk/ChangeLog
trunk/plugins/coherence/coherence.rb-plugin.in
trunk/plugins/coherence/upnp_coherence/Makefile.am
trunk/plugins/coherence/upnp_coherence/MediaStore.py
trunk/plugins/coherence/upnp_coherence/UpnpSource.py
trunk/plugins/coherence/upnp_coherence/__init__.py
Modified: trunk/plugins/coherence/coherence.rb-plugin.in
==============================================================================
--- trunk/plugins/coherence/coherence.rb-plugin.in (original)
+++ trunk/plugins/coherence/coherence.rb-plugin.in Tue Feb 5 12:02:46 2008
@@ -2,8 +2,8 @@
Loader=python
Module=upnp_coherence
IAge=1
-_Name=UPnP sharing support
-_Description=Adds support for playing media from, and sending media to UPnP/DLNA network devices
-Authors=James Livingston <doclivingston gmail com>
-Copyright=Copyright  2007 James Livingston
-Website=http://www.rhythmbox.org/
+_Name=DLNA/UPnP sharing and control support
+_Description=Adds support for playing media from and sending media to DLNA/UPnP network devices, and enables Rhythmbox to be controlled by a DLNA/UPnP ControlPoint
+Authors=James Livingston <doclivingston gmail com>, Frank Scholz <fs beebits net>
+Copyright=Copyright  2007,2008 James Livingston & Frank Scholz
+Website=http://www.rhythmbox.org/ - https://coherence.beebits.net
Modified: trunk/plugins/coherence/upnp_coherence/Makefile.am
==============================================================================
--- trunk/plugins/coherence/upnp_coherence/Makefile.am (original)
+++ trunk/plugins/coherence/upnp_coherence/Makefile.am Tue Feb 5 12:02:46 2008
@@ -4,4 +4,5 @@
plugin_PYTHON = \
UpnpSource.py \
MediaStore.py \
+ MediaPlayer.py \
__init__.py
Added: trunk/plugins/coherence/upnp_coherence/MediaPlayer.py
==============================================================================
--- (empty file)
+++ trunk/plugins/coherence/upnp_coherence/MediaPlayer.py Tue Feb 5 12:02:46 2008
@@ -0,0 +1,437 @@
+# Licensed under the MIT license
+# http://opensource.org/licenses/mit-license.php
+
+# Copyright 2008, Frank Scholz <coherence beebits net>
+
+import urllib
+
+import rhythmdb
+
+from coherence.upnp.core.soap_service import errorCode
+from coherence.upnp.core import DIDLLite
+
+import louie
+
+from coherence.extern.simple_plugin import Plugin
+
+from coherence import log
+
+TRACK_COUNT = 1000000
+
+class RhythmboxPlayer(log.Loggable):
+
+ """ a backend to the Rhythmbox
+
+ """
+ logCategory = 'rb_media_renderer'
+
+ implements = ['MediaRenderer']
+ vendor_value_defaults = {'RenderingControl': {'A_ARG_TYPE_Channel':'Master'}}
+ 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.player = None
+ self.metadata = None
+ self.host = '127.0.0.1'
+ self.name = "Rhythmbox on %s" % self.server.coherence.hostname
+
+ self.player = self.shell.get_player()
+ self.player.connect ('playing-song-changed',
+ self.playing_song_changed),
+ self.player.connect ('playing-changed',
+ self.playing_changed)
+ self.player.connect ('elapsed-changed',
+ self.elapsed_changed)
+ self.player.connect("notify::volume", self.volume_changed)
+ louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self)
+
+ self.playing = False
+ self.state = None
+ self.duration = None
+ self.volume = 1.0
+ self.muted_volume = None
+ self.view = []
+ self.tags = {}
+
+ def __repr__(self):
+ return str(self.__class__).split('.')[-1]
+
+ def volume_changed(self, player, parameter):
+ self.volume = self.player.props.volume
+ self.warning('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.warning("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:
+ self.update('STOPPED')
+ self.playing = False
+ #self.entry = None
+ self.metadata = None
+ self.duration = None
+ else:
+ self.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)
+ hours = seconds / 3600
+ seconds = seconds - hours * 3600
+ minutes = seconds / 60
+ seconds = seconds - minutes * 60
+ self.duration = "%02d:%02d:%02d" % (hours, minutes, seconds)
+
+ mimetype = self.shell.props.db.entry_get(entry, rhythmdb.PROP_MIMETYPE)
+ # This isn't a real mime-type
+ if mimetype == "application/x-id3":
+ mimetype = "audio/mpeg"
+ size = self.shell.props.db.entry_get(entry, rhythmdb.PROP_FILE_SIZE)
+
+ # create item
+ item = DIDLLite.MusicTrack(self.id + TRACK_COUNT)
+ 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 = []
+
+ uri = self.shell.props.db.entry_get(entry, rhythmdb.PROP_LOCATION)
+ if uri.startswith("file://"):
+ location = unicode(urllib.unquote(uri[len("file://"):]))
+
+ # 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)
+
+ 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, 'AVTransportURIMetaData',self.metadata)
+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackMetaData',self.metadata)
+ self.warning("playing_song_changed %r", self.metadata)
+ if self.server != None:
+ 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.warning("playing_changed", state)
+ if state is True:
+ transport_state = 'PLAYING'
+ else:
+ if self.playing is False:
+ transport_state = 'STOPPED'
+ else:
+ transport_state = 'PAUSED_PLAYBACK'
+ self.update(transport_state)
+ try:
+ position = player.get_playing_time()
+ except:
+ position = None
+ try:
+ duration = player.get_playing_song_duration()
+ except:
+ duration = None
+ self.update_position(position,duration)
+ self.warning("playing_changed %r %r ", position, duration)
+
+ def elapsed_changed(self, player, time):
+ self.warning("elapsed_changed %r %r", player, time)
+ try:
+ duration = player.get_playing_song_duration()
+ except:
+ duration = None
+ self.update_position(time,duration)
+
+ def update(self, state):
+
+ self.warning("update %r", state)
+
+ if state in ('STOPPED','READY'):
+ transport_state = 'STOPPED'
+ if state == 'PLAYING':
+ transport_state = 'PLAYING'
+ if state == 'PAUSED_PLAYBACK':
+ transport_state = 'PAUSED_PLAYBACK'
+
+ if self.state != transport_state:
+ self.state = transport_state
+ 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,
+ 'TransportState',
+ transport_state)
+
+
+ def update_position(self, position,duration):
+ self.warning("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)
+
+ if position is not None:
+ m,s = divmod( position, 60)
+ h,m = divmod(m,60)
+ if self.server != None:
+ self.server.av_transport_server.set_variable(connection_id, 'RelativeTimePosition', '%02d:%02d:%02d' % (h,m,s))
+ self.server.av_transport_server.set_variable(connection_id, 'AbsoluteTimePosition', '%02d:%02d:%02d' % (h,m,s))
+
+ if duration <= 0:
+ duration = None
+
+ if duration is not None:
+ m,s = divmod( duration, 60)
+ h,m = divmod(m,60)
+
+ if self.server != None:
+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackDuration', '%02d:%02d:%02d' % (h,m,s))
+ self.server.av_transport_server.set_variable(connection_id, 'CurrentMediaDuration', '%02d:%02d:%02d' % (h,m,s))
+
+ if self.duration is None:
+ if self.metadata is not None:
+ self.warning("update_position %r", self.metadata)
+ elt = DIDLLite.DIDLElement.fromString(self.metadata)
+ for item in elt:
+ for res in item.findall('res'):
+ res.attrib['duration'] = "%d:%02d:%02d" % (h,m,s)
+ self.metadata = elt.toString()
+
+ if self.server != None:
+ 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.duration = duration
+
+ def load( self, uri, metadata):
+ self.warning("player load %r %r", uri, metadata)
+ #self.shell.load_uri(uri,play=False)
+ self.duration = None
+ self.metadata = metadata
+ self.tags = {}
+
+ if len(self.metadata)>0:
+ elt = DIDLLite.DIDLElement.fromString(self.metadata)
+ if elt.numItems() == 1:
+ item = elt.getItems()[0]
+
+ self.entry = self.shell.props.db.entry_lookup_by_location(uri)
+ self.warning("check for entry %r %r", self.entry, item.server_uuid)
+ 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.warning("create new entry %r", self.entry)
+ else:
+ self.shell.load_uri(uri,play=False)
+ self.entry = self.shell.props.db.entry_lookup_by_location(uri)
+ self.warning("load and check for entry %r", self.entry)
+
+
+ duration = None
+ size = None
+ bitrate = None
+ for res in item.res:
+ if res.data == uri:
+ duration = res.duration
+ size = res.size
+ bitrate = res.bitrate
+ break
+
+ self.shell.props.db.set(self.entry, rhythmdb.PROP_TITLE, item.title)
+ try:
+ if item.artist is not None:
+ self.shell.props.db.set(self.entry, rhythmdb.PROP_ARTIST, item.artist)
+ except AttributeError:
+ pass
+ try:
+ if item.album is not None:
+ self.shell.props.db.set(self.entry, rhythmdb.PROP_ALBUM, item.album)
+ except AttributeError:
+ pass
+
+ try:
+ self.info("%r %r", item.title,item.originalTrackNumber)
+ if item.originalTrackNumber is not None:
+ self.shell.props.db.set(self.entry, rhythmdb.PROP_TRACK_NUMBER, int(item.originalTrackNumber))
+ except AttributeError:
+ pass
+
+ if duration is not None:
+ h,m,s = duration.split(':')
+ seconds = int(h)*3600 + int(m)*60 + int(s)
+ self.info("%r %r:%r:%r %r", duration, h, m , s, seconds)
+ self.shell.props.db.set(self.entry, rhythmdb.PROP_DURATION, seconds)
+
+ if size is not None:
+ self.shell.props.db.set(self.entry, rhythmdb.PROP_FILE_SIZE,int(size))
+
+ else:
+ self.shell.load_uri(uri,play=False)
+ self.entry = self.shell.props.db.entry_lookup_by_location(uri)
+
+ self.playing = False
+
+ 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')
+ 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)
+ self.server.av_transport_server.set_variable(connection_id, 'AVTransportURIMetaData',metadata)
+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackURI',uri)
+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackMetaData',metadata)
+
+ def start(self, uri):
+ self.load(uri)
+ self.play()
+
+ def stop(self):
+ self.warning("player stop")
+
+ self.player.stop()
+ self.playing = False
+ #self.server.av_transport_server.set_variable( \
+ # self.server.connection_manager_server.lookup_avt_id(self.current_connection_id),\
+ # 'TransportState', 'STOPPED')
+
+ def play(self):
+ self.warning("player play")
+
+ if self.playing == False:
+ self.player.play_entry(self.entry)
+ self.playing = True
+ else:
+ self.player.playpause()
+ #self.server.av_transport_server.set_variable( \
+ # self.server.connection_manager_server.lookup_avt_id(self.current_connection_id),\
+ # 'TransportState', 'PLAYING')
+
+ def pause(self):
+ self.player.pause()
+ #self.server.av_transport_server.set_variable( \
+ # self.server.connection_manager_server.lookup_avt_id(self.current_connection_id),\
+ # 'TransportState', 'PAUSED_PLAYBACK')
+
+ def seek(self, location):
+ """
+ @param location: simple number = time to seek to, in seconds
+ +nL = relative seek forward n seconds
+ -nL = relative seek backwards n seconds
+ """
+
+ def mute(self):
+ self.muted_volume = self.volume
+ self.player.set_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, 'Mute', 'True')
+
+ def unmute(self):
+ if self.muted_volume is not None:
+ self.player.set_volume(self.muted_volume)
+ self.muted_volume = None
+ self.player.set_mute(False)
+ rcs_id = self.server.connection_manager_server.lookup_rcs_id(self.current_connection_id)
+ self.server.rendering_control_server.set_variable(rcs_id, 'Mute', 'False')
+
+ def get_mute(self):
+ return self.player.get_mute()
+
+ def get_volume(self):
+ self.volume = self.player.get_volume()
+ self.warning("get_volume %r", self.volume)
+ return self.volume * 100
+
+ def set_volume(self, volume):
+ self.warning("set_volume %r", volume)
+ volume = int(volume)
+ if volume < 0:
+ volume=0
+ if volume > 100:
+ volume=100
+
+ self.player.set_volume(float(volume/100.0))
+
+ def upnp_init(self):
+ self.current_connection_id = None
+ self.server.connection_manager_server.set_variable(0, 'SinkProtocolInfo',
+ ['internal:%s:*:*' % self.host,
+ 'http-get:*:audio/mpeg:*'],
+ default=True)
+ self.server.av_transport_server.set_variable(0, 'TransportState', 'NO_MEDIA_PRESENT', default=True)
+ self.server.av_transport_server.set_variable(0, 'TransportStatus', 'OK', default=True)
+ self.server.av_transport_server.set_variable(0, 'CurrentPlayMode', 'NORMAL', default=True)
+ self.server.av_transport_server.set_variable(0, 'CurrentTransportActions', '', default=True)
+ self.server.rendering_control_server.set_variable(0, 'Volume', self.get_volume())
+ self.server.rendering_control_server.set_variable(0, 'Mute', self.get_mute())
+
+ def upnp_Play(self, *args, **kwargs):
+ InstanceID = int(kwargs['InstanceID'])
+ Speed = int(kwargs['Speed'])
+ self.play()
+ return {}
+
+ def upnp_Pause(self, *args, **kwargs):
+ InstanceID = int(kwargs['InstanceID'])
+ self.pause()
+ return {}
+
+ def upnp_Stop(self, *args, **kwargs):
+ InstanceID = int(kwargs['InstanceID'])
+ self.stop()
+ return {}
+
+ def upnp_SetAVTransportURI(self, *args, **kwargs):
+ InstanceID = int(kwargs['InstanceID'])
+ CurrentURI = kwargs['CurrentURI']
+ CurrentURIMetaData = kwargs['CurrentURIMetaData']
+ local_protocol_infos=self.server.connection_manager_server.get_variable('SinkProtocolInfo').value.split(',')
+ #print '>>>', local_protocol_infos
+ if len(CurrentURIMetaData)==0:
+ self.load(CurrentURI,CurrentURIMetaData)
+ else:
+ elt = DIDLLite.DIDLElement.fromString(CurrentURIMetaData)
+ #import pdb; pdb.set_trace()
+ if elt.numItems() == 1:
+ item = elt.getItems()[0]
+ res = item.res.get_matching(local_protocol_infos, protocol_type='internal')
+ if len(res) == 0:
+ res = item.res.get_matching(local_protocol_infos)
+ if len(res) > 0:
+ res = res[0]
+ remote_protocol,remote_network,remote_content_format,_ = res.protocolInfo.split(':')
+ self.load(res.data,CurrentURIMetaData)
+ return {}
+ return failure.Failure(errorCode(714))
+
+ def upnp_SetMute(self, *args, **kwargs):
+ InstanceID = int(kwargs['InstanceID'])
+ Channel = kwargs['Channel']
+ DesiredMute = kwargs['DesiredMute']
+ if DesiredMute in ['TRUE', 'True', 'true', '1','Yes','yes']:
+ self.mute()
+ else:
+ self.unmute()
+ return {}
+
+ def upnp_SetVolume(self, *args, **kwargs):
+ InstanceID = int(kwargs['InstanceID'])
+ Channel = kwargs['Channel']
+ DesiredVolume = int(kwargs['DesiredVolume'])
+ self.set_volume(DesiredVolume)
+ return {}
Modified: trunk/plugins/coherence/upnp_coherence/MediaStore.py
==============================================================================
--- trunk/plugins/coherence/upnp_coherence/MediaStore.py (original)
+++ trunk/plugins/coherence/upnp_coherence/MediaStore.py Tue Feb 5 12:02:46 2008
@@ -1,10 +1,12 @@
# Copyright 2007, James Livingston <doclivingston gmail com>
+# Copyright 2007, Frank Scholz <coherence beebits net>
import rhythmdb
import louie
import urllib
from coherence.upnp.core import DIDLLite
+from coherence import log
ROOT_CONTAINER_ID = 0
AUDIO_CONTAINER = 10
@@ -12,12 +14,16 @@
AUDIO_ARTIST_CONTAINER_ID = 12
AUDIO_ALBUM_CONTAINER_ID = 13
-CONTAINER_COUNT = 1000
+CONTAINER_COUNT = 10000
+TRACK_COUNT = 1000000
+
+# most of this class is from Coherence, originally under the MIT licence
+
+class Container(log.Loggable):
+
+ logCategory = 'rb_media_store'
-# this class is from Coherence, originally under the MIT licence
-# Copyright 2007, Frank Scholz <coherence beebits net>
-class Container(object):
def __init__(self, id, parent_id, name, children_callback=None):
self.id = id
self.parent_id = parent_id
@@ -25,11 +31,11 @@
self.mimetype = 'directory'
self.item = DIDLLite.Container(id, parent_id,self.name)
self.update_id = 0
+ self.item.childCount = 0
if children_callback != None:
self.children = children_callback
else:
self.children = []
- self.item.childCount = self.get_child_count()
def add_child(self, child):
self.children.append(child)
@@ -40,18 +46,22 @@
children = self.children()
else:
children = self.children
+
+ self.info("Container get_children %r (%r,%r)", children, start, request_count)
if request_count == 0:
return children[start:]
else:
return children[start:request_count]
def get_child_count(self):
+
if callable(self.children):
return len(self.children())
else:
return len(self.children)
def get_item(self):
+ self.item.childCount = self.get_child_count()
return self.item
def get_name(self):
@@ -61,162 +71,349 @@
return self.id
-class Track:
- def __init__(self, store, id):
- self.id = id
- self.store = store
-
- def get_children(self, start=0, request_count=0):
- return []
-
- def get_child_count(self):
- return 0
-
- def get_item(self):
- host = ""
-
- # load common values
- entry = self.store.db.entry_lookup_by_id (self.id)
- # Bitrate is in bytes/second, not kilobits/second
- bitrate = self.store.db.entry_get (entry, rhythmdb.PROP_BITRATE) * 1024 / 8
- # Duration is in HH:MM:SS format
- seconds = self.store.db.entry_get (entry, rhythmdb.PROP_DURATION)
- hours = seconds / 3600
- seconds = seconds - hours * 3600
- minutes = seconds / 60
- seconds = seconds - minutes * 60
- duration = ("%02d:%02d:%02d") % (hours, minutes, seconds)
-
- location = self.store.db.entry_get (entry, rhythmdb.PROP_LOCATION)
- if location.startswith("file://"):
- location = unicode(urllib.url2pathname(location)[len("file://"):])
- else:
- location = None
- mimetype = self.store.db.entry_get (entry, rhythmdb.PROP_MIMETYPE)
- # This isn't a real mime-type
- if mimetype == "application/x-id3":
- mimetype = "audio/mpeg"
- size = self.store.db.entry_get (entry, rhythmdb.PROP_FILE_SIZE)
-
- # create item
- item = DIDLLite.MusicTrack(self.id + CONTAINER_COUNT)
- item.album = self.store.db.entry_get (entry, rhythmdb.PROP_ALBUM)
- #item.albumArtURI = ## can we somehow store art in the upnp share??
- item.artist = self.store.db.entry_get (entry, rhythmdb.PROP_ARTIST)
- #item.date =
- item.genre = self.store.db.entry_get (entry, rhythmdb.PROP_GENRE)
- 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
- item.res = []
-
- # add internal resource
- #res = DIDLLite.Resource(location, 'internal:%s:%s:*' % (host, mimetype))
- #res.size = size
- #res.duration = duration
- #res.bitrate = bitrate
- #item.res.append(res)
-
- # add http resource
- res = DIDLLite.Resource(self.get_url(), 'http-get:*:%s:*' % mimetype)
- if size > 0:
- res.size = size
- if duration > 0:
- res.duration = str(duration)
- if bitrate > 0:
- res.bitrate = str(bitrate)
- item.res.append(res)
-
- return item
-
- def get_id(self):
- return self.id
-
- def get_name(self):
- entry = self.store.db.entry_lookup_by_id (self.id)
- return self.store.db.entry_get (entry, rhythmdb.PROP_TITLE)
-
- def get_url(self):
- return self.store.urlbase + str(self.id + CONTAINER_COUNT)
-
- def get_path(self):
- entry = self.store.db.entry_lookup_by_id (self.id)
- uri = self.store.db.entry_get (entry, rhythmdb.PROP_LOCATION)
- if uri.startswith("file://"):
- return unicode(urllib.url2pathname(uri)[len("file://"):])
- else:
- return None
-
-class MediaStore:
- implements = ['MediaServer']
-
- def __init__(self, server, **kwargs):
- print "creating UPnP MediaStore"
- self.server = server
- self.db = kwargs['db']
- self.plugin = kwargs['plugin']
-
- self.urlbase = kwargs.get('urlbase','')
- if( len(self.urlbase) > 0 and self.urlbase[len(self.urlbase)-1] != '/'):
- self.urlbase += '/'
-
- self.name = self.server.coherence.hostname
-
- self.containers = {}
- self.containers[ROOT_CONTAINER_ID] = \
- Container( ROOT_CONTAINER_ID,-1, self.server.coherence.hostname)
-
- self.containers[AUDIO_ALL_CONTAINER_ID] = \
- Container( AUDIO_ALL_CONTAINER_ID,ROOT_CONTAINER_ID, 'All tracks',
- children_callback=self.children_tracks)
- self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALL_CONTAINER_ID])
-
- #self.containers[AUDIO_ALBUM_CONTAINER_ID] = \
- # Container( AUDIO_ALBUM_CONTAINER_ID,ROOT_CONTAINER_ID, 'Albums',
- # children_callback=self.children_albums)
- #self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALBUM_CONTAINER_ID])
-
- #self.containers[AUDIO_ARTIST_CONTAINER_ID] = \
- # Container( AUDIO_ARTIST_CONTAINER_ID,ROOT_CONTAINER_ID, 'Artists',
- # children_callback=self.children_artists)
- #self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ARTIST_CONTAINER_ID])
-
- louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self)
-
- def get_by_id(self,id):
- print "getting resource id " + str(id)
- if id.startswith('artist_all_tracks_'):
- return self.containers[id]
-
- id = int(id)
- if id < 1000:
- item = self.containers[id]
- else:
- item = Track(self, (id - CONTAINER_COUNT))
-
- return item
-
- def upnp_init(self):
- if self.server:
- self.server.connection_manager_server.set_variable(0, 'SourceProtocolInfo', [
- #'internal:%s:*:*' % self.name,
- 'http-get:*:audio/mpeg:*',
- ])
-
- def children_tracks(self):
- tracks = []
-
- def track_cb (entry):
- if self.db.entry_get (entry, rhythmdb.PROP_HIDDEN):
- return
- id = self.db.entry_get (entry, rhythmdb.PROP_ENTRY_ID)
- tracks.append(Track(self, id))
- self.db.entry_foreach_by_type (self.db.entry_type_get_by_name('song'), track_cb)
+class Album(log.Loggable):
+
+ logCategory = 'rb_media_store'
+
+ def __init__(self, store, title, id):
+ self.id = id
+ self.title = title
+ self.store = store
+
+ 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_ALBUM, self.title])
+ self.tracks_per_album_query = self.store.db.query_model_new(query)
+ #self.tracks_per_album_query.set_sort_order(rhythmdb.rhythmdb_query_model_track_sort_func)
+ self.store.db.do_full_query_async_parsed(self.tracks_per_album_query, query)
+
+ def get_children(self,start=0,request_count=0):
+ children = []
+
+ def track_sort(x,y):
+ entry = self.store.db.entry_lookup_by_id (x.id)
+ x_track = self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER)
+ entry = self.store.db.entry_lookup_by_id (y.id)
+ y_track = self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER)
+ return cmp(x_track,y_track)
+
+ def collate (model, path, iter):
+ self.info("Album get_children %r %r %r" %(model, path, iter))
+ id = model.get(iter, 0)[0]
+ children.append(Track(self.store,id))
+
+ self.tracks_per_album_query.foreach(collate)
+
+ children.sort(cmp=track_sort)
+
+ if request_count == 0:
+ return children[start:]
+ else:
+ return children[start:request_count]
+
+ def get_child_count(self):
+ return len(self.get_children())
+
+ def get_item(self):
+ item = DIDLLite.MusicAlbum(self.id, AUDIO_ALBUM_CONTAINER_ID, self.title)
+ return item
+
+ def get_id(self):
+ return self.id
+
+ def get_name(self):
+ return self.title
+
+ def get_cover(self):
+ return self.cover
+
+
+class Artist(log.Loggable):
+
+ logCategory = 'rb_media_store'
+
+ def __init__(self, store, name, id):
+ self.id = id
+ self.name = name
+ self.store = store
+
+ 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.albums_per_artist_query = self.store.db.property_model_new(rhythmdb.PROP_ALBUM)
+ self.albums_per_artist_query.props.query_model = qm
+
+ def get_children(self,start=0,request_count=0):
+ children = []
+
+ def collate (model, path, iter):
+ name = model.get(iter, 0)[0]
+ priority = model.get(iter, 1)[0]
+ self.info("get_children collate %r %r", name, priority)
+ if priority is False:
+ try:
+ album = self.store.albums[name]
+ children.append(album)
+ except:
+ self.warning("hmm, a new album %r, that shouldn't happen", name)
+
+ self.albums_per_artist_query.foreach(collate)
+
+ if request_count == 0:
+ return children[start:]
+ else:
+ return children[start:request_count]
+
+ def get_child_count(self):
+ return len(self.get_children())
+
+ def get_item(self):
+ item = DIDLLite.MusicArtist(self.id, AUDIO_ARTIST_CONTAINER_ID, self.name)
+ return item
+
+ def get_id(self):
+ return self.id
+
+ def get_name(self):
+ return self.name
+
+
+class Track(log.Loggable):
+
+ logCategory = 'rb_media_store'
+
+ def __init__(self, store, id):
+ self.store = store
+ if type(id) == int:
+ self.id = id
+ else:
+ self.id = self.store.db.entry_get (id, rhythmdb.PROP_ENTRY_ID)
+
+ def get_children(self, start=0, request_count=0):
+ return []
+
+ def get_child_count(self):
+ return 0
+
+ def get_item(self):
+
+ self.info("Track get_item %r" %(self.id))
+
+ host = ""
+
+ # load common values
+ entry = self.store.db.entry_lookup_by_id(self.id)
+ # Bitrate is in bytes/second, not kilobits/second
+ bitrate = self.store.db.entry_get(entry, rhythmdb.PROP_BITRATE) * 1024 / 8
+ # Duration is in HH:MM:SS format
+ seconds = self.store.db.entry_get(entry, rhythmdb.PROP_DURATION)
+ hours = seconds / 3600
+ seconds = seconds - hours * 3600
+ minutes = seconds / 60
+ seconds = seconds - minutes * 60
+ duration = ("%02d:%02d:%02d") % (hours, minutes, seconds)
+
+ location = self.get_path(entry)
+ mimetype = self.store.db.entry_get(entry, rhythmdb.PROP_MIMETYPE)
+ # This isn't a real mime-type
+ if mimetype == "application/x-id3":
+ mimetype = "audio/mpeg"
+ size = self.store.db.entry_get(entry, rhythmdb.PROP_FILE_SIZE)
+
+ # create item
+ item = DIDLLite.MusicTrack(self.id + TRACK_COUNT)
+ item.album = self.store.db.entry_get(entry, rhythmdb.PROP_ALBUM)
+ item.artist = self.store.db.entry_get(entry, rhythmdb.PROP_ARTIST)
+ #item.date =
+ item.genre = self.store.db.entry_get(entry, rhythmdb.PROP_GENRE)
+ 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")
+ #self.warning("cover for %r is %r", item.title, cover)
+ #item.albumArtURI = ## can we somehow store art in the upnp share??
+
+ # add internal resource
+ #res = DIDLLite.Resource(location, 'internal:%s:%s:*' % (host, mimetype))
+ #res.size = size
+ #res.duration = duration
+ #res.bitrate = bitrate
+ #item.res.append(res)
+
+ # add http resource
+ res = DIDLLite.Resource(self.get_url(), 'http-get:*:%s:*' % mimetype)
+ if size > 0:
+ res.size = size
+ if duration > 0:
+ res.duration = str(duration)
+ if bitrate > 0:
+ res.bitrate = str(bitrate)
+ item.res.append(res)
+
+ return item
+
+ def get_id(self):
+ return self.id
+
+ def get_name(self):
+ entry = self.store.db.entry_lookup_by_id (self.id)
+ return self.store.db.entry_get(entry, rhythmdb.PROP_TITLE)
+
+ def get_url(self):
+ return self.store.urlbase + str(self.id + TRACK_COUNT)
+
+ def get_path(self, entry = None):
+ 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)
+ location = None
+ if uri.startswith("file://"):
+ location = unicode(urllib.unquote(uri[len("file://"):]))
+ self.warning("Track get_path location = %r", location)
+
+ return location
+
+class MediaStore(log.Loggable):
+
+ logCategory = 'rb_media_store'
+ implements = ['MediaServer']
+
+ def __init__(self, server, **kwargs):
+ print "creating UPnP MediaStore"
+ self.server = server
+ self.db = kwargs['db']
+ self.plugin = kwargs['plugin']
- return tracks
+ self.update_id = 0
+
+ self.next_id = CONTAINER_COUNT
+ self.albums = None
+ self.artists = None
+ self.tracks = None
+
+ self.urlbase = kwargs.get('urlbase','')
+ if( len(self.urlbase) > 0 and self.urlbase[len(self.urlbase)-1] != '/'):
+ self.urlbase += '/'
+
+ self.name = "Rhythmbox on %s" % self.server.coherence.hostname
+
+ query = self.db.query_new()
+ self.info(query)
+ self.db.query_append(query, [rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_TYPE, self.db.entry_type_get_by_name('song')])
+ qm = self.db.query_model_new(query)
+ self.db.do_full_query_async_parsed(qm, query)
+
+ self.album_query = self.db.property_model_new(rhythmdb.PROP_ALBUM)
+ self.album_query.props.query_model = qm
+
+ self.artist_query = self.db.property_model_new(rhythmdb.PROP_ARTIST)
+ self.artist_query.props.query_model = qm
+
+ self.containers = {}
+ self.containers[ROOT_CONTAINER_ID] = \
+ Container( ROOT_CONTAINER_ID,-1, "Rhythmbox on %s" % self.server.coherence.hostname)
+
+ self.containers[AUDIO_ALL_CONTAINER_ID] = \
+ Container( AUDIO_ALL_CONTAINER_ID,ROOT_CONTAINER_ID, 'All tracks',
+ children_callback=self.children_tracks)
+ self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALL_CONTAINER_ID])
+
+ self.containers[AUDIO_ALBUM_CONTAINER_ID] = \
+ Container( AUDIO_ALBUM_CONTAINER_ID,ROOT_CONTAINER_ID, 'Albums',
+ children_callback=self.children_albums)
+ self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALBUM_CONTAINER_ID])
+
+ self.containers[AUDIO_ARTIST_CONTAINER_ID] = \
+ Container( AUDIO_ARTIST_CONTAINER_ID,ROOT_CONTAINER_ID, 'Artists',
+ children_callback=self.children_artists)
+ self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ARTIST_CONTAINER_ID])
+
+ louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self)
+
+ def get_by_id(self,id):
+
+ self.info("looking for id %r", id)
+ id = int(id)
+ if id < TRACK_COUNT:
+ item = self.containers[id]
+ else:
+ item = Track(self, (id - TRACK_COUNT))
- def children_albums(self):
- return []
+ return item
- def children_artists(self):
- return []
+ def get_next_container_id(self):
+ ret = self.next_id
+ self.next_id += 1
+ return ret
+
+ def upnp_init(self):
+ if self.server:
+ self.server.connection_manager_server.set_variable(0, 'SourceProtocolInfo', [
+ #'internal:%s:*:*' % self.name,
+ 'http-get:*:audio/mpeg:*',
+ ])
+
+ def children_tracks(self):
+ tracks = []
+
+ def track_cb (entry):
+ if self.db.entry_get (entry, rhythmdb.PROP_HIDDEN):
+ return
+ id = self.db.entry_get (entry, rhythmdb.PROP_ENTRY_ID)
+ track = Track(self, id)
+ tracks.append(track)
+
+ self.db.entry_foreach_by_type (self.db.entry_type_get_by_name('song'), track_cb)
+ return tracks
+
+ def children_albums(self):
+ albums = {}
+
+ self.info('children_albums')
+
+ def album_sort(x,y):
+ r = cmp(x.title,y.title)
+ self.info("sort %r - %r = %r", x.title, y.title, r)
+ return r
+
+ def collate (model, path, iter):
+ name = model.get(iter, 0)[0]
+ priority = model.get(iter, 1)[0]
+ self.info("children_albums collate %r %r", name, priority)
+ if priority is False:
+ id = self.get_next_container_id()
+ album = Album(self, name, id)
+ self.containers[id] = album
+ albums[name] = album
+
+ if self.albums is None:
+ self.album_query.foreach(collate)
+ self.albums = albums
+
+ albums = self.albums.values() #.sort(cmp=album_sort)
+ albums.sort(cmp=album_sort)
+ return albums
+
+ def children_artists(self,killbug=False):
+ artists = []
+
+ self.info('children_artists')
+
+ def collate (model, path, iter):
+ name = model.get(iter, 0)[0]
+ priority = model.get(iter, 1)[0]
+ if priority is False:
+ id = self.get_next_container_id()
+ artist = Artist(self,name, id)
+ self.containers[id] = artist
+ artists.append(artist)
+
+ if self.artists is None:
+ self.artist_query.foreach(collate)
+ self.artists = artists
+ return self.artists
Modified: trunk/plugins/coherence/upnp_coherence/UpnpSource.py
==============================================================================
--- trunk/plugins/coherence/upnp_coherence/UpnpSource.py (original)
+++ trunk/plugins/coherence/upnp_coherence/UpnpSource.py Tue Feb 5 12:02:46 2008
@@ -1,101 +1,177 @@
+# Copyright 2007, James Livingston <doclivingston gmail com>
+# Copyright 2007, Frank Scholz <coherence beebits net>
+
import rb, rhythmdb
import gobject, gtk
-class UpnpSource(rb.BrowserSource):
- __gproperties__ = {
- 'plugin': (rb.Plugin, 'plugin', 'plugin', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY),
- 'client': (gobject.TYPE_PYOBJECT, 'client', 'client', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY),
- 'usn': (gobject.TYPE_PYOBJECT, 'usn', 'usn', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY),
- }
-
- def __init__(self):
- rb.BrowserSource.__init__(self)
- self.__db = None
- self.__activated = False
-
-
- def do_set_property(self, property, value):
- if property.name == 'plugin':
- self.__plugin = value
- elif property.name == 'client':
- self.__client = value
- self.props.name = self.__client.device.get_friendly_name()
- elif property.name == 'usn':
- self.__usn = value
- else:
- raise AttributeError, 'unknown property %s' % property.name
-
-
- def do_impl_activate(self):
- if not self.__activated:
- print "activating upnp source"
- self.__activated = True
-
- shell = self.get_property('shell')
- self.__db = shell.get_property('db')
- self.__entry_type = self.get_property('entry-type')
-
- # load upnp db
- self.load_db(0)
- self.__client.content_directory.subscribe_for_variable('ContainerUpdateIDs', self.state_variable_change)
- self.__client.content_directory.subscribe_for_variable('SystemUpdateID', self.state_variable_change)
-
-
- def load_db(self, id):
- d = self.__client.content_directory.browse(id, browse_flag='BrowseDirectChildren', backward_compatibility=False)
- d.addCallback(self.process_media_server_browse, self.__usn)
-
-
- def state_variable_change(self, variable, usn):
- print "%s changed from %s to %s" % (variable.name, variable.old_value, variable.value)
- if variable.old_value == '':
- return
-
- if variable.name == 'SystemUpdateID':
- self.load_db(0)
- elif variable.name == 'ContainerUpdateIDs':
- changes = variable.value.split(',')
- while len(changes) > 1:
- container = changes.pop(0).strip()
- update_id = changes.pop(0).strip()
- if container in self.container_watch:
- print "we have a change in %s, container needs a reload" % container
- self.load_db(container)
-
-
- def process_media_server_browse(self, results, usn):
- for k,v in results.iteritems():
- if k == 'items':
- for id, values in v.iteritems():
- if values['upnp_class'].startswith('object.container'):
- self.load_db(id)
- if values['upnp_class'].startswith('object.item.audioItem'):
- # (url, [method, something which is in asterix, format, semicolon delimited key=value map of something])
- resources = [(k, v.split(':')) for (k, v) in values['resources'].iteritems()]
- # break data into map
- for r in resources:
- if r[1][3] is not '*':
- r[1][3] = dict([v.split('=') for v in r[1][3].split(';')])
- else:
- r[1][3] = dict()
-
- url = None
- for r in resources:
- if r[1][3].has_key('DLNA.ORG_CI') and r[1][3]['DLNA.ORG_CI'] is not '1':
- url = r[0]
- break
-
- if url is None:
- # use transcoded format, since we can't find a normal one
- url = resources[0][0]
-
- entry = self.__db.entry_lookup_by_location (url)
- if entry == None:
- entry = self.__db.entry_new(self.__entry_type, url)
-
- self.__db.set(entry, rhythmdb.PROP_TITLE, values['title'])
-
- self.__db.commit()
+from coherence import __version_info__ as coherence_version
-gobject.type_register(UpnpSource)
+from coherence import log
+
+class UpnpSource(rb.BrowserSource,log.Loggable):
+
+ logCategory = 'rb_media_store'
+ __gproperties__ = {
+ 'plugin': (rb.Plugin, 'plugin', 'plugin', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY),
+ 'client': (gobject.TYPE_PYOBJECT, 'client', 'client', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY),
+ 'usn': (gobject.TYPE_PYOBJECT, 'usn', 'usn', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY),
+ }
+
+ def __init__(self):
+ rb.BrowserSource.__init__(self)
+ self.__db = None
+ self.__activated = False
+ self.container_watch = []
+ if coherence_version < (0,5,1):
+ self.process_media_server_browse = self.old_process_media_server_browse
+ else:
+ self.process_media_server_browse = self.new_process_media_server_browse
+
+ def do_set_property(self, property, value):
+ if property.name == 'plugin':
+ self.__plugin = value
+ elif property.name == 'client':
+ self.__client = value
+ self.props.name = self.__client.device.get_friendly_name()
+ elif property.name == 'usn':
+ self.__usn = value
+ else:
+ raise AttributeError, 'unknown property %s' % property.name
+
+
+ def do_impl_activate(self):
+ if not self.__activated:
+ print "activating upnp source"
+ self.__activated = True
+
+ shell = self.get_property('shell')
+ self.__db = shell.get_property('db')
+ self.__entry_type = self.get_property('entry-type')
+
+ # load upnp db
+ self.load_db(0)
+ self.__client.content_directory.subscribe_for_variable('ContainerUpdateIDs', self.state_variable_change)
+ self.__client.content_directory.subscribe_for_variable('SystemUpdateID', self.state_variable_change)
+
+
+ def load_db(self, id):
+ if coherence_version < (0,5,1):
+ d = self.__client.content_directory.browse(id, browse_flag='BrowseDirectChildren', backward_compatibility=False)
+ else:
+ d = self.__client.content_directory.browse(id, browse_flag='BrowseDirectChildren', process_result=False, backward_compatibility=False)
+ d.addCallback(self.process_media_server_browse, self.__usn)
+
+
+ def state_variable_change(self, variable, usn=None):
+ print "%s changed from %s to %s" % (variable.name, variable.old_value, variable.value)
+ if variable.old_value == '':
+ return
+
+ if variable.name == 'SystemUpdateID':
+ self.load_db(0)
+ elif variable.name == 'ContainerUpdateIDs':
+ changes = variable.value.split(',')
+ while len(changes) > 1:
+ container = changes.pop(0).strip()
+ update_id = changes.pop(0).strip()
+ if container in self.container_watch:
+ print "we have a change in %s, container needs a reload" % container
+ self.load_db(container)
+
+
+ def new_process_media_server_browse(self, results, usn):
+ for item in results:
+ self.info("process_media_server_browse %r %r", item.id, item)
+ if item.upnp_class.startswith('object.container'):
+ self.load_db(item.id)
+ if item.upnp_class.startswith('object.item.audioItem'):
+
+ url = None
+ duration = None
+ size = None
+ bitrate = None
+
+ for res in item.res:
+ remote_protocol,remote_network,remote_content_format,remote_flags = res.protocolInfo.split(':')
+ self.info("%r %r %r %r",remote_protocol,remote_network,remote_content_format,remote_flags)
+ if remote_protocol == 'http-get':
+ url = res.data
+ duration = res.duration
+ size = res.size
+ bitrate = res.bitrate
+ break
+
+ if url is not None:
+ self.info("url %r %r",url,item.title)
+
+ entry = self.__db.entry_lookup_by_location (url)
+ if entry == None:
+ entry = self.__db.entry_new(self.__entry_type, url)
+
+ self.__db.set(entry, rhythmdb.PROP_TITLE, item.title)
+ try:
+ if item.artist is not None:
+ self.__db.set(entry, rhythmdb.PROP_ARTIST, item.artist)
+ except AttributeError:
+ pass
+ try:
+ if item.album is not None:
+ self.__db.set(entry, rhythmdb.PROP_ALBUM, item.album)
+ except AttributeError:
+ pass
+
+ try:
+ self.info("%r %r", item.title,item.originalTrackNumber)
+ if item.originalTrackNumber is not None:
+ self.__db.set(entry, rhythmdb.PROP_TRACK_NUMBER, int(item.originalTrackNumber))
+ except AttributeError:
+ pass
+
+ if duration is not None:
+ h,m,s = duration.split(':')
+ seconds = int(h)*3600 + int(m)*60 + int(s)
+ self.info("%r %r:%r:%r %r", duration, h, m , s, seconds)
+ self.__db.set(entry, rhythmdb.PROP_DURATION, seconds)
+
+ if size is not None:
+ self.__db.set(entry, rhythmdb.PROP_FILE_SIZE,int(size))
+
+ self.__db.commit()
+
+
+ def old_process_media_server_browse(self, results, usn):
+ for k,v in results.iteritems():
+ if k == 'items':
+ for id, values in v.iteritems():
+ if values['upnp_class'].startswith('object.container'):
+ self.load_db(id)
+ if values['upnp_class'].startswith('object.item.audioItem'):
+ # (url, [method, something which is in asterix, format, semicolon delimited key=value map of something])
+ resources = [(k, v.split(':')) for (k, v) in values['resources'].iteritems()]
+ # break data into map
+ for r in resources:
+ if r[1][3] is not '*':
+ r[1][3] = dict([v.split('=') for v in r[1][3].split(';')])
+ else:
+ r[1][3] = dict()
+
+ url = None
+ for r in resources:
+ if r[1][3].has_key('DLNA.ORG_CI') and r[1][3]['DLNA.ORG_CI'] is not '1':
+ url = r[0]
+ break
+
+ if url is None:
+ # use transcoded format, since we can't find a normal one
+ url = resources[0][0]
+
+ entry = self.__db.entry_lookup_by_location (url)
+ if entry == None:
+ entry = self.__db.entry_new(self.__entry_type, url)
+
+ self.__db.set(entry, rhythmdb.PROP_TITLE, values['title'])
+
+ self.__db.commit()
+
+gobject.type_register(UpnpSource)
Modified: trunk/plugins/coherence/upnp_coherence/__init__.py
==============================================================================
--- trunk/plugins/coherence/upnp_coherence/__init__.py (original)
+++ trunk/plugins/coherence/upnp_coherence/__init__.py Tue Feb 5 12:02:46 2008
@@ -1,144 +1,165 @@
import rhythmdb, rb
import gobject, gtk
+
import louie
+
+from coherence import log
+
# For the icon
import os.path, urllib, gnomevfs, gtk.gdk
+class CoherencePlugin(rb.Plugin,log.Loggable):
+
+ logCategory = 'rb_coherence_plugin'
-class CoherencePlugin(rb.Plugin):
- def __init__(self):
- rb.Plugin.__init__(self)
-
- def activate(self, shell):
- from twisted.internet import gtk2reactor
- try:
- gtk2reactor.install()
- except AssertionError, e:
- # sometimes it's already installed
- print e
-
- self.coherence = self.get_coherence()
- if self.coherence is None:
- print "Coherence is not installed or too old, aborting"
- return
-
- print "coherence UPnP plugin activated"
- self.shell = shell
- self.sources = {}
-
- # 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)
-
- # Set up our icon
- face_path = os.path.join(os.path.expanduser('~'), ".face")
- if os.path.exists(face_path):
- url = "file://" + urllib.pathname2url(face_path)
- else:
- url = None
-
- if 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
- }
- 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)
-
- def deactivate(self, shell):
- print "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)
-
- del self.shell
- del self.coherence
-
- for usn, source in self.sources.iteritems():
- source.delete_thyself()
- del self.sources
-
- # uninstall twisted reactor? probably not, since other thigngs may have used it
-
-
- def get_coherence (self):
- coherence_instance = None
- required_version = (0, 3, 2)
-
- try:
- from coherence.base import Coherence
- from coherence import __version_info__
- except ImportError, e:
- print "Coherence not found"
- return None
-
- if __version_info__ < required_version:
- required = '.'.join([str(i) for i in required_version])
- found = '.'.join([str(i) for i in __version_info__])
- print "Coherence %s required. %s found. Please upgrade" % (required, found)
- return None
-
- coherence_config = {
- #'logmode': 'info',
- 'controlpoint': 'yes',
- 'plugins':{}
- }
- coherence_instance = Coherence(coherence_config)
-
- return coherence_instance
-
-
- def removed_media_server(self, usn):
- print "upnp server went away %s" % usn
- if self.sources.has_key(usn):
- self.sources[usn].delete_thyself()
- del self.sources[usn]
-
- def detected_media_server(self, client, usn):
- print "found upnp server %s (%s)" % (client.device.get_friendly_name(), usn)
-
- db = self.shell.props.db
- group = rb.rb_source_group_get_by_name ("shared")
- entry_type = db.entry_register_type("CoherenceUpnp:" + usn)
-
- from UpnpSource import UpnpSource
- source = gobject.new (UpnpSource,
- shell=self.shell,
- entry_type=entry_type,
- source_group=group,
- plugin=self,
- client=client,
- usn=usn)
+ def __init__(self):
+ rb.Plugin.__init__(self)
- self.sources[usn] = source
+ def activate(self, shell):
+ from twisted.internet import gtk2reactor
+ try:
+ gtk2reactor.install()
+ except AssertionError, e:
+ # sometimes it's already installed
+ print e
+
+ self.coherence = self.get_coherence()
+ if self.coherence is None:
+ print "Coherence is not installed or too old, aborting"
+ return
+
+ print "coherence UPnP plugin activated"
+ self.shell = shell
+ self.sources = {}
+
+ # Set up our icon
+ the_icon = None
+ face_path = os.path.join(os.path.expanduser('~'), ".face")
+ if os.path.exists(face_path):
+ url = "file://" + urllib.pathname2url(face_path)
+ 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
+ }
+ 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)
+
+ self.uuid = str(server.uuid)
+
+ if self.coherence_version >= (0,5,2):
+ # 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)
+ else:
+ MediaRenderer(self.coherence, RhythmboxPlayer, no_thread_needed=True, shell=self.shell)
+
+ # 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"
+ 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)
+
+ del self.shell
+ del self.coherence
+
+ for usn, source in self.sources.iteritems():
+ source.delete_thyself()
+ del self.sources
+
+ # uninstall twisted reactor? probably not, since other thigngs may have used it
+
+
+ def get_coherence (self):
+ coherence_instance = None
+ required_version = (0, 3, 2)
+
+ try:
+ from coherence.base import Coherence
+ from coherence import __version_info__
+ except ImportError, e:
+ print "Coherence not found"
+ return None
+
+ if __version_info__ < required_version:
+ required = '.'.join([str(i) for i in required_version])
+ found = '.'.join([str(i) for i in __version_info__])
+ print "Coherence %s required. %s found. Please upgrade" % (required, found)
+ return None
+
+ self.coherence_version = __version_info__
+
+ coherence_config = {
+ #'logmode': 'info',
+ 'controlpoint': 'yes',
+ 'plugins': {},
+ 'interface': 'eth0',
+ }
+ coherence_instance = Coherence(coherence_config)
+
+ return coherence_instance
+
+ def removed_media_server(self, usn):
+ print "upnp server went away %s" % usn
+ if self.sources.has_key(usn):
+ self.sources[usn].delete_thyself()
+ del self.sources[usn]
+
+ def detected_media_server(self, client, usn):
+ print "found upnp server %s (%s)" % (client.device.get_friendly_name(), usn)
+ self.warning("found upnp server %s (%s)" % (client.device.get_friendly_name(), usn))
+ if client.device.get_id() == self.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:])
+
+ from UpnpSource import UpnpSource
+ source = gobject.new (UpnpSource,
+ shell=self.shell,
+ entry_type=entry_type,
+ source_group=group,
+ plugin=self,
+ client=client,
+ usn=usn)
- self.shell.append_source (source, None)
+ self.sources[usn] = source
+ self.shell.append_source (source, None)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]