forked from Wobbl/Wobuzz
MPRIS: Got everything necessary working. (I think.)
This commit is contained in:
parent
f23530628c
commit
1f149a25a3
10 changed files with 333 additions and 58 deletions
22
wobuzz/mpris/dbus_introspectable.py
Normal file
22
wobuzz/mpris/dbus_introspectable.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from .utils import *
|
||||||
|
|
||||||
|
|
||||||
|
class DBUSIntrospectable(DBusInterface):
|
||||||
|
def __init__(self, server, interface: str):
|
||||||
|
self.server = server
|
||||||
|
self.interface = interface
|
||||||
|
|
||||||
|
file = open(server.app.utils.wobuzz_location + "/mpris/introspection.xml")
|
||||||
|
self.introspection_xml = file.read()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
def get_all(self):
|
||||||
|
body = ({},)
|
||||||
|
return body
|
||||||
|
|
||||||
|
# ======== Methods ========
|
||||||
|
|
||||||
|
def Introspect(self, msg):
|
||||||
|
return new_method_return(msg, "s", (self.introspection_xml,))
|
|
@ -1,9 +1,6 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from jeepney import Header, MessageType, Endianness, MessageFlag, new_method_return
|
from jeepney import new_signal
|
||||||
from jeepney.bus_messages import message_bus
|
|
||||||
from jeepney.io.blocking import open_dbus_connection
|
|
||||||
from jeepney.wrappers import new_header
|
|
||||||
|
|
||||||
from .utils import *
|
from .utils import *
|
||||||
|
|
||||||
|
@ -13,25 +10,31 @@ class DBusProperties(DBusInterface):
|
||||||
body = ({},)
|
body = ({},)
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def properties_changed(self, interface: str):
|
def properties_changed(self, interface: str, prop_name: str):
|
||||||
body = None
|
body = None
|
||||||
|
|
||||||
if interface == MPRIS_ROOT_INTERFACE:
|
if interface == MPRIS_ROOT_INTERFACE:
|
||||||
body = (MPRIS_ROOT_INTERFACE,) + self.server.root_interface.get_all()
|
prop = getattr(self.server.root_interface, prop_name)(None)
|
||||||
|
|
||||||
|
body = (MPRIS_ROOT_INTERFACE,) + ({prop_name: prop}, [])
|
||||||
|
|
||||||
elif interface == MPRIS_PLAYER_INTERFACE:
|
elif interface == MPRIS_PLAYER_INTERFACE:
|
||||||
body = (MPRIS_PLAYER_INTERFACE,) + self.server.player_interface.get_all()
|
prop = getattr(self.server.player_interface, prop_name)(None)
|
||||||
|
|
||||||
signature = "" if body is None else "sa{sv}"
|
body = (MPRIS_PLAYER_INTERFACE,) + ({prop_name: prop}, [])
|
||||||
|
|
||||||
|
signature = "" if body is None else "sa{sv}as"
|
||||||
|
|
||||||
msg = new_signal(
|
msg = new_signal(
|
||||||
self.server.bus_address.with_interface("org.freedesktop.DBus"),
|
self.server.bus_address.with_interface(PROPERTIES_INTERFACE),
|
||||||
"PropertiesChanged",
|
"PropertiesChanged",
|
||||||
signature,
|
signature,
|
||||||
body
|
body
|
||||||
)
|
)
|
||||||
self.server.bus.send(msg)
|
self.server.bus.send(msg)
|
||||||
|
|
||||||
|
# ======== Methods ========
|
||||||
|
|
||||||
def Get(self, msg: Message):
|
def Get(self, msg: Message):
|
||||||
interface_name = msg.body[0]
|
interface_name = msg.body[0]
|
||||||
|
|
||||||
|
|
76
wobuzz/mpris/introspection.xml
Normal file
76
wobuzz/mpris/introspection.xml
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<node>
|
||||||
|
<interface name="org.freedesktop.DBus.Introspectable">
|
||||||
|
<method name="Introspect">
|
||||||
|
<arg name="data" type="s" direction="out"/>
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
<interface name="org.freedesktop.DBus.Properties">
|
||||||
|
<method name="Get">
|
||||||
|
<arg name="interface_name" type="s" direction="in"/>
|
||||||
|
<arg name="property_name" type="s" direction="in"/>
|
||||||
|
<arg name="value" type="v" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="GetAll">
|
||||||
|
<arg name="interface_name" type="s" direction="in"/>
|
||||||
|
<arg name="properties" type="a{sv}" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<!--
|
||||||
|
<method name="Set">
|
||||||
|
<arg name="interface_name" type="s" direction="in"/>
|
||||||
|
<arg name="property_name" type="s" direction="in"/>
|
||||||
|
<arg name="value" type="v" direction="in"/>
|
||||||
|
</method>
|
||||||
|
-->
|
||||||
|
<signal name="PropertiesChanged">
|
||||||
|
<arg name="interface_name" type="s"/>
|
||||||
|
<arg name="changed_properties" type="a{sv}"/>
|
||||||
|
<arg name="invalidated_properties" type="as"/>
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
<interface name="org.mpris.MediaPlayer2">
|
||||||
|
<method name="Raise"/>
|
||||||
|
<method name="Quit"/>
|
||||||
|
<property name="CanQuit" access="read" type="b"/>
|
||||||
|
<property name="Fullscreen" access="read" type="b"/>
|
||||||
|
<property name="CanSetFullscreen" access="read" type="b"/>
|
||||||
|
<property name="CanRaise" access="read" type="b"/>
|
||||||
|
<property name="HasTrackList" access="read" type="b"/>
|
||||||
|
<property name="Identity" access="read" type="s"/>
|
||||||
|
<property name="DesktopEntry" access="read" type="s"/>
|
||||||
|
<property name="SupportedUriSchemes" access="read" type="as"/>
|
||||||
|
<property name="SupportedMimeTypes" access="read" type="as"/>
|
||||||
|
</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 name="Offset" type="x" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetPosition">
|
||||||
|
<arg name="TrackId" type="o" direction="in"/>
|
||||||
|
<arg name="Position" type="x" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="OpenUri">
|
||||||
|
<arg name="Uri" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<property name="PlaybackStatus" access="read" type="s"/>
|
||||||
|
<property name="LoopStatus" access="read" type="s"/>
|
||||||
|
<property name="Rate" access="read" type="d"/>
|
||||||
|
<property name="Shuffle" access="read" type="b"/>
|
||||||
|
<property name="Metadata" access="read" type="a{sv}"/>
|
||||||
|
<property name="Volume" access="read" type="d"/>
|
||||||
|
<property name="Position" access="read" type="x"/>
|
||||||
|
<property name="MinimumRate" access="read" type="d"/>
|
||||||
|
<property name="MaximumRate" access="read" type="d"/>
|
||||||
|
<property name="CanGoNext" access="read" type="b"/>
|
||||||
|
<property name="CanGoPrevious" access="read" type="b"/>
|
||||||
|
<property name="CanPlay" access="read" type="b"/>
|
||||||
|
<property name="CanPause" access="read" type="b"/>
|
||||||
|
<property name="CanSeek" access="read" type="b"/>
|
||||||
|
<property name="CanControl" access="read" type="b"/>
|
||||||
|
</interface>
|
||||||
|
</node>
|
|
@ -1,7 +1,5 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from jeepney import new_method_return
|
|
||||||
|
|
||||||
from .utils import *
|
from .utils import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,26 +7,116 @@ class MPRISPlayer(DBusInterface):
|
||||||
def __init__(self, server, interface):
|
def __init__(self, server, interface):
|
||||||
super().__init__(server, interface)
|
super().__init__(server, interface)
|
||||||
|
|
||||||
|
self.playback_status = "Stopped"
|
||||||
|
self.loop_status = "None"
|
||||||
|
self.shuffle = False
|
||||||
self.metadata = {
|
self.metadata = {
|
||||||
"mpris:trackid": ("o", "/org/mpris/MediaPlayer2/murx"), # random junk, no functionality
|
"mpris:trackid": ("o", "/org/bla/gubber"), # random junk, no functionality
|
||||||
|
"mpris:length": ("x", 0),
|
||||||
"xesam:title": ("s", "Huggenburgl")
|
"xesam:title": ("s", "Huggenburgl")
|
||||||
}
|
}
|
||||||
|
self.volume = 1.0
|
||||||
|
|
||||||
def get_all(self):
|
def get_all(self):
|
||||||
body = ({
|
body = ({
|
||||||
|
"PlaybackStatus": ("s", self.playback_status),
|
||||||
|
"LoopStatus": ("s", self.loop_status),
|
||||||
|
"Rate": ("d", 1.0),
|
||||||
|
"Shuffle": ("b", self.shuffle),
|
||||||
|
"Metadata": ("a{sv}", self.metadata),
|
||||||
|
"Volume": ("d", self.volume),
|
||||||
|
"Position": ("x", self.server.app.player.get_progress() * 1000), # milliseconds to microseconds
|
||||||
|
"MinimumRate": ("d", 1.0),
|
||||||
|
"MaximumRate": ("d", 1.0),
|
||||||
|
"CanGoNext": ("b", True),
|
||||||
|
"CanGoPrevious": ("b", True),
|
||||||
"CanPlay": ("b", True),
|
"CanPlay": ("b", True),
|
||||||
"Metadata": ("a{sv}", self.metadata)
|
"CanPause": ("b", True),
|
||||||
|
"CanSeek": ("b", True),
|
||||||
|
"CanControl": ("b", True)
|
||||||
},)
|
},)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def Play(self, msg: Message):
|
# ======== Methods ========
|
||||||
print("Play!")
|
|
||||||
|
def Next(self, msg: Message):
|
||||||
|
self.server.app.player.next_track()
|
||||||
|
|
||||||
|
def Previous(self, msg: Message):
|
||||||
|
self.server.app.player.previous_track()
|
||||||
|
|
||||||
|
def Pause(self, msg: Message):
|
||||||
|
return lambda: self.server.app.player.toggle_playing()
|
||||||
|
|
||||||
def PlayPause(self, msg: Message):
|
def PlayPause(self, msg: Message):
|
||||||
print("Play/Pause!")
|
return lambda: self.server.app.player.toggle_playing()
|
||||||
|
|
||||||
|
def Stop(self, msg: Message):
|
||||||
|
self.server.app.player.stop()
|
||||||
|
|
||||||
|
def Play(self, msg: Message):
|
||||||
|
return lambda: self.server.app.player.toggle_playing()
|
||||||
|
|
||||||
|
def Seek(self, msg: Message):
|
||||||
|
seek_forward = msg.body[0] // 1000 # microseconds to milliseconds
|
||||||
|
new_position = self.server.app.player.get_progress() + seek_forward
|
||||||
|
self.server.app.player.seek(new_position)
|
||||||
|
|
||||||
|
def SetPosition(self, msg: Message):
|
||||||
|
trackid = msg.body[0]
|
||||||
|
position = msg.body[1] // 1000 # microseconds to milliseconds
|
||||||
|
|
||||||
|
self.server.app.player.seek(position)
|
||||||
|
|
||||||
|
def OpenUri(self, msg: Message):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ======== Properties ========
|
||||||
|
|
||||||
|
def PlaybackStatus(self, msg: Message):
|
||||||
|
return "s", self.playback_status
|
||||||
|
|
||||||
|
def LoopStatus(self, msg: Message):
|
||||||
|
return "s", self.loop_status
|
||||||
|
|
||||||
|
def Rate(self, msg: Message):
|
||||||
|
return "d", 1.0
|
||||||
|
|
||||||
|
def Shuffle(self, msg: Message):
|
||||||
|
return "b", self.shuffle
|
||||||
|
|
||||||
def Metadata(self, msg: Message):
|
def Metadata(self, msg: Message):
|
||||||
body = (self.metadata,)
|
return "a{sv}", self.metadata
|
||||||
|
|
||||||
|
def Volume(self, msg: Message):
|
||||||
|
return "d", self.volume
|
||||||
|
|
||||||
|
def Position(self, msg: Message):
|
||||||
|
return "x", self.server.app.player.get_progress() * 1000 # milliseconds to microseconds
|
||||||
|
|
||||||
|
def MinimumRate(self, msg: Message):
|
||||||
|
return "d", 1.0
|
||||||
|
|
||||||
|
def MaximumRate(self, msg: Message):
|
||||||
|
return "d", 1.0
|
||||||
|
|
||||||
|
def CanGoNext(self, msg: Message):
|
||||||
|
return "b", True
|
||||||
|
|
||||||
|
def CanGoPrevious(self, msg: Message):
|
||||||
|
return "b", True
|
||||||
|
|
||||||
|
def CanPlay(self, msg: Message):
|
||||||
|
return "b", True
|
||||||
|
|
||||||
|
def CanPause(self, msg: Message):
|
||||||
|
return "b", True
|
||||||
|
|
||||||
|
def CanSeek(self, msg: Message):
|
||||||
|
return "b", True
|
||||||
|
|
||||||
|
def CanControl(self, msg: Message):
|
||||||
|
return "b", True
|
||||||
|
|
||||||
|
|
||||||
return new_method_return(msg, "a{sv}", body)
|
|
||||||
|
|
|
@ -7,10 +7,52 @@ class MPRISRoot(DBusInterface):
|
||||||
def get_all(self):
|
def get_all(self):
|
||||||
body = ({
|
body = ({
|
||||||
"CanQuit": ("b", True),
|
"CanQuit": ("b", True),
|
||||||
"CanRaise": ("b", True)
|
"Fullscreen": ("b", False),
|
||||||
|
"CanSetFullscreen": ("b", False),
|
||||||
|
"CanRaise": ("b", True),
|
||||||
|
"HasTrackList": ("b", False),
|
||||||
|
"Identity": ("s", "Wobuzz"),
|
||||||
|
"DesktopEntry": ("s", "wobuzz"),
|
||||||
|
"SupportedUriSchemes": ("as", ["file"]),
|
||||||
|
"SupportedMimeTypes": ("as", ["audio/mpeg"])
|
||||||
},)
|
},)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
# ======== Methods ========
|
||||||
|
|
||||||
def Raise(self, msg):
|
def Raise(self, msg):
|
||||||
print("Raise!")
|
self.server.app.gui.window.activateWindow()
|
||||||
|
self.server.app.gui.window.showMaximized()
|
||||||
|
|
||||||
|
def Quit(self, msg):
|
||||||
|
self.server.app.gui.window.close()
|
||||||
|
|
||||||
|
# ======== Properties ========
|
||||||
|
|
||||||
|
def CanQuit(self, msg: Message):
|
||||||
|
return "b", True
|
||||||
|
|
||||||
|
def Fullscreen(self, msg: Message):
|
||||||
|
return "b", False
|
||||||
|
|
||||||
|
def CanSetFullscreen(self, msg: Message):
|
||||||
|
return "b", False
|
||||||
|
|
||||||
|
def CanRaise(self, msg: Message):
|
||||||
|
return "b", True
|
||||||
|
|
||||||
|
def HasTrackList(self, msg: Message):
|
||||||
|
return "b", False
|
||||||
|
|
||||||
|
def Identity(self, msg: Message):
|
||||||
|
return "s", "Wobuzz"
|
||||||
|
|
||||||
|
def DesktopEntry(self, msg: Message):
|
||||||
|
return "s", "wobuzz"
|
||||||
|
|
||||||
|
def SupportedUriSchemes(self, msg: Message):
|
||||||
|
return "as", ["file"]
|
||||||
|
|
||||||
|
def SupportedMimeTypes(self, msg: Message):
|
||||||
|
return "as", ["audio/mpeg"]
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
from jeepney import DBusAddress
|
||||||
from jeepney.bus_messages import message_bus
|
from jeepney.bus_messages import message_bus
|
||||||
from jeepney.io.blocking import DBusConnection, open_dbus_connection
|
from jeepney.io.blocking import DBusConnection, open_dbus_connection
|
||||||
|
|
||||||
from .utils import *
|
from .utils import *
|
||||||
from .dbus_properties import DBusProperties
|
from .dbus_properties import DBusProperties
|
||||||
|
from .dbus_introspectable import DBUSIntrospectable
|
||||||
from .mpris_root import MPRISRoot
|
from .mpris_root import MPRISRoot
|
||||||
from .mpris_player import MPRISPlayer
|
from .mpris_player import MPRISPlayer
|
||||||
|
|
||||||
|
@ -15,6 +17,7 @@ class MPRISServer:
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
self.properties_interface = DBusProperties(self, PROPERTIES_INTERFACE)
|
self.properties_interface = DBusProperties(self, PROPERTIES_INTERFACE)
|
||||||
|
self.introspection_interface = DBUSIntrospectable(self, INTROSPECTION_INTERFACE)
|
||||||
self.root_interface = MPRISRoot(self, MPRIS_ROOT_INTERFACE)
|
self.root_interface = MPRISRoot(self, MPRIS_ROOT_INTERFACE)
|
||||||
self.player_interface = MPRISPlayer(self, MPRIS_PLAYER_INTERFACE)
|
self.player_interface = MPRISPlayer(self, MPRIS_PLAYER_INTERFACE)
|
||||||
|
|
||||||
|
@ -51,6 +54,9 @@ class MPRISServer:
|
||||||
if interface == PROPERTIES_INTERFACE:
|
if interface == PROPERTIES_INTERFACE:
|
||||||
self.properties_interface.handle_message(msg)
|
self.properties_interface.handle_message(msg)
|
||||||
|
|
||||||
|
elif interface == INTROSPECTION_INTERFACE:
|
||||||
|
self.introspection_interface.handle_message(msg)
|
||||||
|
|
||||||
elif interface == MPRIS_ROOT_INTERFACE:
|
elif interface == MPRIS_ROOT_INTERFACE:
|
||||||
self.root_interface.handle_message(msg)
|
self.root_interface.handle_message(msg)
|
||||||
|
|
||||||
|
@ -58,11 +64,30 @@ class MPRISServer:
|
||||||
self.player_interface.handle_message(msg)
|
self.player_interface.handle_message(msg)
|
||||||
|
|
||||||
def on_playstate_update(self):
|
def on_playstate_update(self):
|
||||||
current_playlist = self.app.player.current_playlist
|
player = self.app.player
|
||||||
|
current_playlist = player.current_playlist
|
||||||
|
|
||||||
if current_playlist is not None and current_playlist.current_track is not None:
|
if current_playlist is not None and current_playlist.current_track is not None:
|
||||||
current_track = current_playlist.current_track
|
current_track = current_playlist.current_track
|
||||||
|
|
||||||
self.player_interface.metadata["xesam:title"] = ("s", current_track.metadata.title)
|
art_path = self.app.utils.tmp_path + "/cover_cache/" + current_track.metadata.path.split("/")[-1][:-4]
|
||||||
|
|
||||||
self.properties_interface.properties_changed(MPRIS_PLAYER_INTERFACE)
|
# metadata milli to microseconds --↓
|
||||||
|
self.player_interface.metadata["mpris:length"] = ("x", current_track.duration * 1000)
|
||||||
|
self.player_interface.metadata["mpris:artUrl"] = ("s", "file://" + art_path)
|
||||||
|
self.player_interface.metadata["xesam:title"] = ("s", current_track.metadata.title)
|
||||||
|
self.properties_interface.properties_changed(MPRIS_PLAYER_INTERFACE, "Metadata")
|
||||||
|
|
||||||
|
if player.playing:
|
||||||
|
if player.paused:
|
||||||
|
playback_status = "Paused"
|
||||||
|
|
||||||
|
else:
|
||||||
|
playback_status = "Playing"
|
||||||
|
|
||||||
|
else:
|
||||||
|
playback_status = "Stopped"
|
||||||
|
|
||||||
|
self.player_interface.playback_status = playback_status
|
||||||
|
|
||||||
|
self.properties_interface.properties_changed(MPRIS_PLAYER_INTERFACE, "PlaybackStatus")
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from jeepney import DBusAddress, Message, MessageType, HeaderFields, new_error, new_signal
|
from jeepney import Message, MessageType, HeaderFields, new_error, new_method_return
|
||||||
|
|
||||||
|
|
||||||
SERVICE_NAME = "org.mpris.MediaPlayer2.wobuzz"
|
SERVICE_NAME = "org.mpris.MediaPlayer2.wobuzz"
|
||||||
OBJECT_PATH = "/org/mpris/MediaPlayer2"
|
OBJECT_PATH = "/org/mpris/MediaPlayer2"
|
||||||
PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties"
|
PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties"
|
||||||
|
INTROSPECTION_INTERFACE = "org.freedesktop.DBus.Introspectable"
|
||||||
MPRIS_ROOT_INTERFACE = "org.mpris.MediaPlayer2"
|
MPRIS_ROOT_INTERFACE = "org.mpris.MediaPlayer2"
|
||||||
MPRIS_PLAYER_INTERFACE = "org.mpris.MediaPlayer2.Player"
|
MPRIS_PLAYER_INTERFACE = "org.mpris.MediaPlayer2.Player"
|
||||||
|
|
||||||
|
@ -46,18 +46,9 @@ class DBusInterface:
|
||||||
self.server = server
|
self.server = server
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
|
|
||||||
def __setattr__(self, key: str, value) -> None:
|
|
||||||
super().__setattr__(key, value)
|
|
||||||
|
|
||||||
if not key[0].isupper() and not callable(value) and hasattr(self, key.title()):
|
|
||||||
getter = getattr(self, key.title())
|
|
||||||
|
|
||||||
if callable(getter):
|
|
||||||
if hasattr(self.server, "bus"):
|
|
||||||
self.server.properties_interface.properties_changed(self.interface)
|
|
||||||
|
|
||||||
def handle_message(self, msg: Message):
|
def handle_message(self, msg: Message):
|
||||||
return_msg = None
|
return_msg = None
|
||||||
|
post_action = None # function that gets called after return_msg was set
|
||||||
|
|
||||||
match msg.header.message_type:
|
match msg.header.message_type:
|
||||||
case MessageType.method_call:
|
case MessageType.method_call:
|
||||||
|
@ -67,7 +58,17 @@ class DBusInterface:
|
||||||
method = getattr(self, msg.header.fields[HeaderFields.member])
|
method = getattr(self, msg.header.fields[HeaderFields.member])
|
||||||
|
|
||||||
if callable(method):
|
if callable(method):
|
||||||
return_msg = method(msg)
|
method_data = method(msg)
|
||||||
|
|
||||||
|
if isinstance(method_data, tuple):
|
||||||
|
post_action, return_msg = method_data
|
||||||
|
|
||||||
|
else:
|
||||||
|
if callable(method_data):
|
||||||
|
post_action = method_data
|
||||||
|
|
||||||
|
else:
|
||||||
|
return_msg = method_data
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return_msg = new_error(msg, *DBusErrors.unknownMethod(method_name))
|
return_msg = new_error(msg, *DBusErrors.unknownMethod(method_name))
|
||||||
|
@ -75,9 +76,14 @@ class DBusInterface:
|
||||||
else:
|
else:
|
||||||
return_msg = new_error(msg, *DBusErrors.unknownMethod(method_name))
|
return_msg = new_error(msg, *DBusErrors.unknownMethod(method_name))
|
||||||
|
|
||||||
if return_msg is not None:
|
if return_msg is None:
|
||||||
|
return_msg = new_method_return(msg)
|
||||||
|
|
||||||
self.server.bus.send_message(return_msg)
|
self.server.bus.send_message(return_msg)
|
||||||
|
|
||||||
|
if not post_action is None:
|
||||||
|
post_action()
|
||||||
|
|
||||||
def get(self, msg: Message):
|
def get(self, msg: Message):
|
||||||
prop_name: str = msg.body[1]
|
prop_name: str = msg.body[1]
|
||||||
|
|
||||||
|
@ -85,7 +91,13 @@ class DBusInterface:
|
||||||
prop = getattr(self, prop_name)
|
prop = getattr(self, prop_name)
|
||||||
|
|
||||||
if callable(prop):
|
if callable(prop):
|
||||||
return prop()
|
prop_value = prop(msg)
|
||||||
|
signature = prop_value[0]
|
||||||
|
value = prop_value[1]
|
||||||
|
|
||||||
|
return_msg = new_method_return(msg, "v", (prop_value,))
|
||||||
|
|
||||||
|
return return_msg
|
||||||
|
|
||||||
return new_error(msg, *DBusErrors.unknownProperty(prop_name))
|
return new_error(msg, *DBusErrors.unknownProperty(prop_name))
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,10 @@ class Player:
|
||||||
self.playing = True
|
self.playing = True
|
||||||
self.paused = False
|
self.paused = False
|
||||||
|
|
||||||
self.app.gui.on_playstate_update()
|
|
||||||
|
|
||||||
self.export_cover_art_tmp()
|
self.export_cover_art_tmp()
|
||||||
|
|
||||||
|
self.app.gui.on_playstate_update()
|
||||||
|
|
||||||
# cache next track so it immediately starts when the current track finishes
|
# cache next track so it immediately starts when the current track finishes
|
||||||
self.cache_next_track()
|
self.cache_next_track()
|
||||||
|
|
||||||
|
@ -234,3 +234,24 @@ class Player:
|
||||||
file = open(art_path, "wb")
|
file = open(art_path, "wb")
|
||||||
file.write(metadata.images.any.data)
|
file.write(metadata.images.any.data)
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
|
def get_progress(self) -> int:
|
||||||
|
"""
|
||||||
|
Gets the progress of the current track in milliseconds.
|
||||||
|
(Also when paused.)
|
||||||
|
:return: Progress in milliseconds
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.playing:
|
||||||
|
remaining = self.track_progress.timer.remainingTime()
|
||||||
|
|
||||||
|
if remaining == -1:
|
||||||
|
remaining = self.track_progress.remaining_time
|
||||||
|
|
||||||
|
track_duration = self.current_playlist.current_track.duration
|
||||||
|
|
||||||
|
progress = track_duration - remaining
|
||||||
|
|
||||||
|
return progress
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from PyQt6.QtCore import pyqtSignal
|
|
||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon
|
||||||
from PyQt6.QtWidgets import QToolBar, QLabel
|
from PyQt6.QtWidgets import QToolBar, QLabel
|
||||||
|
|
||||||
|
|
|
@ -55,22 +55,9 @@ class TrackProgressSlider(QSlider):
|
||||||
|
|
||||||
def update_progress(self):
|
def update_progress(self):
|
||||||
if not self.dragged:
|
if not self.dragged:
|
||||||
if self.app.player.playing:
|
progress = self.app.player.get_progress()
|
||||||
remaining = self.app.player.track_progress.timer.remainingTime()
|
|
||||||
|
|
||||||
if remaining == -1:
|
|
||||||
remaining = self.app.player.track_progress.remaining_time
|
|
||||||
|
|
||||||
track_duration = self.app.player.current_playlist.current_track.duration
|
|
||||||
|
|
||||||
progress = track_duration - remaining
|
|
||||||
|
|
||||||
self.track_control.progress_indicator.setText(self.app.utils.format_time(progress))
|
self.track_control.progress_indicator.setText(self.app.utils.format_time(progress))
|
||||||
|
|
||||||
self.setValue(progress)
|
self.setValue(progress)
|
||||||
|
|
||||||
else:
|
|
||||||
self.track_control.progress_indicator.setText(self.app.utils.format_time(0))
|
|
||||||
|
|
||||||
self.setValue(0)
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue