MPRIS: Switching from python-sdbus to jeepney, no functionality.
This commit is contained in:
parent
a236370d47
commit
f23530628c
14 changed files with 301 additions and 125 deletions
|
@ -3,5 +3,6 @@ pygame~=2.6.1
|
||||||
tinytag~=2.1.0
|
tinytag~=2.1.0
|
||||||
pydub~=0.25.1
|
pydub~=0.25.1
|
||||||
wobbl_tools @ git+https://teapot.informationsanarchistik.de/Wobbl/wobbl_tools@9b7e796877781f77f6df93475750c15a0ca51dd9#egg=wobbl_tools
|
wobbl_tools @ git+https://teapot.informationsanarchistik.de/Wobbl/wobbl_tools@9b7e796877781f77f6df93475750c15a0ca51dd9#egg=wobbl_tools
|
||||||
sdbus~=0.14.0
|
setuptools~=78.1.0
|
||||||
setuptools~=68.1.2
|
Wobuzz~=0.1a3
|
||||||
|
jeepney~=0.8.0
|
|
@ -1,7 +1,6 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from PyQt6.QtCore import Qt, QTimer
|
from PyQt6.QtCore import QTimer
|
||||||
from PyQt6.QtWidgets import QDockWidget, QFileDialog
|
|
||||||
|
|
||||||
from .ui.main_window import MainWindow
|
from .ui.main_window import MainWindow
|
||||||
from .ui.popups import Popups
|
from .ui.popups import Popups
|
||||||
|
@ -76,6 +75,7 @@ class GUI:
|
||||||
def on_playstate_update(self):
|
def on_playstate_update(self):
|
||||||
self.track_control.on_playstate_update()
|
self.track_control.on_playstate_update()
|
||||||
self.track_info.update_info()
|
self.track_info.update_info()
|
||||||
|
self.app.mpris_server.on_playstate_update()
|
||||||
|
|
||||||
def update_gui(self):
|
def update_gui(self):
|
||||||
self.track_control.track_progress_slider.update_progress()
|
self.track_control.track_progress_slider.update_progress()
|
||||||
|
|
|
@ -8,6 +8,7 @@ from .utils import Utils
|
||||||
from .player import Player
|
from .player import Player
|
||||||
from .library.library import Library
|
from .library.library import Library
|
||||||
from .gui import GUI
|
from .gui import GUI
|
||||||
|
from .mpris import MPRISServer
|
||||||
|
|
||||||
|
|
||||||
class Wobuzz:
|
class Wobuzz:
|
||||||
|
@ -22,6 +23,9 @@ class Wobuzz:
|
||||||
self.library = Library(self)
|
self.library = Library(self)
|
||||||
self.player = Player(self)
|
self.player = Player(self)
|
||||||
self.gui = GUI(self)
|
self.gui = GUI(self)
|
||||||
|
self.mpris_server = MPRISServer(self)
|
||||||
|
self.gui.window.mpris_signal.connect(self.mpris_server.handle_event)
|
||||||
|
self.mpris_server.start()
|
||||||
|
|
||||||
self.late_init()
|
self.late_init()
|
||||||
|
|
||||||
|
|
4
wobuzz/mpris/__init__.py
Normal file
4
wobuzz/mpris/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from .server import MPRISServer
|
||||||
|
|
67
wobuzz/mpris/dbus_properties.py
Normal file
67
wobuzz/mpris/dbus_properties.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from jeepney import Header, MessageType, Endianness, MessageFlag, new_method_return
|
||||||
|
from jeepney.bus_messages import message_bus
|
||||||
|
from jeepney.io.blocking import open_dbus_connection
|
||||||
|
from jeepney.wrappers import new_header
|
||||||
|
|
||||||
|
from .utils import *
|
||||||
|
|
||||||
|
|
||||||
|
class DBusProperties(DBusInterface):
|
||||||
|
def get_all(self):
|
||||||
|
body = ({},)
|
||||||
|
return body
|
||||||
|
|
||||||
|
def properties_changed(self, interface: str):
|
||||||
|
body = None
|
||||||
|
|
||||||
|
if interface == MPRIS_ROOT_INTERFACE:
|
||||||
|
body = (MPRIS_ROOT_INTERFACE,) + self.server.root_interface.get_all()
|
||||||
|
|
||||||
|
elif interface == MPRIS_PLAYER_INTERFACE:
|
||||||
|
body = (MPRIS_PLAYER_INTERFACE,) + self.server.player_interface.get_all()
|
||||||
|
|
||||||
|
signature = "" if body is None else "sa{sv}"
|
||||||
|
|
||||||
|
msg = new_signal(
|
||||||
|
self.server.bus_address.with_interface("org.freedesktop.DBus"),
|
||||||
|
"PropertiesChanged",
|
||||||
|
signature,
|
||||||
|
body
|
||||||
|
)
|
||||||
|
self.server.bus.send(msg)
|
||||||
|
|
||||||
|
def Get(self, msg: Message):
|
||||||
|
interface_name = msg.body[0]
|
||||||
|
|
||||||
|
return_msg = None
|
||||||
|
|
||||||
|
if interface_name == PROPERTIES_INTERFACE:
|
||||||
|
return self.get(msg)
|
||||||
|
|
||||||
|
elif interface_name == MPRIS_ROOT_INTERFACE:
|
||||||
|
return self.server.root_interface.get(msg)
|
||||||
|
|
||||||
|
elif interface_name == MPRIS_PLAYER_INTERFACE:
|
||||||
|
return self.server.player_interface.get(msg)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return new_error(msg, *DBusErrors.invalidArgs(interface=interface_name))
|
||||||
|
|
||||||
|
def GetAll(self, msg: Message):
|
||||||
|
interface = msg.body[0]
|
||||||
|
|
||||||
|
if interface == PROPERTIES_INTERFACE:
|
||||||
|
body = self.get_all()
|
||||||
|
|
||||||
|
elif interface == MPRIS_ROOT_INTERFACE:
|
||||||
|
body = self.server.root_interface.get_all()
|
||||||
|
|
||||||
|
elif interface == MPRIS_PLAYER_INTERFACE:
|
||||||
|
body = self.server.player_interface.get_all()
|
||||||
|
|
||||||
|
else:
|
||||||
|
return new_error(msg, *DBusErrors.invalidArgs(interface=interface))
|
||||||
|
|
||||||
|
return new_method_return(msg, "a{sv}", body)
|
34
wobuzz/mpris/mpris_player.py
Normal file
34
wobuzz/mpris/mpris_player.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from jeepney import new_method_return
|
||||||
|
|
||||||
|
from .utils import *
|
||||||
|
|
||||||
|
|
||||||
|
class MPRISPlayer(DBusInterface):
|
||||||
|
def __init__(self, server, interface):
|
||||||
|
super().__init__(server, interface)
|
||||||
|
|
||||||
|
self.metadata = {
|
||||||
|
"mpris:trackid": ("o", "/org/mpris/MediaPlayer2/murx"), # random junk, no functionality
|
||||||
|
"xesam:title": ("s", "Huggenburgl")
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_all(self):
|
||||||
|
body = ({
|
||||||
|
"CanPlay": ("b", True),
|
||||||
|
"Metadata": ("a{sv}", self.metadata)
|
||||||
|
},)
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
def Play(self, msg: Message):
|
||||||
|
print("Play!")
|
||||||
|
|
||||||
|
def PlayPause(self, msg: Message):
|
||||||
|
print("Play/Pause!")
|
||||||
|
|
||||||
|
def Metadata(self, msg: Message):
|
||||||
|
body = (self.metadata,)
|
||||||
|
|
||||||
|
return new_method_return(msg, "a{sv}", body)
|
16
wobuzz/mpris/mpris_root.py
Normal file
16
wobuzz/mpris/mpris_root.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from .utils import *
|
||||||
|
|
||||||
|
|
||||||
|
class MPRISRoot(DBusInterface):
|
||||||
|
def get_all(self):
|
||||||
|
body = ({
|
||||||
|
"CanQuit": ("b", True),
|
||||||
|
"CanRaise": ("b", True)
|
||||||
|
},)
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
def Raise(self, msg):
|
||||||
|
print("Raise!")
|
68
wobuzz/mpris/server.py
Normal file
68
wobuzz/mpris/server.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
from jeepney.bus_messages import message_bus
|
||||||
|
from jeepney.io.blocking import DBusConnection, open_dbus_connection
|
||||||
|
|
||||||
|
from .utils import *
|
||||||
|
from .dbus_properties import DBusProperties
|
||||||
|
from .mpris_root import MPRISRoot
|
||||||
|
from .mpris_player import MPRISPlayer
|
||||||
|
|
||||||
|
|
||||||
|
class MPRISServer:
|
||||||
|
def __init__(self, app):
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
self.properties_interface = DBusProperties(self, PROPERTIES_INTERFACE)
|
||||||
|
self.root_interface = MPRISRoot(self, MPRIS_ROOT_INTERFACE)
|
||||||
|
self.player_interface = MPRISPlayer(self, MPRIS_PLAYER_INTERFACE)
|
||||||
|
|
||||||
|
self.bus_address = DBusAddress(OBJECT_PATH, SERVICE_NAME, PROPERTIES_INTERFACE)
|
||||||
|
self.bus: DBusConnection = None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.bus = open_dbus_connection()
|
||||||
|
|
||||||
|
reply = self.bus.send_and_get_reply(message_bus.RequestName(SERVICE_NAME))
|
||||||
|
if reply.body[0] == 1:
|
||||||
|
print("MPRIS Server connected to DBus: ", SERVICE_NAME)
|
||||||
|
|
||||||
|
Thread(target=self.run_event_loop, daemon=True).start()
|
||||||
|
|
||||||
|
def run_event_loop(self):
|
||||||
|
while True:
|
||||||
|
msg = self.bus.receive()
|
||||||
|
self.app.gui.window.mpris_signal.emit(msg) # queue message in the PyQt event loop
|
||||||
|
|
||||||
|
def handle_event(self, event): # called by app.gui.window.mpris_signal
|
||||||
|
self.handle_message(event)
|
||||||
|
|
||||||
|
def handle_message(self, msg: Message):
|
||||||
|
object_path = msg.header.fields[HeaderFields.path]
|
||||||
|
|
||||||
|
if not object_path == OBJECT_PATH: # only accept messages for "/org/mpris/MediaPlayer2"
|
||||||
|
self.bus.send_message(new_error(msg, *DBusErrors.unknownObject(object_path)))
|
||||||
|
return
|
||||||
|
|
||||||
|
interface = msg.header.fields[HeaderFields.interface]
|
||||||
|
|
||||||
|
# let the corresponding interface handle the message
|
||||||
|
if interface == PROPERTIES_INTERFACE:
|
||||||
|
self.properties_interface.handle_message(msg)
|
||||||
|
|
||||||
|
elif interface == MPRIS_ROOT_INTERFACE:
|
||||||
|
self.root_interface.handle_message(msg)
|
||||||
|
|
||||||
|
elif interface == MPRIS_PLAYER_INTERFACE:
|
||||||
|
self.player_interface.handle_message(msg)
|
||||||
|
|
||||||
|
def on_playstate_update(self):
|
||||||
|
current_playlist = self.app.player.current_playlist
|
||||||
|
|
||||||
|
if current_playlist is not None and current_playlist.current_track is not None:
|
||||||
|
current_track = current_playlist.current_track
|
||||||
|
|
||||||
|
self.player_interface.metadata["xesam:title"] = ("s", current_track.metadata.title)
|
||||||
|
|
||||||
|
self.properties_interface.properties_changed(MPRIS_PLAYER_INTERFACE)
|
96
wobuzz/mpris/utils.py
Normal file
96
wobuzz/mpris/utils.py
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from jeepney import DBusAddress, Message, MessageType, HeaderFields, new_error, new_signal
|
||||||
|
|
||||||
|
|
||||||
|
SERVICE_NAME = "org.mpris.MediaPlayer2.wobuzz"
|
||||||
|
OBJECT_PATH = "/org/mpris/MediaPlayer2"
|
||||||
|
PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties"
|
||||||
|
MPRIS_ROOT_INTERFACE = "org.mpris.MediaPlayer2"
|
||||||
|
MPRIS_PLAYER_INTERFACE = "org.mpris.MediaPlayer2.Player"
|
||||||
|
|
||||||
|
|
||||||
|
class DBusErrors:
|
||||||
|
@classmethod
|
||||||
|
def unknownMethod(cls, method: str) -> tuple[str, str, tuple[str]]:
|
||||||
|
return "org.freedesktop.DBus.Error.UnknownMethod", "s", (f"No such method '{method}'.",)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unknownProperty(cls, prop: str) -> tuple[str, str, tuple[str]]:
|
||||||
|
return "org.freedesktop.DBus.Error.InvalidArgs", "s", (f"No such property '{prop}'",)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def invalidArgs(cls, prop: str=None, interface: str=None):
|
||||||
|
if prop is None and interface is None:
|
||||||
|
return "org.freedesktop.DBus.Error.InvalidArgs"
|
||||||
|
|
||||||
|
if interface is None:
|
||||||
|
return "org.freedesktop.DBus.Error.InvalidArgs", "s", (f"No such property '{prop}'.",)
|
||||||
|
|
||||||
|
if prop is None:
|
||||||
|
return "org.freedesktop.DBus.Error.InvalidArgs", "s", (f"No such interface '{interface}'.",)
|
||||||
|
|
||||||
|
return (
|
||||||
|
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||||
|
"s",
|
||||||
|
(f"No such property '{prop}' on interface '{interface}'.",)
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unknownObject(cls, path: str):
|
||||||
|
return "org.freedesktop.DBus.Error.UnknownObject", "s", (f"No such object on path '{path}'.",)
|
||||||
|
|
||||||
|
|
||||||
|
class DBusInterface:
|
||||||
|
def __init__(self, server, interface: str):
|
||||||
|
self.server = server
|
||||||
|
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):
|
||||||
|
return_msg = None
|
||||||
|
|
||||||
|
match msg.header.message_type:
|
||||||
|
case MessageType.method_call:
|
||||||
|
method_name: str = msg.header.fields[HeaderFields.member]
|
||||||
|
|
||||||
|
if hasattr(self, method_name) and method_name[0].isupper():
|
||||||
|
method = getattr(self, msg.header.fields[HeaderFields.member])
|
||||||
|
|
||||||
|
if callable(method):
|
||||||
|
return_msg = method(msg)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return_msg = new_error(msg, *DBusErrors.unknownMethod(method_name))
|
||||||
|
|
||||||
|
else:
|
||||||
|
return_msg = new_error(msg, *DBusErrors.unknownMethod(method_name))
|
||||||
|
|
||||||
|
if return_msg is not None:
|
||||||
|
self.server.bus.send_message(return_msg)
|
||||||
|
|
||||||
|
def get(self, msg: Message):
|
||||||
|
prop_name: str = msg.body[1]
|
||||||
|
|
||||||
|
if prop_name[0].isupper() and hasattr(self, prop_name):
|
||||||
|
prop = getattr(self, prop_name)
|
||||||
|
|
||||||
|
if callable(prop):
|
||||||
|
return prop()
|
||||||
|
|
||||||
|
return new_error(msg, *DBusErrors.unknownProperty(prop_name))
|
||||||
|
|
||||||
|
else:
|
||||||
|
return new_error(msg, *DBusErrors.unknownProperty(prop_name))
|
||||||
|
|
||||||
|
def get_all(self) -> tuple[dict[str: tuple[str: any]]]:
|
||||||
|
raise NotImplementedError
|
|
@ -1,102 +0,0 @@
|
||||||
from sdbus import (
|
|
||||||
dbus_method_async,
|
|
||||||
dbus_property_async,
|
|
||||||
DbusInterfaceCommonAsync,
|
|
||||||
request_default_bus_name_async
|
|
||||||
)
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
from wobuzz.player.track import TrackMetadata
|
|
||||||
|
|
||||||
SERVICE_NAME = "org.mpris.MediaPlayer2.wobuzz"
|
|
||||||
OBJECT_PATH = "/org/mpris/MediaPlayer2"
|
|
||||||
MPRIS_ROOT_INTERFACE = "org.mpris.MediaPlayer2"
|
|
||||||
MPRIS_PLAYER_INTERFACE = "org.mpris.MediaPlayer2.Player"
|
|
||||||
|
|
||||||
|
|
||||||
class MPRISRoot(DbusInterfaceCommonAsync, interface_name=MPRIS_ROOT_INTERFACE):
|
|
||||||
@dbus_method_async()
|
|
||||||
async def Raise(self):
|
|
||||||
print("Raise, maybe?")
|
|
||||||
|
|
||||||
@dbus_property_async("s")
|
|
||||||
def Identity(self):
|
|
||||||
return "Wobuzz"
|
|
||||||
|
|
||||||
|
|
||||||
class MPRISPlayer(DbusInterfaceCommonAsync, interface_name=MPRIS_PLAYER_INTERFACE):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self._metadata = {}
|
|
||||||
|
|
||||||
@dbus_property_async("b")
|
|
||||||
def CanSeek(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
@dbus_method_async()
|
|
||||||
async def PlayPause(self):
|
|
||||||
self.app.gui.track_control.toggle_playing_signal.emit()
|
|
||||||
|
|
||||||
@dbus_method_async()
|
|
||||||
async def Next(self):
|
|
||||||
self.app.gui.track_control.next_signal.emit()
|
|
||||||
|
|
||||||
@dbus_method_async()
|
|
||||||
async def Previous(self):
|
|
||||||
self.app.gui.track_control.previous_signal.emit()
|
|
||||||
|
|
||||||
@dbus_method_async()
|
|
||||||
async def Stop(self):
|
|
||||||
self.app.gui.track_control.stop_signal.emit()
|
|
||||||
|
|
||||||
@dbus_property_async("a{sv}")
|
|
||||||
def Metadata(self):
|
|
||||||
return self._metadata
|
|
||||||
|
|
||||||
@Metadata.setter # noqa
|
|
||||||
def Metadata_setter(self, metadata: dict) -> None:
|
|
||||||
self._metadata = metadata
|
|
||||||
|
|
||||||
async def set_metadata(self, metadata: TrackMetadata):
|
|
||||||
await self.Metadata.set_async(self.to_xesam(metadata))
|
|
||||||
|
|
||||||
def to_xesam(self, metadata: "TrackMetadata") -> dict:
|
|
||||||
# cache name by filename without extension
|
|
||||||
art_path = self.app.utils.tmp_path + "/cover_cache/" + metadata.path.split("/")[-1][:-4]
|
|
||||||
|
|
||||||
xesam_metadata = {
|
|
||||||
"mpris:trackid": ("s", "kjuztuktg"), # nonsense, no functionality
|
|
||||||
"mpris:artUrl": ("s", "file://" + art_path),
|
|
||||||
"xesam:title": ("s", metadata.title),
|
|
||||||
"xesam:artist": ("as", [metadata.artist])
|
|
||||||
}
|
|
||||||
|
|
||||||
return xesam_metadata
|
|
||||||
|
|
||||||
|
|
||||||
class MPRISServer(MPRISRoot, MPRISPlayer):
|
|
||||||
def __init__(self, app):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.app = app
|
|
||||||
|
|
||||||
self.loop = None
|
|
||||||
|
|
||||||
async def setup_bus(self) -> None:
|
|
||||||
await request_default_bus_name_async(SERVICE_NAME)
|
|
||||||
self.export_to_dbus(OBJECT_PATH)
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self.loop = asyncio.new_event_loop()
|
|
||||||
self.loop.run_until_complete(self.setup_bus())
|
|
||||||
self.loop.run_forever()
|
|
||||||
|
|
||||||
def exec_async(self, function):
|
|
||||||
"""
|
|
||||||
This stupid function somehow allows us to execute an async function from the main thread.
|
|
||||||
If someone ha a better solution, please improve this. I have no idea of how asyncio works.
|
|
||||||
"""
|
|
||||||
|
|
||||||
loop = asyncio.new_event_loop()
|
|
||||||
loop.run_until_complete(function)
|
|
|
@ -8,7 +8,6 @@ import pygame.event
|
||||||
|
|
||||||
from .playlist import Playlist
|
from .playlist import Playlist
|
||||||
from .track_progress_timer import TrackProgress
|
from .track_progress_timer import TrackProgress
|
||||||
from .mpris import MPRISServer
|
|
||||||
|
|
||||||
|
|
||||||
class Player:
|
class Player:
|
||||||
|
@ -24,10 +23,6 @@ class Player:
|
||||||
self.history = Playlist(self.app, "History")
|
self.history = Playlist(self.app, "History")
|
||||||
self.current_playlist = None
|
self.current_playlist = None
|
||||||
|
|
||||||
self.mpris_server = MPRISServer(self.app)
|
|
||||||
# start mpris server in a thread (daemon = exit with main thread)
|
|
||||||
threading.Thread(target=self.mpris_server.start, daemon=True).start()
|
|
||||||
|
|
||||||
self.playing = False
|
self.playing = False
|
||||||
self.paused = False
|
self.paused = False
|
||||||
|
|
||||||
|
@ -43,7 +38,6 @@ class Player:
|
||||||
self.app.gui.on_playstate_update()
|
self.app.gui.on_playstate_update()
|
||||||
|
|
||||||
self.export_cover_art_tmp()
|
self.export_cover_art_tmp()
|
||||||
self.mpris_server.exec_async(self.mpris_server.set_metadata(self.current_playlist.current_track.metadata))
|
|
||||||
|
|
||||||
# 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()
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from PyQt6.QtCore import Qt
|
from PyQt6.QtCore import Qt, pyqtSignal
|
||||||
from PyQt6.QtGui import QIcon, QShortcut
|
from PyQt6.QtGui import QIcon, QShortcut
|
||||||
from PyQt6.QtWidgets import QMainWindow, QMenu
|
from PyQt6.QtWidgets import QMainWindow, QMenu
|
||||||
|
from jeepney import Message
|
||||||
|
|
||||||
from .track_control import TrackControl
|
from .track_control import TrackControl
|
||||||
from .settings import Settings
|
from .settings import Settings
|
||||||
from .process.process_dock import ProcessDock
|
from .process.process_dock import ProcessDock
|
||||||
|
@ -10,6 +12,8 @@ from .track_info import TrackInfo
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
|
mpris_signal = pyqtSignal(Message)
|
||||||
|
|
||||||
def __init__(self, app, gui, parent=None):
|
def __init__(self, app, gui, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,6 @@ from .track_progress_slider import TrackProgressSlider
|
||||||
|
|
||||||
|
|
||||||
class TrackControl(QToolBar):
|
class TrackControl(QToolBar):
|
||||||
toggle_playing_signal = pyqtSignal() # signals for MPRIS
|
|
||||||
next_signal = pyqtSignal()
|
|
||||||
previous_signal = pyqtSignal()
|
|
||||||
stop_signal = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self, app, parent=None):
|
def __init__(self, app, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
|
@ -52,13 +47,9 @@ class TrackControl(QToolBar):
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
self.previous_button.triggered.connect(self.previous_track)
|
self.previous_button.triggered.connect(self.previous_track)
|
||||||
self.previous_signal.connect(self.previous_track)
|
|
||||||
self.toggle_play_button.triggered.connect(self.app.player.toggle_playing)
|
self.toggle_play_button.triggered.connect(self.app.player.toggle_playing)
|
||||||
self.toggle_playing_signal.connect(self.app.player.toggle_playing)
|
|
||||||
self.stop_button.triggered.connect(self.app.player.stop)
|
self.stop_button.triggered.connect(self.app.player.stop)
|
||||||
self.stop_signal.connect(self.app.player.stop)
|
|
||||||
self.next_button.triggered.connect(self.next_track)
|
self.next_button.triggered.connect(self.next_track)
|
||||||
self.next_signal.connect(self.next_track)
|
|
||||||
|
|
||||||
def previous_track(self):
|
def previous_track(self):
|
||||||
if self.app.player.current_playlist is not None and self.app.player.current_playlist.has_tracks():
|
if self.app.player.current_playlist is not None and self.app.player.current_playlist.has_tracks():
|
||||||
|
@ -94,4 +85,3 @@ class TrackControl(QToolBar):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.toggle_play_button.setIcon(self.play_icon)
|
self.toggle_play_button.setIcon(self.play_icon)
|
||||||
|
|
||||||
|
|
|
@ -67,10 +67,10 @@ class TrackProgressSlider(QSlider):
|
||||||
|
|
||||||
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.track_control.track_progress_slider.setValue(progress)
|
self.setValue(progress)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.track_control.progress_indicator.setText(self.app.utils.format_time(0))
|
self.track_control.progress_indicator.setText(self.app.utils.format_time(0))
|
||||||
|
|
||||||
self.track_control.track_progress_slider.setValue(0)
|
self.setValue(0)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue