Communication between GUI and client back to multiprocessing, since Popen yielded 100% CPU :/
This commit is contained in:
parent
1f1c2c4f1e
commit
5468b39bc1
3 changed files with 52 additions and 24 deletions
|
@ -15,6 +15,8 @@ be one of:
|
|||
import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
from logging.handlers import QueueHandler
|
||||
from multiprocessing import Queue
|
||||
import secrets
|
||||
import string
|
||||
import tempfile
|
||||
|
@ -192,7 +194,7 @@ async def handle_connect() -> None:
|
|||
|
||||
:rtype: None
|
||||
"""
|
||||
logging.info("Connected to server")
|
||||
logger.info("Connected to server")
|
||||
data = {
|
||||
"queue": state.queue,
|
||||
"waiting_room": state.waiting_room,
|
||||
|
@ -309,6 +311,7 @@ async def handle_search(data: dict[str, Any]) -> None:
|
|||
:type data: dict[str, Any]
|
||||
:rtype: None
|
||||
"""
|
||||
logger.info(f"Searching for: {data['query']}")
|
||||
query = data["query"]
|
||||
sid = data["sid"]
|
||||
results_list = await asyncio.gather(*[source.search(query) for source in sources.values()])
|
||||
|
@ -343,7 +346,7 @@ async def handle_client_registered(data: dict[str, Any]) -> None:
|
|||
:rtype: None
|
||||
"""
|
||||
if data["success"]:
|
||||
logging.info("Registered")
|
||||
logger.info("Registered")
|
||||
print(f"Join here: {state.config['server']}/{data['room']}")
|
||||
qr = QRCode(box_size=20, border=2)
|
||||
qr.add_data(f"{state.config['server']}/{data['room']}")
|
||||
|
@ -354,7 +357,7 @@ async def handle_client_registered(data: dict[str, Any]) -> None:
|
|||
if state.current_source is None: # A possible race condition can occur here
|
||||
await sio.emit("get-first")
|
||||
else:
|
||||
logging.warning("Registration failed")
|
||||
logger.warning("Registration failed")
|
||||
await sio.disconnect()
|
||||
|
||||
|
||||
|
@ -451,14 +454,24 @@ async def start_client(config: dict[str, Any]) -> None:
|
|||
state.current_source.player.kill()
|
||||
|
||||
|
||||
def create_async_and_start_client(config: dict[str, Any]) -> None:
|
||||
def create_async_and_start_client(
|
||||
config: dict[str, Any], queue: Optional[Queue[logging.LogRecord]] = None
|
||||
) -> None:
|
||||
"""
|
||||
Create an asyncio event loop and start the client.
|
||||
|
||||
If a multiprocessing queue is given, the client will log to the queue.
|
||||
|
||||
:param config: Config options for the client
|
||||
:type config: dict[str, Any]
|
||||
:param queue: A multiprocessing queue to log to
|
||||
:type queue: Optional[Queue[LogRecord]]
|
||||
:rtype: None
|
||||
"""
|
||||
|
||||
if queue is not None:
|
||||
logger.addHandler(QueueHandler(queue))
|
||||
|
||||
asyncio.run(start_client(config))
|
||||
|
||||
|
||||
|
|
53
syng/gui.py
53
syng/gui.py
|
@ -1,6 +1,8 @@
|
|||
from argparse import Namespace
|
||||
from io import BytesIO
|
||||
from multiprocessing import Process
|
||||
import logging
|
||||
from logging.handlers import QueueListener
|
||||
from multiprocessing import Process, Queue
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
@ -12,7 +14,6 @@ import webbrowser
|
|||
import multiprocessing
|
||||
import secrets
|
||||
import string
|
||||
import subprocess
|
||||
import signal
|
||||
|
||||
from PyQt6.QtCore import QTimer
|
||||
|
@ -38,7 +39,7 @@ from yaml import dump, load, Loader, Dumper
|
|||
from qrcode.main import QRCode
|
||||
import platformdirs
|
||||
|
||||
from .client import default_config
|
||||
from .client import create_async_and_start_client, default_config
|
||||
|
||||
from .sources import available_sources
|
||||
|
||||
|
@ -286,7 +287,7 @@ class SyngGui(QMainWindow):
|
|||
|
||||
if self.syng_client is not None:
|
||||
self.syng_client.terminate()
|
||||
self.syng_client.wait(1.0)
|
||||
self.syng_client.join(1.0)
|
||||
self.syng_client.kill()
|
||||
|
||||
self.destroy()
|
||||
|
@ -367,7 +368,9 @@ class SyngGui(QMainWindow):
|
|||
self.setWindowIcon(self.qt_icon)
|
||||
|
||||
self.syng_server: Optional[Process] = None
|
||||
self.syng_client: Optional[subprocess.Popen[bytes]] = None
|
||||
# self.syng_client: Optional[subprocess.Popen[bytes]] = None
|
||||
self.syng_client: Optional[Process] = None
|
||||
self.syng_client_logging_listener: Optional[QueueListener] = None
|
||||
|
||||
self.configfile = os.path.join(platformdirs.user_config_dir("syng"), "config.yaml")
|
||||
|
||||
|
@ -416,7 +419,7 @@ class SyngGui(QMainWindow):
|
|||
|
||||
self.setCentralWidget(self.central_widget)
|
||||
|
||||
# check every 100 ms if client is running
|
||||
# check every 500 ms if client is running
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(self.check_if_client_is_running)
|
||||
|
||||
|
@ -440,15 +443,7 @@ class SyngGui(QMainWindow):
|
|||
self.timer.stop()
|
||||
return
|
||||
|
||||
ret = self.syng_client.poll()
|
||||
if ret is not None:
|
||||
_, stderr = self.syng_client.communicate()
|
||||
stderr_lines = stderr.decode("utf-8").strip().split("\n")
|
||||
if stderr_lines and stderr_lines[-1].startswith("Warning"):
|
||||
self.notification_label.setText(stderr_lines[-1])
|
||||
else:
|
||||
self.notification_label.setText("")
|
||||
self.syng_client.wait()
|
||||
if not self.syng_client.is_alive():
|
||||
self.syng_client = None
|
||||
self.set_client_button_start()
|
||||
else:
|
||||
|
@ -461,15 +456,26 @@ class SyngGui(QMainWindow):
|
|||
self.startbutton.setText("Save and Start")
|
||||
|
||||
def start_syng_client(self) -> None:
|
||||
if self.syng_client is None or self.syng_client.poll() is not None:
|
||||
if self.syng_client is None or not self.syng_client.is_alive():
|
||||
self.save_config()
|
||||
self.syng_client = subprocess.Popen(["syng", "client"], stderr=subprocess.PIPE)
|
||||
config = self.gather_config()
|
||||
queue: Queue[logging.LogRecord] = multiprocessing.Queue()
|
||||
|
||||
self.syng_client_logging_listener = QueueListener(
|
||||
queue, LoggingLabelHandler(self.notification_label)
|
||||
)
|
||||
self.syng_client_logging_listener.start()
|
||||
|
||||
self.syng_client = multiprocessing.Process(
|
||||
target=create_async_and_start_client, args=[config, queue]
|
||||
)
|
||||
self.syng_client.start()
|
||||
self.notification_label.setText("")
|
||||
self.timer.start()
|
||||
self.timer.start(500)
|
||||
self.set_client_button_stop()
|
||||
else:
|
||||
self.syng_client.terminate()
|
||||
self.syng_client.wait(1.0)
|
||||
self.syng_client.join(1.0)
|
||||
self.syng_client.kill()
|
||||
self.set_client_button_start()
|
||||
|
||||
|
@ -527,6 +533,15 @@ class SyngGui(QMainWindow):
|
|||
self.change_qr(syng_server + room)
|
||||
|
||||
|
||||
class LoggingLabelHandler(logging.Handler):
|
||||
def __init__(self, label: QLabel):
|
||||
super().__init__()
|
||||
self.label = label
|
||||
|
||||
def emit(self, record: logging.LogRecord) -> None:
|
||||
self.label.setText(self.format(record))
|
||||
|
||||
|
||||
def run_gui() -> None:
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
|
|
|
@ -352,7 +352,7 @@ class YoutubeSource(Source):
|
|||
title=result.title,
|
||||
artist=result.author,
|
||||
album="YouTube",
|
||||
duration=result.length,
|
||||
duration=str(result.length),
|
||||
)
|
||||
for result in results
|
||||
]
|
||||
|
|
Loading…
Add table
Reference in a new issue