mirror of
https://github.com/SoongNoonien/mpdevil.git
synced 2023-08-10 21:12:44 +03:00
migrated MPRISInterface to Gio
This commit is contained in:
parent
7ae0f1e216
commit
b6be0f0430
@ -61,7 +61,6 @@ Python modules:
|
|||||||
- gi (Gtk, Gio, Gdk, GdkPixbuf, Pango, GObject, GLib, Notify)
|
- gi (Gtk, Gio, Gdk, GdkPixbuf, Pango, GObject, GLib, Notify)
|
||||||
- requests
|
- requests
|
||||||
- bs4 (beautifulsoup)
|
- bs4 (beautifulsoup)
|
||||||
- dbus
|
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
```bash
|
```bash
|
||||||
|
406
bin/mpdevil
406
bin/mpdevil
@ -34,12 +34,6 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# MPRIS modules
|
|
||||||
import dbus
|
|
||||||
import dbus.service
|
|
||||||
from dbus.mainloop.glib import DBusGMainLoop
|
|
||||||
DBusGMainLoop(set_as_default=True)
|
|
||||||
|
|
||||||
VERSION="0.9.7-dev" # sync with setup.py
|
VERSION="0.9.7-dev" # sync with setup.py
|
||||||
COVER_REGEX=r"^\.?(album|cover|folder|front).*\.(gif|jpeg|jpg|png)$"
|
COVER_REGEX=r"^\.?(album|cover|folder|front).*\.(gif|jpeg|jpg|png)$"
|
||||||
|
|
||||||
@ -48,23 +42,134 @@ COVER_REGEX=r"^\.?(album|cover|folder|front).*\.(gif|jpeg|jpg|png)$"
|
|||||||
# MPRIS #
|
# MPRIS #
|
||||||
#########
|
#########
|
||||||
|
|
||||||
class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
|
class MPRISInterface: # TODO emit Seeked if needed
|
||||||
"""
|
"""
|
||||||
based on 'mpDris2' (master 19.03.2020) by Jean-Philippe Braun <eon@patapon.info>, Mantas Mikulėnas <grawity@gmail.com>
|
based on 'Lollypop' (master 22.12.2020) by Cedric Bellegarde <cedric.bellegarde@adishatz.org>
|
||||||
|
and 'mpDris2' (master 19.03.2020) by Jean-Philippe Braun <eon@patapon.info>, Mantas Mikulėnas <grawity@gmail.com>
|
||||||
|
"""
|
||||||
|
_MPRIS_IFACE="org.mpris.MediaPlayer2"
|
||||||
|
_MPRIS_PLAYER_IFACE="org.mpris.MediaPlayer2.Player"
|
||||||
|
_MPRIS_NAME="org.mpris.MediaPlayer2.mpdevil"
|
||||||
|
_MPRIS_PATH="/org/mpris/MediaPlayer2"
|
||||||
|
_INTERFACES_XML="""
|
||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
<interface name="org.freedesktop.DBus.Introspectable">
|
||||||
|
<method name="Introspect">
|
||||||
|
<arg name="data" direction="out" type="s"/>
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
<interface name="org.freedesktop.DBus.Properties">
|
||||||
|
<method name="Get">
|
||||||
|
<arg name="interface" direction="in" type="s"/>
|
||||||
|
<arg name="property" direction="in" type="s"/>
|
||||||
|
<arg name="value" direction="out" type="v"/>
|
||||||
|
</method>
|
||||||
|
<method name="Set">
|
||||||
|
<arg name="interface_name" direction="in" type="s"/>
|
||||||
|
<arg name="property_name" direction="in" type="s"/>
|
||||||
|
<arg name="value" direction="in" type="v"/>
|
||||||
|
</method>
|
||||||
|
<method name="GetAll">
|
||||||
|
<arg name="interface" direction="in" type="s"/>
|
||||||
|
<arg name="properties" direction="out" type="a{sv}"/>
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
<interface name="org.mpris.MediaPlayer2">
|
||||||
|
<method name="Raise">
|
||||||
|
</method>
|
||||||
|
<method name="Quit">
|
||||||
|
</method>
|
||||||
|
<property name="CanQuit" type="b" access="read" />
|
||||||
|
<property name="CanRaise" type="b" access="read" />
|
||||||
|
<property name="HasTrackList" type="b" access="read"/>
|
||||||
|
<property name="Identity" type="s" access="read"/>
|
||||||
|
<property name="DesktopEntry" type="s" access="read"/>
|
||||||
|
<property name="SupportedUriSchemes" type="as" access="read"/>
|
||||||
|
<property name="SupportedMimeTypes" type="as" access="read"/>
|
||||||
|
</interface>
|
||||||
|
<interface name="org.mpris.MediaPlayer2.Player">
|
||||||
|
<method name="Next"/>
|
||||||
|
<method name="Previous"/>
|
||||||
|
<method name="Pause"/>
|
||||||
|
<method name="PlayPause"/>
|
||||||
|
<method name="Stop"/>
|
||||||
|
<method name="Play"/>
|
||||||
|
<method name="Seek">
|
||||||
|
<arg direction="in" name="Offset" type="x"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetPosition">
|
||||||
|
<arg direction="in" name="TrackId" type="o"/>
|
||||||
|
<arg direction="in" name="Position" type="x"/>
|
||||||
|
</method>
|
||||||
|
<method name="OpenUri">
|
||||||
|
<arg direction="in" name="Uri" type="s"/>
|
||||||
|
</method>
|
||||||
|
<signal name="Seeked">
|
||||||
|
<arg name="Position" type="x"/>
|
||||||
|
</signal>
|
||||||
|
<property name="PlaybackStatus" type="s" access="read"/>
|
||||||
|
<property name="LoopStatus" type="s" access="readwrite"/>
|
||||||
|
<property name="Rate" type="d" access="readwrite"/>
|
||||||
|
<property name="Shuffle" type="b" access="readwrite"/>
|
||||||
|
<property name="Metadata" type="a{sv}" access="read"/>
|
||||||
|
<property name="Volume" type="d" access="readwrite"/>
|
||||||
|
<property name="Position" type="x" access="read"/>
|
||||||
|
<property name="MinimumRate" type="d" access="read"/>
|
||||||
|
<property name="MaximumRate" type="d" access="read"/>
|
||||||
|
<property name="CanGoNext" type="b" access="read"/>
|
||||||
|
<property name="CanGoPrevious" type="b" access="read"/>
|
||||||
|
<property name="CanPlay" type="b" access="read"/>
|
||||||
|
<property name="CanPause" type="b" access="read"/>
|
||||||
|
<property name="CanSeek" type="b" access="read"/>
|
||||||
|
<property name="CanControl" type="b" access="read"/>
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
"""
|
"""
|
||||||
def __init__(self, window, client, settings):
|
|
||||||
super().__init__(dbus.SessionBus(), "/org/mpris/MediaPlayer2")
|
|
||||||
self._name="org.mpris.MediaPlayer2.mpdevil"
|
|
||||||
|
|
||||||
|
def __init__(self, window, client, settings):
|
||||||
# adding vars
|
# adding vars
|
||||||
self._window=window
|
self._window=window
|
||||||
self._client=client
|
self._client=client
|
||||||
self._settings=settings
|
self._settings=settings
|
||||||
self._metadata={}
|
self._metadata={}
|
||||||
self._bus=dbus.SessionBus()
|
|
||||||
|
# MPRIS property mappings
|
||||||
|
self._prop_mapping={
|
||||||
|
self._MPRIS_IFACE:
|
||||||
|
{"CanQuit": (GLib.Variant("b", False), None),
|
||||||
|
"CanRaise": (GLib.Variant("b", True), None),
|
||||||
|
"HasTrackList": (GLib.Variant("b", False), None),
|
||||||
|
"Identity": (GLib.Variant("s", "mpdevil"), None),
|
||||||
|
"DesktopEntry": (GLib.Variant("s", "mpdevil"), None),
|
||||||
|
"SupportedUriSchemes": (GLib.Variant("s", "None"), None),
|
||||||
|
"SupportedMimeTypes": (GLib.Variant("s", "None"), None)},
|
||||||
|
self._MPRIS_PLAYER_IFACE:
|
||||||
|
{"PlaybackStatus": (self._get_playback_status, None),
|
||||||
|
"LoopStatus": (self._get_loop_status, self._set_loop_status),
|
||||||
|
"Rate": (GLib.Variant("d", 1.0), None),
|
||||||
|
"Shuffle": (self._get_shuffle, self._set_shuffle),
|
||||||
|
"Metadata": (self._get_metadata, None),
|
||||||
|
"Volume": (self._get_volume, self._set_volume),
|
||||||
|
"Position": (self._get_position, None),
|
||||||
|
"MinimumRate": (GLib.Variant("d", 1.0), None),
|
||||||
|
"MaximumRate": (GLib.Variant("d", 1.0), None),
|
||||||
|
"CanGoNext": (self._get_can_next_prev, None),
|
||||||
|
"CanGoPrevious": (self._get_can_next_prev, None),
|
||||||
|
"CanPlay": (self._get_can_play_pause_seek, None),
|
||||||
|
"CanPause": (self._get_can_play_pause_seek, None),
|
||||||
|
"CanSeek": (self._get_can_play_pause_seek, None),
|
||||||
|
"CanControl": (GLib.Variant("b", True), None)},
|
||||||
|
}
|
||||||
|
|
||||||
# start
|
# start
|
||||||
self._bus_name=dbus.service.BusName(self._name, bus=self._bus, allow_replacement=True, replace_existing=True) # TODO
|
self._bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
||||||
|
Gio.bus_own_name_on_connection(self._bus, self._MPRIS_NAME, Gio.BusNameOwnerFlags.NONE, None, None)
|
||||||
|
self._node_info=Gio.DBusNodeInfo.new_for_xml(self._INTERFACES_XML)
|
||||||
|
for interface in self._node_info.interfaces:
|
||||||
|
self._bus.register_object(self._MPRIS_PATH, interface, self._handle_method_call, None, None)
|
||||||
|
|
||||||
# connect
|
# connect
|
||||||
self._client.emitter.connect("state", self._on_state_changed)
|
self._client.emitter.connect("state", self._on_state_changed)
|
||||||
@ -76,26 +181,25 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
|
|||||||
self._client.emitter.connect("connection_error", self._on_connection_error)
|
self._client.emitter.connect("connection_error", self._on_connection_error)
|
||||||
self._client.emitter.connect("reconnected", self._on_reconnected)
|
self._client.emitter.connect("reconnected", self._on_reconnected)
|
||||||
|
|
||||||
# Interfaces
|
def _handle_method_call(self, connection, sender, object_path, interface_name, method_name, parameters, invocation):
|
||||||
__prop_interface=dbus.PROPERTIES_IFACE
|
args=list(parameters.unpack())
|
||||||
__root_interface="org.mpris.MediaPlayer2"
|
result=getattr(self, method_name)(*args)
|
||||||
__root_props={
|
out_args=self._node_info.lookup_interface(interface_name).lookup_method(method_name).out_args
|
||||||
"CanQuit": (False, None),
|
if out_args == []:
|
||||||
"CanRaise": (True, None),
|
invocation.return_value(None)
|
||||||
"DesktopEntry": ("mpdevil", None),
|
else:
|
||||||
"HasTrackList": (False, None),
|
signature="("+"".join([arg.signature for arg in out_args])+")"
|
||||||
"Identity": ("mpdevil", None),
|
variant=GLib.Variant(signature, (result,))
|
||||||
"SupportedUriSchemes": (dbus.Array(signature="s"), None),
|
invocation.return_value(variant)
|
||||||
"SupportedMimeTypes": (dbus.Array(signature="s"), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
def __get_playback_status(self):
|
# setter and getter
|
||||||
|
def _get_playback_status(self):
|
||||||
if self._client.connected():
|
if self._client.connected():
|
||||||
status=self._client.wrapped_call("status")
|
status=self._client.wrapped_call("status")
|
||||||
return {"play": "Playing", "pause": "Paused", "stop": "Stopped"}[status["state"]]
|
return GLib.Variant("s", {"play": "Playing", "pause": "Paused", "stop": "Stopped"}[status["state"]])
|
||||||
return "Stopped"
|
return GLib.Variant("s", "Stopped")
|
||||||
|
|
||||||
def __set_loop_status(self, value):
|
def _set_loop_status(self, value):
|
||||||
if self._client.connected():
|
if self._client.connected():
|
||||||
if value == "Playlist":
|
if value == "Playlist":
|
||||||
self._client.wrapped_call("repeat", 1)
|
self._client.wrapped_call("repeat", 1)
|
||||||
@ -106,177 +210,142 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
|
|||||||
elif value == "None":
|
elif value == "None":
|
||||||
self._client.wrapped_call("repeat", 0)
|
self._client.wrapped_call("repeat", 0)
|
||||||
self._client.wrapped_call("single", 0)
|
self._client.wrapped_call("single", 0)
|
||||||
else:
|
|
||||||
raise dbus.exceptions.DBusException("Loop mode '{}' not supported".format(value))
|
|
||||||
return
|
|
||||||
|
|
||||||
def __get_loop_status(self):
|
def _get_loop_status(self):
|
||||||
if self._client.connected():
|
if self._client.connected():
|
||||||
status=self._client.wrapped_call("status")
|
status=self._client.wrapped_call("status")
|
||||||
if int(status["repeat"]) == 1:
|
if int(status["repeat"]) == 1:
|
||||||
if int(status.get("single", 0)) == 1:
|
if int(status.get("single", 0)) == 1:
|
||||||
return "Track"
|
return GLib.Variant("s", "Track")
|
||||||
else:
|
else:
|
||||||
return "Playlist"
|
return GLib.Variant("s", "Playlist")
|
||||||
else:
|
else:
|
||||||
return "None"
|
return GLib.Variant("s", "None")
|
||||||
return "None"
|
return GLib.Variant("s", "None")
|
||||||
|
|
||||||
def __set_shuffle(self, value):
|
def _set_shuffle(self, value):
|
||||||
if self._client.connected():
|
if self._client.connected():
|
||||||
self._client.wrapped_call("random", value)
|
if value:
|
||||||
return
|
self._client.wrapped_call("random", "1")
|
||||||
|
|
||||||
def __get_shuffle(self):
|
|
||||||
if self._client.connected():
|
|
||||||
if int(self._client.wrapped_call("status")["random"]) == 1:
|
|
||||||
return True
|
|
||||||
else:
|
else:
|
||||||
return False
|
self._client.wrapped_call("random", "0")
|
||||||
return False
|
|
||||||
|
|
||||||
def __get_metadata(self):
|
def _get_shuffle(self):
|
||||||
return dbus.Dictionary(self._metadata, signature="sv")
|
|
||||||
|
|
||||||
def __get_volume(self):
|
|
||||||
if self._client.connected():
|
if self._client.connected():
|
||||||
return float(self._client.wrapped_call("status").get("volume", 0))/100
|
if self._client.wrapped_call("status")["random"] == "1":
|
||||||
return 0.0
|
return GLib.Variant("b", True)
|
||||||
|
else:
|
||||||
|
return GLib.Variant("b", False)
|
||||||
|
return GLib.Variant("b", False)
|
||||||
|
|
||||||
def __set_volume(self, value):
|
def _get_metadata(self):
|
||||||
|
return GLib.Variant("a{sv}", self._metadata)
|
||||||
|
|
||||||
|
def _get_volume(self):
|
||||||
|
if self._client.connected():
|
||||||
|
return GLib.Variant("d", float(self._client.wrapped_call("status").get("volume", 0))/100)
|
||||||
|
return GLib.Variant("d", 0)
|
||||||
|
|
||||||
|
def _set_volume(self, value):
|
||||||
if self._client.connected():
|
if self._client.connected():
|
||||||
if value >= 0 and value <= 1:
|
if value >= 0 and value <= 1:
|
||||||
self._client.wrapped_call("setvol", int(value * 100))
|
self._client.wrapped_call("setvol", int(value * 100))
|
||||||
return
|
|
||||||
|
|
||||||
def __get_position(self):
|
def _get_position(self):
|
||||||
if self._client.connected():
|
if self._client.connected():
|
||||||
status=self._client.wrapped_call("status")
|
status=self._client.wrapped_call("status")
|
||||||
return dbus.Int64(float(status.get("elapsed", 0))*1000000)
|
return GLib.Variant("x", float(status.get("elapsed", 0))*1000000)
|
||||||
return dbus.Int64(0)
|
return GLib.Variant("x", 0)
|
||||||
|
|
||||||
def __get_can_next_prev(self):
|
def _get_can_next_prev(self):
|
||||||
if self._client.connected():
|
if self._client.connected():
|
||||||
status=self._client.wrapped_call("status")
|
status=self._client.wrapped_call("status")
|
||||||
if status["state"] == "stop":
|
if status["state"] == "stop":
|
||||||
return False
|
return GLib.Variant("b", False)
|
||||||
else:
|
else:
|
||||||
return True
|
return GLib.Variant("b", True)
|
||||||
return False
|
return GLib.Variant("b", False)
|
||||||
|
|
||||||
def __get_can_play_pause_seek(self):
|
def _get_can_play_pause_seek(self):
|
||||||
return self._client.connected()
|
return GLib.Variant("b", self._client.connected())
|
||||||
|
|
||||||
__player_interface="org.mpris.MediaPlayer2.Player"
|
# introspect methods
|
||||||
__player_props={
|
def Introspect(self):
|
||||||
"PlaybackStatus": (__get_playback_status, None),
|
return self._INTERFACES_XML
|
||||||
"LoopStatus": (__get_loop_status, __set_loop_status),
|
|
||||||
"Rate": (1.0, None),
|
|
||||||
"Shuffle": (__get_shuffle, __set_shuffle),
|
|
||||||
"Metadata": (__get_metadata, None),
|
|
||||||
"Volume": (__get_volume, __set_volume),
|
|
||||||
"Position": (__get_position, None),
|
|
||||||
"MinimumRate": (1.0, None),
|
|
||||||
"MaximumRate": (1.0, None),
|
|
||||||
"CanGoNext": (__get_can_next_prev, None),
|
|
||||||
"CanGoPrevious": (__get_can_next_prev, None),
|
|
||||||
"CanPlay": (__get_can_play_pause_seek, None),
|
|
||||||
"CanPause": (__get_can_play_pause_seek, None),
|
|
||||||
"CanSeek": (__get_can_play_pause_seek, None),
|
|
||||||
"CanControl": (True, None),
|
|
||||||
}
|
|
||||||
__prop_mapping={
|
|
||||||
__player_interface: __player_props,
|
|
||||||
__root_interface: __root_props,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Prop methods
|
# property methods
|
||||||
@dbus.service.signal(__prop_interface, signature="sa{sv}as")
|
def Get(self, interface_name, prop):
|
||||||
def PropertiesChanged(self, interface, changed_properties, invalidated_properties):
|
getter, setter=self._prop_mapping[interface_name][prop]
|
||||||
pass
|
|
||||||
|
|
||||||
@dbus.service.method(__prop_interface, in_signature="ss", out_signature="v")
|
|
||||||
def Get(self, interface, prop):
|
|
||||||
getter, setter=self.__prop_mapping[interface][prop]
|
|
||||||
if callable(getter):
|
if callable(getter):
|
||||||
return getter(self)
|
return getter()
|
||||||
return getter
|
return getter
|
||||||
|
|
||||||
@dbus.service.method(__prop_interface, in_signature="ssv", out_signature="")
|
def Set(self, interface_name, prop, value):
|
||||||
def Set(self, interface, prop, value):
|
getter, setter=self._prop_mapping[interface_name][prop]
|
||||||
getter, setter=self.__prop_mapping[interface][prop]
|
|
||||||
if setter is not None:
|
if setter is not None:
|
||||||
setter(self, value)
|
setter(value)
|
||||||
|
|
||||||
@dbus.service.method(__prop_interface, in_signature="s", out_signature="a{sv}")
|
def GetAll(self, interface_name):
|
||||||
def GetAll(self, interface):
|
|
||||||
read_props={}
|
read_props={}
|
||||||
props=self.__prop_mapping[interface]
|
try:
|
||||||
for key, (getter, setter) in props.items():
|
props=self._prop_mapping[interface_name]
|
||||||
if callable(getter):
|
for key, (getter, setter) in props.items():
|
||||||
getter=getter(self)
|
if callable(getter):
|
||||||
read_props[key]=getter
|
getter=getter()
|
||||||
|
read_props[key]=getter
|
||||||
|
except KeyError: # interface has no properties
|
||||||
|
pass
|
||||||
return read_props
|
return read_props
|
||||||
|
|
||||||
# Root methods
|
def PropertiesChanged(self, interface_name, changed_properties, invalidated_properties):
|
||||||
@dbus.service.method(__root_interface, in_signature="", out_signature="")
|
self._bus.emit_signal(
|
||||||
|
None, self._MPRIS_PATH, "org.freedesktop.DBus.Properties", "PropertiesChanged",
|
||||||
|
GLib.Variant.new_tuple(
|
||||||
|
GLib.Variant("s", interface_name),
|
||||||
|
GLib.Variant("a{sv}", changed_properties),
|
||||||
|
GLib.Variant("as", invalidated_properties)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# root methods
|
||||||
def Raise(self):
|
def Raise(self):
|
||||||
self._window.present()
|
self._window.present()
|
||||||
return
|
|
||||||
|
|
||||||
@dbus.service.method(__root_interface, in_signature="", out_signature="")
|
|
||||||
def Quit(self):
|
def Quit(self):
|
||||||
return
|
app_action_group=self._window.get_action_group("app")
|
||||||
|
quit_action=app_action_group.lookup_action("quit")
|
||||||
|
quit_action.activate()
|
||||||
|
|
||||||
# Player methods
|
# player methods
|
||||||
@dbus.service.signal(__player_interface, signature="x")
|
|
||||||
def Seeked(self, position):
|
|
||||||
return float(position)
|
|
||||||
|
|
||||||
@dbus.service.method(__player_interface, in_signature="", out_signature="")
|
|
||||||
def Next(self):
|
def Next(self):
|
||||||
self._client.wrapped_call("next")
|
self._client.wrapped_call("next")
|
||||||
return
|
|
||||||
|
|
||||||
@dbus.service.method(__player_interface, in_signature="", out_signature="")
|
|
||||||
def Previous(self):
|
def Previous(self):
|
||||||
self._client.wrapped_call("previous")
|
self._client.wrapped_call("previous")
|
||||||
return
|
|
||||||
|
|
||||||
@dbus.service.method(__player_interface, in_signature="", out_signature="")
|
|
||||||
def Pause(self):
|
def Pause(self):
|
||||||
self._client.wrapped_call("pause", 1)
|
self._client.wrapped_call("pause", 1)
|
||||||
return
|
|
||||||
|
|
||||||
@dbus.service.method(__player_interface, in_signature="", out_signature="")
|
|
||||||
def PlayPause(self):
|
def PlayPause(self):
|
||||||
status=self._client.wrapped_call("status")
|
status=self._client.wrapped_call("status")
|
||||||
if status["state"] == "play":
|
if status["state"] == "play":
|
||||||
self._client.wrapped_call("pause", 1)
|
self._client.wrapped_call("pause", 1)
|
||||||
else:
|
else:
|
||||||
self._client.wrapped_call("play")
|
self._client.wrapped_call("play")
|
||||||
return
|
|
||||||
|
|
||||||
@dbus.service.method(__player_interface, in_signature="", out_signature="")
|
|
||||||
def Stop(self):
|
def Stop(self):
|
||||||
self._client.wrapped_call("stop")
|
self._client.wrapped_call("stop")
|
||||||
return
|
|
||||||
|
|
||||||
@dbus.service.method(__player_interface, in_signature="", out_signature="")
|
|
||||||
def Play(self):
|
def Play(self):
|
||||||
self._client.wrapped_call("play")
|
self._client.wrapped_call("play")
|
||||||
return
|
|
||||||
|
|
||||||
@dbus.service.method(__player_interface, in_signature="x", out_signature="")
|
|
||||||
def Seek(self, offset):
|
def Seek(self, offset):
|
||||||
if offset > 0:
|
if offset > 0:
|
||||||
offset="+"+str(offset/1000000)
|
offset="+"+str(offset/1000000)
|
||||||
else:
|
else:
|
||||||
offset=str(offset/1000000)
|
offset=str(offset/1000000)
|
||||||
self._client.wrapped_call("seekcur", offset)
|
self._client.wrapped_call("seekcur", offset)
|
||||||
return
|
|
||||||
|
|
||||||
@dbus.service.method(__player_interface, in_signature="ox", out_signature="")
|
|
||||||
def SetPosition(self, trackid, position):
|
def SetPosition(self, trackid, position):
|
||||||
song=self._client.wrapped_call("currentsong")
|
song=self._client.wrapped_call("currentsong")
|
||||||
if str(trackid).split("/")[-1] != song["id"]:
|
if str(trackid).split("/")[-1] != song["id"]:
|
||||||
@ -284,29 +353,17 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
|
|||||||
mpd_pos=position/1000000
|
mpd_pos=position/1000000
|
||||||
if mpd_pos >= 0 and mpd_pos <= float(song["duration"]):
|
if mpd_pos >= 0 and mpd_pos <= float(song["duration"]):
|
||||||
self._client.wrapped_call("seekcur", str(mpd_pos))
|
self._client.wrapped_call("seekcur", str(mpd_pos))
|
||||||
return
|
|
||||||
|
|
||||||
@dbus.service.method(__player_interface, in_signature="s", out_signature="")
|
|
||||||
def OpenUri(self, uri):
|
def OpenUri(self, uri):
|
||||||
return
|
pass
|
||||||
|
|
||||||
# MPRIS implemented metadata tags (all: http://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata)
|
def Seeked(self, position):
|
||||||
implemented_tags={
|
self._bus.emit_signal(
|
||||||
"mpris:trackid": dbus.ObjectPath,
|
None, self._MPRIS_PATH, self._MPRIS_PLAYER_IFACE, "Seeked",
|
||||||
"mpris:length": dbus.Int64,
|
GLib.Variant.new_tuple(GLib.Variant("x", position))
|
||||||
"mpris:artUrl": str,
|
)
|
||||||
"xesam:album": str,
|
|
||||||
"xesam:albumArtist": list,
|
|
||||||
"xesam:artist": list,
|
|
||||||
"xesam:composer": list,
|
|
||||||
"xesam:contentCreated": str,
|
|
||||||
"xesam:discNumber": int,
|
|
||||||
"xesam:genre": list,
|
|
||||||
"xesam:title": str,
|
|
||||||
"xesam:trackNumber": int,
|
|
||||||
"xesam:url": str,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# other methods
|
||||||
def _update_metadata(self):
|
def _update_metadata(self):
|
||||||
"""
|
"""
|
||||||
Translate metadata returned by MPD to the MPRIS v2 syntax.
|
Translate metadata returned by MPD to the MPRIS v2 syntax.
|
||||||
@ -315,67 +372,64 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
|
|||||||
mpd_meta=self._client.wrapped_call("currentsong") # raw values needed for cover
|
mpd_meta=self._client.wrapped_call("currentsong") # raw values needed for cover
|
||||||
song=ClientHelper.song_to_list_dict(mpd_meta)
|
song=ClientHelper.song_to_list_dict(mpd_meta)
|
||||||
self._metadata={}
|
self._metadata={}
|
||||||
for tag, xesam_tag in (("album","album"),("title","title"),("track","trackNumber"),("disc","discNumber"),("date","contentCreated")):
|
for tag, xesam_tag in (("album","album"),("title","title"),("date","contentCreated")):
|
||||||
if tag in song:
|
if tag in song:
|
||||||
self._metadata["xesam:{}".format(xesam_tag)]=song[tag][0]
|
self._metadata["xesam:{}".format(xesam_tag)]=GLib.Variant("s", song[tag][0])
|
||||||
|
for tag, xesam_tag in (("track","trackNumber"),("disc","discNumber")):
|
||||||
|
if tag in song:
|
||||||
|
self._metadata["xesam:{}".format(xesam_tag)]=GLib.Variant("i", int(song[tag][0]))
|
||||||
for tag, xesam_tag in (("albumartist","albumArtist"),("artist","artist"),("composer","composer"),("genre","genre")):
|
for tag, xesam_tag in (("albumartist","albumArtist"),("artist","artist"),("composer","composer"),("genre","genre")):
|
||||||
if tag in song:
|
if tag in song:
|
||||||
self._metadata["xesam:{}".format(xesam_tag)]=song[tag]
|
self._metadata["xesam:{}".format(xesam_tag)]=GLib.Variant("as", song[tag])
|
||||||
if "id" in song:
|
if "id" in song:
|
||||||
self._metadata["mpris:trackid"]="/org/mpris/MediaPlayer2/Track/{}".format(song["id"][0])
|
self._metadata["mpris:trackid"]=GLib.Variant("o", self._MPRIS_PATH+"/Track/{}".format(song["id"][0]))
|
||||||
if "time" in song:
|
if "time" in song:
|
||||||
self._metadata["mpris:length"]=float(song["duration"][0]) * 1000000
|
self._metadata["mpris:length"]=GLib.Variant("x", float(song["duration"][0])*1000000)
|
||||||
if "file" in song:
|
if "file" in song:
|
||||||
song_file=song["file"][0]
|
song_file=song["file"][0]
|
||||||
lib_path=self._settings.get_value("paths")[self._settings.get_int("active-profile")]
|
lib_path=self._settings.get_value("paths")[self._settings.get_int("active-profile")]
|
||||||
self._metadata["xesam:url"]="file://{}".format(os.path.join(lib_path, song_file))
|
self._metadata["xesam:url"]=GLib.Variant("s", "file://{}".format(os.path.join(lib_path, song_file)))
|
||||||
cover=Cover(self._settings, mpd_meta)
|
cover=Cover(self._settings, mpd_meta)
|
||||||
if cover.path is not None:
|
if cover.path is not None:
|
||||||
self._metadata["mpris:artUrl"]="file://{}".format(cover.path)
|
self._metadata["mpris:artUrl"]=GLib.Variant("s", "file://{}".format(cover.path))
|
||||||
# Cast self._metadata to the correct type, or discard it
|
|
||||||
for key, value in self._metadata.items():
|
|
||||||
try:
|
|
||||||
self._metadata[key]=self.implemented_tags[key](value)
|
|
||||||
except:
|
|
||||||
self._metadata[key]=""
|
|
||||||
|
|
||||||
def _update_property(self, interface, prop):
|
def _update_property(self, interface_name, prop):
|
||||||
getter, setter=self.__prop_mapping[interface][prop]
|
getter, setter=self._prop_mapping[interface_name][prop]
|
||||||
if callable(getter):
|
if callable(getter):
|
||||||
value=getter(self)
|
value=getter()
|
||||||
else:
|
else:
|
||||||
value=getter
|
value=getter
|
||||||
self.PropertiesChanged(interface, {prop: value}, [])
|
self.PropertiesChanged(interface_name, {prop: value}, [])
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def _on_state_changed(self, *args):
|
def _on_state_changed(self, *args):
|
||||||
self._update_property("org.mpris.MediaPlayer2.Player", "PlaybackStatus")
|
self._update_property(self._MPRIS_PLAYER_IFACE, "PlaybackStatus")
|
||||||
self._update_property("org.mpris.MediaPlayer2.Player", "CanGoNext")
|
self._update_property(self._MPRIS_PLAYER_IFACE, "CanGoNext")
|
||||||
self._update_property("org.mpris.MediaPlayer2.Player", "CanGoPrevious")
|
self._update_property(self._MPRIS_PLAYER_IFACE, "CanGoPrevious")
|
||||||
|
|
||||||
def _on_song_changed(self, *args):
|
def _on_song_changed(self, *args):
|
||||||
self._update_metadata()
|
self._update_metadata()
|
||||||
self._update_property("org.mpris.MediaPlayer2.Player", "Metadata")
|
self._update_property(self._MPRIS_PLAYER_IFACE, "Metadata")
|
||||||
|
|
||||||
def _on_volume_changed(self, *args):
|
def _on_volume_changed(self, *args):
|
||||||
self._update_property("org.mpris.MediaPlayer2.Player", "Volume")
|
self._update_property(self._MPRIS_PLAYER_IFACE, "Volume")
|
||||||
|
|
||||||
def _on_loop_changed(self, *args):
|
def _on_loop_changed(self, *args):
|
||||||
self._update_property("org.mpris.MediaPlayer2.Player", "LoopStatus")
|
self._update_property(self._MPRIS_PLAYER_IFACE, "LoopStatus")
|
||||||
|
|
||||||
def _on_random_changed(self, *args):
|
def _on_random_changed(self, *args):
|
||||||
self._update_property("org.mpris.MediaPlayer2.Player", "Shuffle")
|
self._update_property(self._MPRIS_PLAYER_IFACE, "Shuffle")
|
||||||
|
|
||||||
def _on_reconnected(self, *args):
|
def _on_reconnected(self, *args):
|
||||||
properties=("CanPlay","CanPause","CanSeek")
|
properties=("CanPlay","CanPause","CanSeek")
|
||||||
for p in properties:
|
for p in properties:
|
||||||
self._update_property("org.mpris.MediaPlayer2.Player", p)
|
self._update_property(self._MPRIS_PLAYER_IFACE, p)
|
||||||
|
|
||||||
def _on_connection_error(self, *args):
|
def _on_connection_error(self, *args):
|
||||||
self._metadata={}
|
self._metadata={}
|
||||||
properties=("PlaybackStatus","CanGoNext","CanGoPrevious","Metadata","Volume","LoopStatus","Shuffle","CanPlay","CanPause","CanSeek")
|
properties=("PlaybackStatus","CanGoNext","CanGoPrevious","Metadata","Volume","LoopStatus","Shuffle","CanPlay","CanPause","CanSeek")
|
||||||
for p in properties:
|
for p in properties:
|
||||||
self._update_property("org.mpris.MediaPlayer2.Player", p)
|
self._update_property(self._MPRIS_PLAYER_IFACE, p)
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# MPD client wrapper #
|
# MPD client wrapper #
|
||||||
|
Loading…
Reference in New Issue
Block a user