plugin ad-hoc D-Bus: fix deprecated use of python-dbus:

the plugin was using python-dbus which is deprecated in the backend in the favor of
TxDBus. Methods calls have been updated, and the plugin works again, but there seems to be
still some issues (warnings in the logs). Those will be fixed later.
This commit is contained in:
Goffi 2023-12-01 15:22:55 +01:00
commit ec2a637847
2 changed files with 79 additions and 75 deletions

View file

@ -17,14 +17,21 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from libervia.backend.core.i18n import D_, _
from collections import OrderedDict
import os.path
import uuid
from twisted.internet import defer
from twisted.internet import reactor
from twisted.words.protocols.jabber import jid
from wokkel import data_form
from libervia.backend.core.constants import Const as C
from libervia.backend.core.i18n import D_, _
from libervia.backend.core.log import getLogger
log = getLogger(__name__)
from twisted.internet import defer
from twisted.words.protocols.jabber import jid
from wokkel import data_form
try:
from lxml import etree
@ -32,19 +39,17 @@ except ImportError:
etree = None
log.warning("Missing module lxml, please download/install it from http://lxml.de/ ."
"Auto D-Bus discovery will be disabled")
from collections import OrderedDict
import os.path
import uuid
try:
import dbus
from dbus.mainloop.glib import DBusGMainLoop
except ImportError:
dbus = None
log.warning("Missing module dbus, please download/install it, "
"auto D-Bus discovery will be disabled")
else:
DBusGMainLoop(set_as_default=True)
try:
import txdbus
from txdbus import client as dbus_client
except ImportError:
txdbus = None
log.warning(
"Missing module txdbus, please download/install it, auto D-Bus discovery will be "
"disabled"
)
NS_MEDIA_PLAYER = "org.libervia.mediaplayer"
FD_NAME = "org.freedesktop.DBus"
@ -92,10 +97,10 @@ PLUGIN_INFO = {
}
class AdHocDBus(object):
class AdHocDBus:
def __init__(self, host):
log.info(_("plugin Ad-Hoc D-Bus initialization"))
log.info(f"plugin {PLUGIN_INFO[C.PI_NAME]!r} initialization")
self.host = host
if etree is not None:
host.bridge.add_method(
@ -116,13 +121,14 @@ class AdHocDBus(object):
)
self._c = host.plugins["XEP-0050"]
host.register_namespace("mediaplayer", NS_MEDIA_PLAYER)
if dbus is not None:
self.session_bus = dbus.SessionBus()
self.fd_object = self.session_bus.get_object(
FD_NAME, FD_PATH, introspect=False)
self.session_con = None
async def profile_connected(self, client):
if txdbus is not None:
if self.session_con is None:
self.session_con = await dbus_client.connect(reactor, 'session')
self.fd_object = await self.session_con.getRemoteObject(FD_NAME, FD_PATH)
def profile_connected(self, client):
if dbus is not None:
self._c.add_ad_hoc_command(
client, self.local_media_cb, D_("Media Players"),
node=NS_MEDIA_PLAYER,
@ -130,7 +136,7 @@ class AdHocDBus(object):
# in the middle of a movie
)
def _dbus_async_call(self, proxy, method, *args, **kwargs):
async def _dbus_async_call(self, proxy, method, *args, **kwargs):
""" Call a DBus method asynchronously and return a deferred
@param proxy: DBus object proxy, as returner by get_object
@ -142,23 +148,18 @@ class AdHocDBus(object):
@return: a deferred
"""
d = defer.Deferred()
interface = kwargs.pop("interface", None)
kwargs["reply_handler"] = lambda ret=None: d.callback(ret)
kwargs["error_handler"] = d.errback
proxy.get_dbus_method(method, dbus_interface=interface)(*args, **kwargs)
return d
return await proxy.callRemote(method, *args, **kwargs)
def _dbus_get_property(self, proxy, interface, name):
return self._dbus_async_call(
async def _dbus_get_property(self, proxy, interface, name):
return await self._dbus_async_call(
proxy, "Get", interface, name, interface="org.freedesktop.DBus.Properties")
def _dbus_list_names(self):
return self._dbus_async_call(self.fd_object, "ListNames")
async def _dbus_list_names(self):
return await self.fd_object.callRemote("ListNames")
def _dbus_introspect(self, proxy):
return self._dbus_async_call(proxy, INTROSPECT_METHOD, interface=INTROSPECT_IFACE)
async def _dbus_introspect(self, proxy):
return await self._dbus_async_call(proxy, INTROSPECT_METHOD, interface=INTROSPECT_IFACE)
def _accept_method(self, method):
""" Return True if we accept the method for a command
@ -172,18 +173,18 @@ class AdHocDBus(object):
return False
return True
@defer.inlineCallbacks
def _introspect(self, methods, bus_name, proxy):
async def _introspect(self, methods, bus_name, proxy):
log.debug("introspecting path [%s]" % proxy.object_path)
introspect_xml = yield self._dbus_introspect(proxy)
assert etree is not None
introspect_xml = await self._dbus_introspect(proxy)
el = etree.fromstring(introspect_xml)
for node in el.iterchildren("node", "interface"):
if node.tag == "node":
new_path = os.path.join(proxy.object_path, node.get("name"))
new_proxy = self.session_bus.get_object(
bus_name, new_path, introspect=False
new_proxy = await self.session_con.getRemoteObject(
bus_name, new_path
)
yield self._introspect(methods, bus_name, new_proxy)
await self._introspect(methods, bus_name, new_proxy)
elif node.tag == "interface":
name = node.get("name")
if any(name.startswith(ignored) for ignored in IGNORED_IFACES_START):
@ -203,24 +204,26 @@ class AdHocDBus(object):
client, prog_name, allowed_jids, allowed_groups, allowed_magics,
forbidden_jids, forbidden_groups, flags)
@defer.inlineCallbacks
def ad_hoc_dbus_add_auto(self, client, prog_name, allowed_jids=None, allowed_groups=None,
async def ad_hoc_dbus_add_auto(self, client, prog_name, allowed_jids=None, allowed_groups=None,
allowed_magics=None, forbidden_jids=None, forbidden_groups=None,
flags=None):
bus_names = yield self._dbus_list_names()
bus_names = await self._dbus_list_names()
bus_names = [bus_name for bus_name in bus_names if "." + prog_name in bus_name]
if not bus_names:
log.info("Can't find any bus for [%s]" % prog_name)
defer.returnValue(("", []))
return ("", [])
bus_names.sort()
for bus_name in bus_names:
if bus_name.endswith(prog_name):
break
log.info("bus name found: [%s]" % bus_name)
proxy = self.session_bus.get_object(bus_name, "/", introspect=False)
else:
log.info(f"Can't find any command for {prog_name}")
return ("", [])
log.info(f"bus name found: {bus_name}")
proxy = await self.session_con.getRemoteObject(bus_name, "/")
methods = set()
yield self._introspect(methods, bus_name, proxy)
await self._introspect(methods, bus_name, proxy)
if methods:
self._add_command(
@ -236,7 +239,7 @@ class AdHocDBus(object):
flags=flags,
)
defer.returnValue((str(bus_name), methods))
return (str(bus_name), methods)
def _add_command(self, client, adhoc_name, bus_name, methods, allowed_jids=None,
allowed_groups=None, allowed_magics=None, forbidden_jids=None,
@ -244,7 +247,7 @@ class AdHocDBus(object):
if flags is None:
flags = set()
def d_bus_callback(client, command_elt, session_data, action, node):
async def d_bus_callback(client, command_elt, session_data, action, node):
actions = session_data.setdefault("actions", [])
names_map = session_data.setdefault("names_map", {})
actions.append(action)
@ -281,9 +284,9 @@ class AdHocDBus(object):
raise self._c.AdHocError(self._c.ERROR.BAD_PAYLOAD)
path, iface, command = names_map[command]
proxy = self.session_bus.get_object(bus_name, path)
proxy = await self.session_con.getRemoteObject(bus_name, path)
self._dbus_async_call(proxy, command, interface=iface)
await self._dbus_async_call(proxy, command, interface=iface)
# job done, we can end the session, except if we have FLAG_LOOP
if FLAG_LOOP in flags:
@ -292,7 +295,7 @@ class AdHocDBus(object):
# is OK)
del actions[:]
names_map.clear()
return d_bus_callback(
return await d_bus_callback(
client, None, session_data, self._c.ACTION.EXECUTE, node
)
form = data_form.Form("form", title=_("Updated"))
@ -321,15 +324,14 @@ class AdHocDBus(object):
def _ad_hoc_remotes_get(self, profile):
return self.ad_hoc_remotes_get(self.host.get_client(profile))
@defer.inlineCallbacks
def ad_hoc_remotes_get(self, client):
async def ad_hoc_remotes_get(self, client):
"""Retrieve available remote media controlers in our devices
@return (list[tuple[unicode, unicode, unicode]]): list of devices with:
- entity full jid
- device name
- device label
"""
found_data = yield defer.ensureDeferred(self.host.find_by_features(
found_data = await defer.ensureDeferred(self.host.find_by_features(
client, [self.host.ns_map['commands']], service=False, roster=False,
own_jid=True, local_device=True))
@ -338,11 +340,11 @@ class AdHocDBus(object):
for found in found_data:
for device_jid_s in found:
device_jid = jid.JID(device_jid_s)
cmd_list = yield self._c.list(client, device_jid)
cmd_list = await self._c.list(client, device_jid)
for cmd in cmd_list:
if cmd.nodeIdentifier == NS_MEDIA_PLAYER:
try:
result_elt = yield self._c.do(client, device_jid,
result_elt = await self._c.do(client, device_jid,
NS_MEDIA_PLAYER, timeout=5)
command_elt = self._c.get_command_elt(result_elt)
form = data_form.findForm(command_elt, NS_MEDIA_PLAYER)
@ -366,9 +368,9 @@ class AdHocDBus(object):
"Can't retrieve remote controllers on {device_jid}: "
"{reason}".format(device_jid=device_jid, reason=e)))
break
defer.returnValue(remotes)
return remotes
def do_mpris_command(self, proxy, command):
async def do_mpris_command(self, proxy, command):
iface, command = command.rsplit(".", 1)
if command == CMD_GO_BACK:
command = 'Seek'
@ -378,7 +380,7 @@ class AdHocDBus(object):
args = [SEEK_OFFSET]
else:
args = []
return self._dbus_async_call(proxy, command, *args, interface=iface)
return await self._dbus_async_call(proxy, command, *args, interface=iface)
def add_mpris_metadata(self, form, metadata):
"""Serialise MRPIS Metadata according to MPRIS_METADATA_MAP"""
@ -389,8 +391,8 @@ class AdHocDBus(object):
var=name,
value=value))
@defer.inlineCallbacks
def local_media_cb(self, client, command_elt, session_data, action, node):
async def local_media_cb(self, client, command_elt, session_data, action, node):
assert txdbus is not None
try:
x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
command_form = data_form.Form.fromElement(x_elt)
@ -399,11 +401,11 @@ class AdHocDBus(object):
if command_form is None or len(command_form.fields) == 0:
# root request, we looks for media players
bus_names = yield self._dbus_list_names()
bus_names = await self._dbus_list_names()
bus_names = [b for b in bus_names if b.startswith(MPRIS_PREFIX)]
if len(bus_names) == 0:
note = (self._c.NOTE.INFO, D_("No media player found."))
defer.returnValue((None, self._c.STATUS.COMPLETED, None, note))
return (None, self._c.STATUS.COMPLETED, None, note)
options = []
status = self._c.STATUS.EXECUTING
form = data_form.Form("form", title=D_("Media Player Selection"),
@ -419,7 +421,7 @@ class AdHocDBus(object):
)
form.addField(field)
payload = form.toElement()
defer.returnValue((payload, status, None, None))
return (payload, status, None, None)
else:
# player request
try:
@ -432,20 +434,22 @@ class AdHocDBus(object):
"Hack attempt? Refused bus: {bus_name}").format(
bus_name=bus_name))
note = (self._c.NOTE.ERROR, D_("Invalid player name."))
defer.returnValue((None, self._c.STATUS.COMPLETED, None, note))
return (None, self._c.STATUS.COMPLETED, None, note)
try:
proxy = self.session_bus.get_object(bus_name, MPRIS_PATH)
except dbus.exceptions.DBusException as e:
proxy = await self.session_con.getRemoteObject(
bus_name, MPRIS_PATH, "org.mpris.MediaPlayer2.Player"
)
except Exception as e:
log.warning(_("Can't get D-Bus proxy: {reason}").format(reason=e))
note = (self._c.NOTE.ERROR, D_("Media player is not available anymore"))
defer.returnValue((None, self._c.STATUS.COMPLETED, None, note))
return (None, self._c.STATUS.COMPLETED, None, note)
try:
command = command_form["command"]
except KeyError:
pass
else:
yield self.do_mpris_command(proxy, command)
await self.do_mpris_command(proxy, command)
# we construct the remote control form
form = data_form.Form("form", title=D_("Media Player Selection"))
@ -455,7 +459,7 @@ class AdHocDBus(object):
for iface, properties_names in MPRIS_PROPERTIES.items():
for name in properties_names:
try:
value = yield self._dbus_get_property(proxy, iface, name)
value = await self._dbus_get_property(proxy, iface, name)
except Exception as e:
log.warning(_("Can't retrieve attribute {name}: {reason}")
.format(name=name, reason=e))
@ -475,4 +479,4 @@ class AdHocDBus(object):
payload = form.toElement()
status = self._c.STATUS.EXECUTING
defer.returnValue((payload, status, None, None))
return (payload, status, None, None)

View file

@ -117,7 +117,7 @@ class Run(base.CommandBase):
"-j",
"--jid",
default="",
help=_("jid of the service (default: profile's server"),
help=_("jid of the service (default: profile's server)"),
)
self.parser.add_argument(
"-S",