Added simple queue view in player
This commit is contained in:
parent
8eb484abc2
commit
7fd54527c8
2 changed files with 73 additions and 3 deletions
|
@ -13,6 +13,7 @@ be one of:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from collections.abc import Callable
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -134,6 +135,7 @@ class Client:
|
||||||
config["config"] = default_config() | config["config"]
|
config["config"] = default_config() | config["config"]
|
||||||
|
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
|
self.is_quitting = False
|
||||||
self.set_log_level(config["config"]["log_level"])
|
self.set_log_level(config["config"]["log_level"])
|
||||||
self.sio = socketio.AsyncClient(json=jsonencoder)
|
self.sio = socketio.AsyncClient(json=jsonencoder)
|
||||||
self.loop: Optional[asyncio.AbstractEventLoop] = None
|
self.loop: Optional[asyncio.AbstractEventLoop] = None
|
||||||
|
@ -149,6 +151,10 @@ class Client:
|
||||||
self.quit_callback,
|
self.quit_callback,
|
||||||
)
|
)
|
||||||
self.register_handlers()
|
self.register_handlers()
|
||||||
|
self.queue_callbacks: list[Callable[[list[Entry]], None]] = []
|
||||||
|
|
||||||
|
def add_queue_callback(self, callback: Callable[[list[Entry]], None]) -> None:
|
||||||
|
self.queue_callbacks.append(callback)
|
||||||
|
|
||||||
def set_log_level(self, level: str) -> None:
|
def set_log_level(self, level: str) -> None:
|
||||||
match level:
|
match level:
|
||||||
|
@ -254,7 +260,9 @@ class Client:
|
||||||
:type data: dict[str, Any]
|
:type data: dict[str, Any]
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
self.state.queue = [Entry(**entry) for entry in data["queue"]]
|
self.state.queue.clear()
|
||||||
|
self.state.queue.extend([Entry(**entry) for entry in data["queue"]])
|
||||||
|
# self.state.queue = [Entry(**entry) for entry in data["queue"]]
|
||||||
self.state.waiting_room = [Entry(**entry) for entry in data["waiting_room"]]
|
self.state.waiting_room = [Entry(**entry) for entry in data["waiting_room"]]
|
||||||
self.state.recent = [Entry(**entry) for entry in data["recent"]]
|
self.state.recent = [Entry(**entry) for entry in data["recent"]]
|
||||||
|
|
||||||
|
@ -273,6 +281,8 @@ class Client:
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error("Error buffering: %s", e)
|
logger.error("Error buffering: %s", e)
|
||||||
await self.sio.emit("skip", {"uuid": entry.uuid})
|
await self.sio.emit("skip", {"uuid": entry.uuid})
|
||||||
|
for callback in self.queue_callbacks:
|
||||||
|
callback(self.state.queue)
|
||||||
|
|
||||||
async def handle_connect(self) -> None:
|
async def handle_connect(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
64
syng/gui.py
64
syng/gui.py
|
@ -11,7 +11,7 @@ from datetime import datetime
|
||||||
import os
|
import os
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import random
|
import random
|
||||||
from typing import TYPE_CHECKING, Any, Optional
|
from typing import TYPE_CHECKING, Any, Optional, cast
|
||||||
import secrets
|
import secrets
|
||||||
import string
|
import string
|
||||||
import signal
|
import signal
|
||||||
|
@ -28,7 +28,15 @@ except ImportError:
|
||||||
|
|
||||||
os.environ["QT_API"] = "pyqt6"
|
os.environ["QT_API"] = "pyqt6"
|
||||||
from qasync import QEventLoop, QApplication
|
from qasync import QEventLoop, QApplication
|
||||||
from PyQt6.QtCore import QObject, QTimer, Qt, pyqtSignal, pyqtSlot
|
from PyQt6.QtCore import (
|
||||||
|
QAbstractListModel,
|
||||||
|
QModelIndex,
|
||||||
|
QObject,
|
||||||
|
QTimer,
|
||||||
|
Qt,
|
||||||
|
pyqtSignal,
|
||||||
|
pyqtSlot,
|
||||||
|
)
|
||||||
from PyQt6.QtGui import QCloseEvent, QIcon, QPixmap
|
from PyQt6.QtGui import QCloseEvent, QIcon, QPixmap
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QCheckBox,
|
QCheckBox,
|
||||||
|
@ -40,6 +48,7 @@ from PyQt6.QtWidgets import (
|
||||||
QLabel,
|
QLabel,
|
||||||
QLayout,
|
QLayout,
|
||||||
QLineEdit,
|
QLineEdit,
|
||||||
|
QListView,
|
||||||
QMainWindow,
|
QMainWindow,
|
||||||
QMessageBox,
|
QMessageBox,
|
||||||
QPushButton,
|
QPushButton,
|
||||||
|
@ -59,6 +68,7 @@ import platformdirs
|
||||||
from . import resources # noqa
|
from . import resources # noqa
|
||||||
from .client import Client, default_config
|
from .client import Client, default_config
|
||||||
from .log import logger
|
from .log import logger
|
||||||
|
from .entry import Entry
|
||||||
|
|
||||||
from .sources import available_sources
|
from .sources import available_sources
|
||||||
from .config import (
|
from .config import (
|
||||||
|
@ -73,6 +83,28 @@ from .config import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class QueueModel(QAbstractListModel):
|
||||||
|
def __init__(self, queue: list[Entry]) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.queue = queue
|
||||||
|
|
||||||
|
def update(self, queue: list[Entry]) -> None:
|
||||||
|
self.queue = queue
|
||||||
|
self.dataChanged.emit(self.index(0, 0), self.index(self.rowCount() - 1, 0))
|
||||||
|
|
||||||
|
def data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole) -> Any:
|
||||||
|
if role == Qt.ItemDataRole.DisplayRole:
|
||||||
|
entry = self.queue[index.row()]
|
||||||
|
return f"{entry.title} - {entry.artist} [{entry.album}]\n{entry.performer}"
|
||||||
|
|
||||||
|
def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
|
||||||
|
return len(self.queue)
|
||||||
|
|
||||||
|
|
||||||
|
class QueueView(QListView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OptionFrame(QWidget):
|
class OptionFrame(QWidget):
|
||||||
def add_bool_option(self, name: str, description: str, value: bool = False) -> None:
|
def add_bool_option(self, name: str, description: str, value: bool = False) -> None:
|
||||||
label = QLabel(description, self)
|
label = QLabel(description, self)
|
||||||
|
@ -497,6 +529,8 @@ class GeneralConfig(OptionFrame):
|
||||||
class SyngGui(QMainWindow):
|
class SyngGui(QMainWindow):
|
||||||
def closeEvent(self, a0: Optional[QCloseEvent]) -> None:
|
def closeEvent(self, a0: Optional[QCloseEvent]) -> None:
|
||||||
if self.client is not None:
|
if self.client is not None:
|
||||||
|
if self.client.player is not None and self.client.player.mpv is not None:
|
||||||
|
self.client.player.mpv.terminate()
|
||||||
self.client.quit_callback()
|
self.client.quit_callback()
|
||||||
|
|
||||||
self.log_label_handler.cleanup()
|
self.log_label_handler.cleanup()
|
||||||
|
@ -529,11 +563,23 @@ class SyngGui(QMainWindow):
|
||||||
spacer_item = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
spacer_item = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
||||||
self.buttons_layout.addItem(spacer_item)
|
self.buttons_layout.addItem(spacer_item)
|
||||||
|
|
||||||
|
if os.getenv("SYNG_DEBUG", "0") == "1":
|
||||||
|
self.print_queue_button = QPushButton("Print Queue")
|
||||||
|
self.print_queue_button.clicked.connect(self.debug_print_queue)
|
||||||
|
self.buttons_layout.addWidget(self.print_queue_button)
|
||||||
|
|
||||||
self.startbutton = QPushButton("Connect")
|
self.startbutton = QPushButton("Connect")
|
||||||
|
|
||||||
self.startbutton.clicked.connect(self.start_syng_client)
|
self.startbutton.clicked.connect(self.start_syng_client)
|
||||||
self.buttons_layout.addWidget(self.startbutton)
|
self.buttons_layout.addWidget(self.startbutton)
|
||||||
|
|
||||||
|
def debug_print_queue(self) -> None:
|
||||||
|
if self.client is not None:
|
||||||
|
print([entry.title for entry in self.client.state.queue])
|
||||||
|
model = cast(Optional[QueueModel], self.queue_list_view.model())
|
||||||
|
if model is not None:
|
||||||
|
print(model.queue)
|
||||||
|
|
||||||
def toggle_advanced(self, state: bool) -> None:
|
def toggle_advanced(self, state: bool) -> None:
|
||||||
self.resetbutton.setVisible(state)
|
self.resetbutton.setVisible(state)
|
||||||
self.exportbutton.setVisible(state)
|
self.exportbutton.setVisible(state)
|
||||||
|
@ -626,6 +672,16 @@ class SyngGui(QMainWindow):
|
||||||
|
|
||||||
self.tabview.addTab(self.log_tab, "Logs")
|
self.tabview.addTab(self.log_tab, "Logs")
|
||||||
|
|
||||||
|
def add_queue_tab(self) -> None:
|
||||||
|
self.queue_tab = QWidget(parent=self.central_widget)
|
||||||
|
self.queue_layout = QVBoxLayout(self.queue_tab)
|
||||||
|
self.queue_tab.setLayout(self.queue_layout)
|
||||||
|
|
||||||
|
self.queue_list_view: QueueView = QueueView(self.queue_tab)
|
||||||
|
self.queue_layout.addWidget(self.queue_list_view)
|
||||||
|
|
||||||
|
self.tabview.addTab(self.queue_tab, "Queue")
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setWindowTitle("Syng")
|
self.setWindowTitle("Syng")
|
||||||
|
@ -654,6 +710,7 @@ class SyngGui(QMainWindow):
|
||||||
for source_name in available_sources:
|
for source_name in available_sources:
|
||||||
self.add_source_config(source_name, config["sources"][source_name])
|
self.add_source_config(source_name, config["sources"][source_name])
|
||||||
|
|
||||||
|
self.add_queue_tab()
|
||||||
self.add_log_tab()
|
self.add_log_tab()
|
||||||
|
|
||||||
self.update_qr()
|
self.update_qr()
|
||||||
|
@ -786,6 +843,9 @@ class SyngGui(QMainWindow):
|
||||||
config = self.gather_config()
|
config = self.gather_config()
|
||||||
self.client = Client(config)
|
self.client = Client(config)
|
||||||
asyncio.run_coroutine_threadsafe(self.client.start_client(config), self.loop)
|
asyncio.run_coroutine_threadsafe(self.client.start_client(config), self.loop)
|
||||||
|
model = QueueModel(self.client.state.queue)
|
||||||
|
self.queue_list_view.setModel(model)
|
||||||
|
self.client.add_queue_callback(model.update)
|
||||||
self.timer.start(500)
|
self.timer.start(500)
|
||||||
self.set_client_button_stop()
|
self.set_client_button_stop()
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Add table
Reference in a new issue