# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# Copyright 2007,2008,2009 - Frank Scholz <coherence@beebits.net>
""" DBUS service class
"""
import time
import urllib.error
import urllib.parse
import urllib.parse
import urllib.request
import dbus
from lxml import etree
if dbus.__version__ < '0.82.2':
raise ImportError(
'dbus-python module too old, pls get a newer one from '
'http://dbus.freedesktop.org/releases/dbus-python/')
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
import dbus.service
from coherence import __version__
from coherence.upnp.core import DIDLLite
from coherence.dbus_constants import *
import coherence.extern.louie as louie
from coherence import log
from twisted.internet import reactor, task
namespaces = {'{http://purl.org/dc/elements/1.1/}': 'dc:',
'{urn:schemas-upnp-org:metadata-1-0/upnp/}': 'upnp:',
'{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}': 'DIDL-Lite:',
'{urn:schemas-dlna-org:metadata-1-0}': 'dlna:',
'{http://www.pv.com/pvns/}': 'pv:'}
[docs]def un_namespace(text):
for k, v in list(namespaces.items()):
if text.startswith(k):
return text.replace(k, v)
return text
[docs]class DBusCDSService(dbus.service.Object, log.LogAble):
logCategory = 'dbus'
NOT_FOR_THE_TUBES = True
def __init__(self, service, dbus_device, bus):
log.LogAble.__init__(self)
self.service = service
self.dbus_device = dbus_device
self.type = self.service.service_type.split(':')[
3] # get the service name
bus_name = dbus.service.BusName(CDS_SERVICE, bus)
device_id = dbus_device.id
self.path = \
OBJECT_PATH + '/devices/' + device_id + '/services/' + 'CDS'
dbus.service.Object.__init__(self, bus, bus_name=bus_name,
object_path=self.path)
self.debug("DBusService %r %r", service, self.type)
louie.connect(self.variable_changed, 'StateVariable.changed',
sender=self.service)
self.subscribeStateVariables()
[docs] def shutdown(self):
self._release_thyself(suicide_mode=False)
[docs] def _release_thyself(self, suicide_mode=True):
louie.disconnect(self.variable_changed, 'StateVariable.changed',
sender=self.service)
self.service = None
self.dbus_device = None
self.remove_from_connection()
self.path = None
if suicide_mode:
del self
[docs] def variable_changed(self, variable):
self.StateVariableChanged(self.dbus_device.device.get_id(), self.type,
variable.name, variable.value)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='', out_signature='s')
def get_id(self):
return self.service.id
[docs] @dbus.service.method(CDS_SERVICE, in_signature='', out_signature='s')
def get_scpd_xml(self):
return self.service.scpdXML
@dbus.service.signal(CDS_SERVICE,
signature='sssv')
def StateVariableChanged(self, udn, service, variable, value):
self.info("%s service %s signals StateVariable %s changed to %r",
self.dbus_device.device.get_friendly_name(), self.type,
variable, value)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='', out_signature='as')
def getAvailableActions(self):
actions = self.service.get_actions()
r = []
for name in list(actions.keys()):
r.append(name)
return r
[docs] @dbus.service.method(CDS_SERVICE, in_signature='', out_signature='ssv')
def subscribeStateVariables(self):
if not self.service:
return
notify = [v for v in list(self.service._variables[0].values()) if
v.send_events is True]
if len(notify) == 0:
return
data = {}
for n in notify:
if n.name == 'LastChange':
lc = {}
for instance, vdict in list(self.service._variables.items()):
v = {}
for variable in list(vdict.values()):
if (variable.name != 'LastChange' and
variable.name[0:11] != 'A_ARG_TYPE_' and
variable.never_evented is False):
if not hasattr(variable, 'dbus_updated'):
variable.dbus_updated = None
if len(v) > 0:
lc[str(instance)] = v
if len(lc) > 0:
data[str(n.name)] = lc
else:
data[str(n.name)] = str(n.value)
return self.dbus_device.device.get_id(), self.type, dbus.Dictionary(
data, signature='sv', variant_level=3)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='', out_signature='s',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def GetSearchCapabilites(self, dbus_async_cb, dbus_async_err_cb):
r = self.callAction('GetSearchCapabilites', {})
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['SearchCaps']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='', out_signature='s',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def GetSortCapabilities(self, dbus_async_cb, dbus_async_err_cb):
r = self.callAction('GetSortCapabilities', {})
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['SortCaps']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='', out_signature='s',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def GetSortExtensionCapabilities(self, dbus_async_cb, dbus_async_err_cb):
r = self.callAction('GetSortExtensionCapabilities', {})
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['SortExtensionCaps']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='', out_signature='s',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def GetFeatureList(self, dbus_async_cb, dbus_async_err_cb):
r = self.callAction('GetFeatureList', {})
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['FeatureList']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='', out_signature='i',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def GetSystemUpdateID(self, dbus_async_cb, dbus_async_err_cb):
r = self.callAction('GetSystemUpdateID', {})
if r == '':
return r
def convert_reply(data):
dbus_async_cb(int(data['Id']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='sssiis',
out_signature='aa{sv}iii', # was viii
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def Browse(self, ObjectID, BrowseFlag, Filter, StartingIndex,
RequestedCount, SortCriteria,
dbus_async_cb, dbus_async_err_cb):
arguments = {'ObjectID': str(ObjectID),
'BrowseFlag': str(BrowseFlag),
'Filter': str(Filter),
'StartingIndex': int(StartingIndex),
'RequestedCount': int(RequestedCount),
'SortCriteria': str(SortCriteria)}
r = self.callAction('Browse', arguments)
if r == '':
return r
def convert_reply(data):
et = etree.fromstring(data['Result'])
items = dbus.Array([], signature='v')
def append(item):
i = dbus.Dictionary({}, signature='sv')
for k, v in list(item.attrib.items()):
i[un_namespace(k)] = v
res = dbus.Array([], signature='v')
for child in item:
if un_namespace(child.tag) == 'DIDL-Lite:res':
res_dict = dbus.Dictionary({}, signature='sv')
res_dict['url'] = str(child.text)
for k, v in list(child.attrib.items()):
res_dict[un_namespace(k)] = v
res.append(res_dict)
else:
i[un_namespace(child.tag)] = child.text
if len(res):
i['res'] = res
items.append(i)
for item in et:
append(item)
dbus_async_cb(items, int(data['NumberReturned']),
int(data['TotalMatches']), int(data['UpdateID']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='sssiis',
out_signature='aa{sv}iii',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def Search(self, ContainerID, SearchCriteria, Filter, StartingIndex,
RequestedCount, SortCriteria,
dbus_async_cb, dbus_async_err_cb):
arguments = {'ContainerID': str(ContainerID),
'SearchCriteria': str(SearchCriteria),
'Filter': str(Filter),
'StartingIndex': int(StartingIndex),
'RequestedCount': int(RequestedCount),
'SortCriteria': str(SortCriteria)}
r = self.callAction('Search', arguments)
if r == '':
return r
def convert_reply(data):
et = etree.fromstring(data['Result'])
items = dbus.Array([], signature='v')
def append(item):
i = dbus.Dictionary({}, signature='sv')
for k, v in list(item.attrib.items()):
i[un_namespace(k)] = v
res = dbus.Array([], signature='v')
for child in item:
if un_namespace(child.tag) == 'DIDL-Lite:res':
res_dict = dbus.Dictionary({}, signature='sv')
res_dict['url'] = str(child.text)
for k, v in list(child.attrib.items()):
res_dict[un_namespace(k)] = v
res.append(res_dict)
else:
i[un_namespace(child.tag)] = child.text
if len(res):
i['res'] = res
items.append(i)
for item in et:
append(item)
dbus_async_cb(items, int(data['NumberReturned']),
int(data['TotalMatches']), int(data['UpdateID']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='ss', out_signature='ss',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def CreateObject(self, ContainerID, Elements,
dbus_async_cb, dbus_async_err_cb):
arguments = {'ContainerID': str(ContainerID),
'Elements': str(Elements)}
r = self.callAction('CreateObject', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['ObjectID']), str(data['Result']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='s', out_signature='',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def DestroyObject(self, ObjectID,
dbus_async_cb, dbus_async_err_cb):
arguments = {'ObjectID': str(ObjectID)}
r = self.callAction('DestroyObject', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb()
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='sss', out_signature='',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def UpdateObject(self, ObjectID, CurrentTagValue, NewTagValue,
dbus_async_cb, dbus_async_err_cb):
arguments = {'ObjectID': str(ObjectID),
'CurrentTagValue': str(CurrentTagValue),
'NewTagValue': NewTagValue}
r = self.callAction('UpdateObject', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb()
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='ss', out_signature='s',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def MoveObject(self, ObjectID, NewParentID,
dbus_async_cb, dbus_async_err_cb):
arguments = {'ObjectID': str(ObjectID),
'NewParentID': str(NewParentID)}
r = self.callAction('MoveObject', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['NewObjectID']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='ss', out_signature='i',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def ImportResource(self, SourceURI, DestinationURI,
dbus_async_cb, dbus_async_err_cb):
arguments = {'SourceURI': str(SourceURI),
'DestinationURI': str(DestinationURI)}
r = self.callAction('ImportResource', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['TransferID']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='ss', out_signature='i',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def ExportResource(self, SourceURI, DestinationURI,
dbus_async_cb, dbus_async_err_cb):
arguments = {'SourceURI': str(SourceURI),
'DestinationURI': str(DestinationURI)}
r = self.callAction('ExportResource', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['TransferID']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='s', out_signature='',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def DeleteResource(self, ResourceURI,
dbus_async_cb, dbus_async_err_cb):
arguments = {'ResourceURI': str(ResourceURI)}
r = self.callAction('DeleteResource', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb()
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='i', out_signature='',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def StopTransferResource(self, TransferID,
dbus_async_cb, dbus_async_err_cb):
arguments = {'TransferID': str(TransferID)}
r = self.callAction('StopTransferResource', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb()
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='i', out_signature='sss',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def GetTransferProgress(self, TransferID,
dbus_async_cb, dbus_async_err_cb):
arguments = {'TransferID': str(TransferID)}
r = self.callAction('GetTransferProgress', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['TransferStatus']),
str(data['TransferLength']),
str(data['TransferTotal']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(CDS_SERVICE, in_signature='ss', out_signature='s',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def CreateReference(self, ContainerID, ObjectID,
dbus_async_cb, dbus_async_err_cb):
arguments = {'ContainerID': str(ContainerID),
'ObjectID': str(ObjectID)}
r = self.callAction('CreateReference', arguments)
if r == '':
return r
def convert_reply(data):
dbus_async_cb(str(data['NewID']))
r.addCallback(convert_reply)
r.addErrback(dbus_async_err_cb)
[docs] def callAction(self, name, arguments):
action = self.service.get_action(name)
if action is not None:
d = action.call(**arguments)
return d
return ''
[docs]class DBusService(dbus.service.Object, log.LogAble):
logCategory = 'dbus'
SUPPORTS_MULTIPLE_CONNECTIONS = True
def __init__(self, service, dbus_device, bus):
log.LogAble.__init__(self)
self.service = service
self.dbus_device = dbus_device
if self.service is not None:
self.type = self.service.service_type.split(':')[
3] # get the service name
self.type = self.type.replace('-', '')
else:
self.type = "from_the_tubes"
try:
bus_name = dbus.service.BusName(SERVICE_IFACE, bus)
except Exception:
bus_name = None
self.tube = bus
else:
self.tube = None
if self.dbus_device is not None:
self.device_id = self.dbus_device.id
else:
self.device_id = "dev_from_the_tubes"
self.path = \
OBJECT_PATH + '/devices/' + self.device_id + \
'/services/' + self.type
dbus.service.Object.__init__(self, bus, bus_name=bus_name,
object_path=self.path)
self.debug("DBusService %r %r", service, self.type)
louie.connect(self.variable_changed,
'Coherence.UPnP.StateVariable.changed',
sender=self.service)
self.subscribe()
# interfaces = self._dbus_class_table[
# self.__class__.__module__ + '.' + self.__class__.__name__]
# for (name, funcs) in interfaces.iteritems():
# print name, funcs
# if funcs.has_key('destroy_object'):
# print """removing 'destroy_object'"""
# del funcs['destroy_object']
# for func in funcs.values():
# if getattr(func, '_dbus_is_method', False):
# print self.__class__._reflect_on_method(func)
# self._get_service_methods()
[docs] def shutdown(self):
self._release_thyself(suicide_mode=False)
[docs] def _release_thyself(self, suicide_mode=True):
louie.disconnect(self.variable_changed,
'Coherence.UPnP.StateVariable.changed',
sender=self.service)
self.service = None
self.dbus_device = None
self.tube = None
self.remove_from_connection()
self.path = None
if suicide_mode:
del self
[docs] def _get_service_methods(self):
'''Returns a list of method descriptors for this object'''
methods = []
for func in dir(self):
func = getattr(self, func)
if callable(func) and hasattr(func, '_dbus_is_method'):
print(func, func._dbus_interface, func._dbus_is_method)
if hasattr(func, 'im_func'):
print(func.__func__)
[docs] def variable_changed(self, variable):
# print self.service, "got signal for change of", variable
# print variable.name, variable.value
# print type(variable.name), type(variable.value)
self.StateVariableChanged(self.device_id, self.type, variable.name,
variable.value)
@dbus.service.signal(SERVICE_IFACE,
signature='sssv')
def StateVariableChanged(self, udn, service, variable, value):
self.info("%s service %s signals StateVariable %s changed to %r",
self.device_id, self.type, variable, value)
[docs] @dbus.service.method(SERVICE_IFACE, in_signature='', out_signature='s')
def get_scpd_xml(self):
return self.service.get_scpdXML()
[docs] @dbus.service.method(SERVICE_IFACE, in_signature='', out_signature='as')
def get_available_actions(self):
actions = self.service.get_actions()
r = []
for name in list(actions.keys()):
r.append(name)
return r
[docs] @dbus.service.method(SERVICE_IFACE, in_signature='', out_signature='s')
def get_id(self):
return self.service.id
[docs] @dbus.service.method(SERVICE_IFACE, in_signature='sv', out_signature='v',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def action(self, name, arguments, dbus_async_cb, dbus_async_err_cb):
# print "action", name, arguments
def reply(data):
dbus_async_cb(
dbus.Dictionary(data, signature='sv', variant_level=4))
if self.service.client is not None:
# print "action", name
func = getattr(self.service.client, name, None)
# print "action", func
if callable(func):
kwargs = {}
try:
for k, v in list(arguments.items()):
kwargs[str(k)] = str(v)
except Exception as e:
self.error('DBusService.action: %r' % e)
d = func(**kwargs)
d.addCallback(reply)
d.addErrback(dbus_async_err_cb)
return ''
[docs] @dbus.service.method(SERVICE_IFACE, in_signature='sa{ss}',
out_signature='v',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb',),
sender_keyword='sender',
connection_keyword='connection')
def call_action(self, name, arguments, dbus_async_cb, dbus_async_err_cb,
sender=None, connection=None):
print("call_action called by ", sender, connection, self.type,
self.tube)
def reply(data, name, connection):
if hasattr(connection, '_tube'):
if name == 'Browse':
didl = DIDLLite.DIDLElement.fromString(data['Result'])
changed = False
for item in didl.getItems():
new_res = DIDLLite.Resources()
for res in item.res:
remote_protocol, remote_network, \
remote_content_format, _ = \
res.protocolInfo.split(':')
if remote_protocol == 'http-get' and \
remote_network == '*':
quoted_url = 'mirabeau' + '/' + \
urllib.parse.quote_plus(res.data)
print("modifying", res.data)
host_port = \
':'.join(
(self.service.device.client.
coherence.mirabeau._external_address,
str(self.service.device.client.
coherence.mirabeau._external_port)
)
)
res.data = urllib.parse.urlunsplit(
('http', host_port, quoted_url, "", ""))
print("--->", res.data)
new_res.append(res)
changed = True
item.res = new_res
if changed:
didl.rebuild()
# FIXME this is not the proper way to do it
data['Result'] = didl.toString().replace('<ns0:',
'<').replace(
'</ns0:', '</')
dbus_async_cb(
dbus.Dictionary(data, signature='sv', variant_level=4))
if self.service.client is not None:
action = self.service.get_action(name)
if action:
kwargs = {}
try:
for k, v in list(arguments.items()):
kwargs[str(k)] = str(v)
except Exception as e:
self.error('DBusService.call_action: %r' % e)
d = action.call(**kwargs)
d.addCallback(reply, name, connection)
d.addErrback(dbus_async_err_cb)
return ''
[docs] @dbus.service.method(SERVICE_IFACE, in_signature='v', out_signature='v',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def destroy_object(self, arguments, dbus_async_cb, dbus_async_err_cb):
def reply(data):
dbus_async_cb(
dbus.Dictionary(data, signature='sv', variant_level=4))
if self.service.client is not None:
kwargs = {}
for k, v in list(arguments.items()):
kwargs[str(k)] = str(v)
d = self.service.client.destroy_object(**kwargs)
d.addCallback(reply)
d.addErrback(dbus_async_err_cb)
return ''
[docs] @dbus.service.method(SERVICE_IFACE, in_signature='', out_signature='ssv')
def subscribe(self):
notify = []
if self.service:
notify = [v for v in list(self.service._variables[0].values()) if
v.send_events]
if len(notify) == 0:
return
data = {}
for n in notify:
if n.name == 'LastChange':
lc = {}
for instance, vdict in list(self.service._variables.items()):
v = {}
for variable in list(vdict.values()):
if (variable.name != 'LastChange' and
variable.name[0:11] != 'A_ARG_TYPE_' and
variable.never_evented is False):
if not hasattr(variable, 'dbus_updated'):
variable.dbus_updated = None
# FIXME: we are missing variable dependencies here
# if variable.dbus_updated != \
# variable.last_touched:
# v[str(variable.name)] = str(variable.value)
# variable.dbus_updated = time.time()
if len(v) > 0:
lc[str(instance)] = v
if len(lc) > 0:
data[str(n.name)] = lc
else:
data[str(n.name)] = str(n.value)
return self.dbus_device.device.get_id(), self.type, dbus.Dictionary(
data, signature='sv', variant_level=3)
[docs]class DBusDevice(dbus.service.Object, log.LogAble):
logCategory = 'dbus'
SUPPORTS_MULTIPLE_CONNECTIONS = True
def __init__(self, device, bus):
log.LogAble.__init__(self)
if device is not None:
self.uuid = device.get_id()[5:]
self.id = self.uuid.replace('-', '')
# we shouldn't need to do this, but ...
self.id = self.id.replace('+', '')
else:
self.id = "from_the_tubes"
try:
bus_name = dbus.service.BusName(DEVICE_IFACE, bus)
except Exception:
bus_name = None
self.tube = bus
else:
self.tube = None
dbus.service.Object.__init__(self, bus, bus_name=bus_name,
object_path=self.path())
self.services = []
self.device = device
self.debug("DBusDevice %r %r", device, self.id)
if device is not None:
for service in device.get_services():
self.services.append(DBusService(service, self, bus))
if service.service_type.split(':')[3] == 'ContentDirectory':
self.services.append(DBusCDSService(service, self, bus))
[docs] def shutdown(self):
self._release_thyself(suicide_mode=False)
[docs] def _release_thyself(self, suicide_mode=True):
for service in self.services:
service._release_thyself()
self.services = None
self.device = None
self.tube = None
self.remove_from_connection()
# FIXME: this is insane
if suicide_mode:
del self
[docs] def path(self):
return OBJECT_PATH + '/devices/' + self.id
[docs] @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='v')
def get_info(self):
services = [x.path for x in self.services
if not getattr(x, "NOT_FOR_THE_TUBES", False)]
r = {'path': self.path(),
'device_type': self.device.get_device_type(),
'friendly_name': self.device.get_friendly_name(),
'udn': self.device.get_id(),
'uri': list(urllib.parse.urlsplit(self.device.get_location())),
'presentation_url': self.device.get_presentation_url(),
'parent_udn': self.device.get_parent_id(),
'services': services}
return dbus.Dictionary(r, signature='sv', variant_level=2)
[docs] @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='s')
def get_markup_name(self):
return self.device.get_markup_name()
[docs] @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='s')
def get_friendly_name(self):
return self.device.get_friendly_name()
[docs] @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='s')
def get_friendly_device_type(self):
return self.device.get_friendly_device_type()
[docs] @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='i')
def get_device_type_version(self):
return int(self.device.get_device_type_version())
[docs] @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='s')
def get_id(self):
return self.device.get_id()
[docs] @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='s')
def get_device_type(self):
return self.device.get_device_type()
[docs] @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='s')
def get_usn(self):
return self.device.get_usn()
[docs] @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='av')
def get_device_icons(self):
return dbus.Array(self.device.icons, signature='av', variant_level=2)
[docs]class DBusPontoon(dbus.service.Object, log.LogAble):
logCategory = 'dbus'
SUPPORTS_MULTIPLE_CONNECTIONS = True
def __init__(self, controlpoint, bus=None):
log.LogAble.__init__(self)
self.bus = bus or dbus.SessionBus()
try:
bus_name = dbus.service.BusName(BUS_NAME, self.bus)
except Exception:
bus_name = None
self.tube = self.bus
else:
self.tube = None
self.bus_name = bus_name
dbus.service.Object.__init__(self, self.bus, bus_name=self.bus_name,
object_path=OBJECT_PATH)
self.debug("D-Bus pontoon %r %r %r", self, self.bus, self.bus_name)
self.devices = {}
self.controlpoint = controlpoint
self.pinboard = {}
# i am a stub service if i have no control point
if self.controlpoint is None:
return
for device in self.controlpoint.get_devices():
self.devices[device.get_id()] = DBusDevice(device, self.bus_name)
# louie.connect(
# self.cp_ms_detected,
# 'Coherence.UPnP.ControlPoint.MediaServer.detected', louie.Any)
# louie.connect(
# self.cp_ms_removed,
# 'Coherence.UPnP.ControlPoint.MediaServer.removed', louie.Any)
# louie.connect(
# self.cp_mr_detected,
# 'Coherence.UPnP.ControlPoint.MediaRenderer.detected', louie.Any)
# louie.connect(
# self.cp_mr_removed,
# 'Coherence.UPnP.ControlPoint.MediaRenderer.removed', louie.Any)
# louie.connect(
# self.remove_client,
# 'Coherence.UPnP.Device.remove_client', louie.Any)
louie.connect(
self._device_detected,
'Coherence.UPnP.Device.detection_completed', louie.Any)
louie.connect(
self._device_removed,
'Coherence.UPnP.Device.removed', louie.Any)
self.debug("D-Bus pontoon started")
[docs] def shutdown(self):
louie.disconnect(
self._device_detected,
'Coherence.UPnP.Device.detection_completed', louie.Any)
louie.disconnect(
self._device_removed,
'Coherence.UPnP.Device.removed', louie.Any)
for device_id, device in self.devices.items():
device.shutdown()
self.devices = {}
self.remove_from_connection()
self.bus = None
[docs] @dbus.service.method(BUS_NAME, in_signature='sv', out_signature='')
def pin(self, key, value):
self.pinboard[key] = value
print(self.pinboard)
[docs] @dbus.service.method(BUS_NAME, in_signature='s', out_signature='v')
def get_pin(self, key):
return self.pinboard.get(key, 'Coherence::Pin::None')
[docs] @dbus.service.method(BUS_NAME, in_signature='s', out_signature='')
def unpin(self, key):
del self.pinboard[key]
[docs] @dbus.service.method(BUS_NAME, in_signature='s', out_signature='s')
def create_oob(self, file):
print('create_oob')
key = str(time.time())
self.pinboard[key] = file
print(self.pinboard)
return self.controlpoint.coherence.urlbase + 'oob?key=' + key
[docs] def remove_client(self, usn, client):
self.info("removed %s %s", client.device_type,
client.device.get_friendly_name())
try:
getattr(self,
str('UPnP_ControlPoint_%s_removed' % client.device_type))(
usn)
except Exception as e:
self.error('DBusPontoon.remove_client: %r' % e)
[docs] def remove(self, udn):
# print "DBusPontoon remove", udn
# print "before remove", self.devices
d = self.devices.pop(udn)
d._release_thyself()
del d
# print "after remove", self.devices
[docs] @dbus.service.method(BUS_NAME, in_signature='', out_signature='s')
def version(self):
return __version__
[docs] @dbus.service.method(BUS_NAME, in_signature='', out_signature='s')
def hostname(self):
return self.controlpoint.coherence.hostname
[docs] @dbus.service.method(BUS_NAME, in_signature='', out_signature='av')
def get_devices(self):
r = []
for device in list(self.devices.values()):
# r.append(device.path())
r.append(device.get_info())
return dbus.Array(r, signature='v', variant_level=2)
[docs] @dbus.service.method(BUS_NAME, in_signature='i', out_signature='av',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def get_devices_async(self, for_mirabeau,
dbus_async_cb, dbus_async_err_cb):
infos = []
allowed_device_types = ['urn:schemas-upnp-org:device:MediaServer:2',
'urn:schemas-upnp-org:device:MediaServer:1']
def iterate_devices(devices):
for device in devices:
if for_mirabeau and device.get_device_type() not in \
allowed_device_types:
continue
infos.append(device.get_info())
yield infos
def done(generator):
dbus_async_cb(dbus.Array(infos, signature='v', variant_level=2))
devices = list(self.devices.copy().values())
dfr = task.coiterate(iterate_devices(devices))
dfr.addCallbacks(
done, lambda failure: dbus_async_err_cb(failure.value))
[docs] @dbus.service.method(BUS_NAME, in_signature='s', out_signature='v')
def get_device_with_id(self, id):
for device in list(self.devices.values()):
if id == device.device.get_id():
return device.get_info()
[docs] @dbus.service.method(BUS_NAME, in_signature='sa{ss}', out_signature='s')
def add_plugin(self, backend, arguments):
kwargs = {}
for k, v in arguments.items():
kwargs[str(k)] = str(v)
p = self.controlpoint.coherence.add_plugin(backend, **kwargs)
return str(p.uuid)
[docs] @dbus.service.method(BUS_NAME, in_signature='s', out_signature='s')
def remove_plugin(self, uuid):
return str(self.controlpoint.coherence.remove_plugin(uuid))
[docs] @dbus.service.method(BUS_NAME, in_signature='ssa{ss}', out_signature='s')
def call_plugin(self, uuid, method, arguments):
try:
plugin = self.controlpoint.coherence.active_backends[uuid]
except KeyError:
self.warning("no backend with the uuid %r found", uuid)
return ""
function = getattr(plugin.backend, method, None)
if function is None:
return ""
kwargs = {}
for k, v in arguments.items():
kwargs[str(k)] = str(v)
function(**kwargs)
return uuid
[docs] @dbus.service.method(BUS_NAME, in_signature='ssa{ss}', out_signature='v',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def create_object(self, device_id, container_id, arguments, dbus_async_cb,
dbus_async_err_cb):
device = self.controlpoint.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
new_arguments = {}
for k, v in list(arguments.items()):
new_arguments[str(k)] = str(v)
def reply(data):
dbus_async_cb(
dbus.Dictionary(data, signature='sv', variant_level=4))
d = client.content_directory.create_object(str(container_id),
new_arguments)
d.addCallback(reply)
d.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(BUS_NAME, in_signature='sss', out_signature='v',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def import_resource(self, device_id, source_uri, destination_uri,
dbus_async_cb, dbus_async_err_cb):
device = self.controlpoint.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
def reply(data):
dbus_async_cb(
dbus.Dictionary(data, signature='sv', variant_level=4))
d = client.content_directory.import_resource(str(source_uri),
str(destination_uri))
d.addCallback(reply)
d.addErrback(dbus_async_err_cb)
[docs] @dbus.service.method(BUS_NAME, in_signature='ss', out_signature='v',
async_callbacks=('dbus_async_cb',
'dbus_async_err_cb'))
def put_resource(self, destination_uri, filepath, dbus_async_cb,
dbus_async_err_cb):
def reply(data):
dbus_async_cb(200)
d = self.controlpoint.put_resource(str(destination_uri), str(filepath))
d.addCallback(reply)
d.addErrback(dbus_async_err_cb)
[docs] def _device_detected(self, device):
id = device.get_id()
print("new_device_detected", device.get_usn(),
device.friendly_device_type, id)
if id not in self.devices:
new_device = DBusDevice(device, self.bus)
self.devices[id] = new_device
# print self.devices, id
info = new_device.get_info()
self.device_detected(info, id)
if device.get_friendly_device_type() == 'MediaServer':
self.UPnP_ControlPoint_MediaServer_detected(info, id)
elif device.get_friendly_device_type() == 'MediaRenderer':
self.UPnP_ControlPoint_MediaRenderer_detected(info, id)
[docs] def _device_removed(self, usn=''):
print("_device_removed", usn)
id = usn.split('::')[0]
device = self.devices[id]
self.device_removed(id)
# print device.get_friendly_device_type()
if device.get_friendly_device_type() == 'MediaServer':
self.UPnP_ControlPoint_MediaServer_removed(id)
if device.get_friendly_device_type() == 'MediaRenderer':
self.UPnP_ControlPoint_MediaServer_removed(id)
reactor.callLater(1, self.remove, id)
[docs] def cp_ms_detected(self, client, udn=''):
print("cp_ms_detected", udn)
if client.device.get_id() not in self.devices:
new_device = DBusDevice(client.device, self.bus)
self.devices[client.device.get_id()] = new_device
self.UPnP_ControlPoint_MediaServer_detected(new_device.get_info(),
udn)
[docs] def cp_mr_detected(self, client, udn=''):
if client.device.get_id() not in self.devices:
new_device = DBusDevice(client.device, self.bus)
self.devices[client.device.get_id()] = new_device
self.UPnP_ControlPoint_MediaRenderer_detected(
new_device.get_info(), udn)
[docs] def cp_ms_removed(self, udn):
print("cp_ms_removed", udn)
self.UPnP_ControlPoint_MediaServer_removed(udn)
# schedule removal of device from our cache after signal has
# been called. Let's assume one second is long enough...
reactor.callLater(1, self.remove, udn)
[docs] def cp_mr_removed(self, udn):
# print "cp_mr_removed", udn
self.UPnP_ControlPoint_MediaRenderer_removed(udn)
# schedule removal of device from our cache after signal has
# been called. Let's assume one second is long enough...
reactor.callLater(1, self.remove, udn)
@dbus.service.signal(BUS_NAME,
signature='vs')
def UPnP_ControlPoint_MediaServer_detected(self, device, udn):
self.info("emitting signal UPnP_ControlPoint_MediaServer_detected")
@dbus.service.signal(BUS_NAME,
signature='s')
def UPnP_ControlPoint_MediaServer_removed(self, udn):
self.info("emitting signal UPnP_ControlPoint_MediaServer_removed")
@dbus.service.signal(BUS_NAME,
signature='vs')
def UPnP_ControlPoint_MediaRenderer_detected(self, device, udn):
self.info("emitting signal UPnP_ControlPoint_MediaRenderer_detected")
@dbus.service.signal(BUS_NAME,
signature='s')
def UPnP_ControlPoint_MediaRenderer_removed(self, udn):
self.info("emitting signal UPnP_ControlPoint_MediaRenderer_removed")
@dbus.service.signal(BUS_NAME,
signature='vs')
def device_detected(self, device, udn):
self.info("emitting signal device_detected")
@dbus.service.signal(BUS_NAME,
signature='s')
def device_removed(self, udn):
self.info("emitting signal device_removed")
""" org.DLNA related methods and signals
"""
[docs] @dbus.service.method(DLNA_BUS_NAME + '.DMC', in_signature='',
out_signature='av')
def getDMSList(self):
return dbus.Array(self._get_devices_of_type('MediaServer'),
signature='v', variant_level=2)
[docs] def _get_devices_of_type(self, typ):
return [device.get_info() for device in self.devices.values()
if device.get_friendly_device_type() == typ]
[docs] @dbus.service.method(DLNA_BUS_NAME + '.DMC', in_signature='',
out_signature='av')
def getDMRList(self):
return dbus.Array(self._get_devices_of_type('MediaRenderer'),
signature='v', variant_level=2)
@dbus.service.signal(BUS_NAME,
signature='vs')
def DMS_added(self, device, udn):
self.info("emitting signal DMS_added")
@dbus.service.signal(BUS_NAME,
signature='s')
def DMS_removed(self, udn):
self.info("emitting signal DMS_removed")
@dbus.service.signal(BUS_NAME,
signature='vs')
def DMR_added(self, device, udn):
self.info("emitting signal DMR_added")
@dbus.service.signal(BUS_NAME,
signature='s')
def DMR_removed(self, udn):
self.info("emitting signal DMR_detected")