cleaning up, preparing for windows tests

This commit is contained in:
Christoph Stahl 2024-11-18 18:15:13 +01:00
parent e7b895888b
commit 55bedf7aa3
8 changed files with 35 additions and 140 deletions

View file

@ -42,8 +42,8 @@ pymediainfo = { version = "^6.1.0", optional = true }
pyyaml = { version = "^6.0.1", optional = true } pyyaml = { version = "^6.0.1", optional = true }
alt-profanity-check = {version = "^1.4.1", optional = true} alt-profanity-check = {version = "^1.4.1", optional = true}
pyqt6 = {version="^6.7.1", optional = true} pyqt6 = {version="^6.7.1", optional = true}
mpv = "^1.0.7" mpv = {version = "^1.0.7", optional = true}
qasync = "^0.27.1" qasync = {version = "^0.27.1", optional = true}
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
types-pyyaml = "^6.0.12.12" types-pyyaml = "^6.0.12.12"

View file

@ -5,10 +5,13 @@ mkdir -p requirements
cd requirements cd requirements
# download mpv # download mpv
wget https://nightly.link/mpv-player/mpv/workflows/build/master/mpv-x86_64-windows-msvc.zip # wget https://nightly.link/mpv-player/mpv/workflows/build/master/mpv-x86_64-windows-msvc.zip
unzip mpv-x86_64-windows-msvc.zip # unzip mpv-x86_64-windows-msvc.zip
cp mpv.exe ../src # cp mpv.exe ../src
cp vulkan-1.dll ../src # cp vulkan-1.dll ../src
wget https://github.com/shinchiro/mpv-winbuild-cmake/releases/download/20241118/mpv-dev-x86_64-20241118-git-e8fd7b8.7z
7z x mpv-dev-x86_64-20241118-git-e8fd7b8.7z
cp libmpv-2.dll ../src
# download ffmpeg # download ffmpeg
wget https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z wget https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z
@ -27,7 +30,7 @@ cp ../icons/syng.ico src/
# rm -rf src/build # rm -rf src/build
# rm -rf src/dist # rm -rf src/dist
# docker run --volume "$(pwd)/src:/src/" batonogov/pyinstaller-windows:latest "pyinstaller --onefile -w -i'.\syng.ico' --add-data='.\syng\static\syng.png;.\static' --add-binary '.\mpv.exe;.' --add-binary '.\vulkan-1.dll;.' --add-binary '.\ffmpeg.exe;.' syng/main.py" # docker run --volume "$(pwd)/src:/src/" batonogov/pyinstaller-windows:latest "pyinstaller --onefile -w -i'.\syng.ico' --add-data='.\syng\static\syng.png;.\static' --add-binary '.\mpv.exe;.' --add-binary '.\vulkan-1.dll;.' --add-binary '.\ffmpeg.exe;.' syng/main.py"
docker run --volume "$(pwd)/src:/src/" batonogov/pyinstaller-windows:latest "pyinstaller -F -w -i'.\syng.ico' --add-data='.\syng.ico;.' --add-binary '.\mpv.exe;.' --add-binary '.\vulkan-1.dll;.' --add-binary '.\ffmpeg.exe;.' syng/main.py" docker run --volume "$(pwd)/src:/src/" batonogov/pyinstaller-windows:latest "pyinstaller -w -i'.\syng.ico' --add-data='.\syng.ico;.' --add-binary '.\libmpv-2.dll;.' --add-binary '.\ffmpeg.exe;.' syng/main.py"
# cd syng-2.0.1 # cd syng-2.0.1
# wine python -m poetry install -E client # wine python -m poetry install -E client

View file

@ -21,7 +21,6 @@ from logging.handlers import QueueHandler
from multiprocessing import Queue from multiprocessing import Queue
import secrets import secrets
import string import string
import tempfile
import signal import signal
from argparse import Namespace from argparse import Namespace
from dataclasses import dataclass from dataclasses import dataclass
@ -35,7 +34,6 @@ from qrcode.main import QRCode
import socketio import socketio
from socketio.exceptions import ConnectionError from socketio.exceptions import ConnectionError
import engineio import engineio
from PIL import Image
from yaml import load, Loader from yaml import load, Loader
from syng.player_libmpv import Player from syng.player_libmpv import Player

View file

@ -5,7 +5,6 @@ import logging
from logging.handlers import QueueListener from logging.handlers import QueueListener
from logging.handlers import QueueHandler from logging.handlers import QueueHandler
# from multiprocessing import Process, Queue
from queue import Queue from queue import Queue
from collections.abc import Callable from collections.abc import Callable
from datetime import datetime from datetime import datetime
@ -13,7 +12,6 @@ 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
import threading
import secrets import secrets
import string import string
import signal import signal
@ -32,7 +30,6 @@ from qasync import QEventLoop, QApplication
from PyQt6.QtCore import QTimer, Qt from PyQt6.QtCore import QTimer, Qt
from PyQt6.QtGui import QCloseEvent, QIcon, QPixmap from PyQt6.QtGui import QCloseEvent, QIcon, QPixmap
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
# QApplication,
QCheckBox, QCheckBox,
QComboBox, QComboBox,
QDateTimeEdit, QDateTimeEdit,
@ -58,7 +55,7 @@ from qrcode.main import QRCode
import platformdirs import platformdirs
from . import resources # noqa from . import resources # noqa
from .client import Client, create_async_and_start_client, default_config from .client import Client, default_config
from .log import logger from .log import logger
from .sources import available_sources from .sources import available_sources
@ -73,16 +70,6 @@ from .config import (
StrOption, StrOption,
) )
# try:
# from .server import run_server
#
# SERVER_AVAILABLE = True
# except ImportError:
# if TYPE_CHECKING:
# from .server import run_server
#
# SERVER_AVAILABLE = False
# TODO: ScrollableFrame # TODO: ScrollableFrame
class OptionFrame(QWidget): class OptionFrame(QWidget):
@ -482,14 +469,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.syng_server is not None: if self.client is not None:
# self.syng_server.kill() self.client.quit_callback()
# self.syng_server.join()
# if self.syng_client is not None:
# self.syng_client.terminate()
# self.syng_client.join(1.0)
# self.syng_client.kill()
self.destroy() self.destroy()
@ -497,10 +478,6 @@ class SyngGui(QMainWindow):
self.buttons_layout = QHBoxLayout() self.buttons_layout = QHBoxLayout()
self.central_layout.addLayout(self.buttons_layout) self.central_layout.addLayout(self.buttons_layout)
# self.startsyng_serverbutton = QPushButton("Start Local Server")
# self.startsyng_serverbutton.clicked.connect(self.start_syng_server)
# self.buttons_layout.addWidget(self.startsyng_serverbutton)
self.resetbutton = QPushButton("Set Config to Default") self.resetbutton = QPushButton("Set Config to Default")
self.exportbutton = QPushButton("Export Config") self.exportbutton = QPushButton("Export Config")
self.importbutton = QPushButton("Import Config") self.importbutton = QPushButton("Import Config")
@ -521,11 +498,7 @@ class SyngGui(QMainWindow):
self.buttons_layout.addWidget(self.show_advanced_toggle) self.buttons_layout.addWidget(self.show_advanced_toggle)
spacer_item = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) spacer_item = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
# self.notification_label = QLabel("", self)
# spacer_item2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.buttons_layout.addItem(spacer_item) self.buttons_layout.addItem(spacer_item)
# self.buttons_layout.addWidget(self.notification_label)
# self.buttons_layout.addItem(spacer_item2)
self.savebutton = QPushButton("Save") self.savebutton = QPushButton("Save")
self.savebutton.clicked.connect(self.save_config) self.savebutton.clicked.connect(self.save_config)
@ -632,8 +605,6 @@ class SyngGui(QMainWindow):
self.setWindowIcon(QIcon(":/icons/syng.ico")) self.setWindowIcon(QIcon(":/icons/syng.ico"))
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
# self.syng_server: Optional[threading.Thread] = None
# self.syng_client: Optional[threading.Thread] = None
self.client: Optional[Client] = None self.client: Optional[Client] = None
self.syng_client_logging_listener: Optional[QueueListener] = None self.syng_client_logging_listener: Optional[QueueListener] = None
@ -658,7 +629,6 @@ class SyngGui(QMainWindow):
self.setCentralWidget(self.central_widget) self.setCentralWidget(self.central_widget)
# check every 500 ms if client is running
self.timer = QTimer() self.timer = QTimer()
self.timer.timeout.connect(self.check_if_client_is_running) self.timer.timeout.connect(self.check_if_client_is_running)
@ -781,48 +751,16 @@ class SyngGui(QMainWindow):
) )
self.syng_client_logging_listener.start() self.syng_client_logging_listener.start()
# self.syng_client = multiprocessing.Process(
# target=create_async_and_start_client, args=[config, queue]
# )
logger.addHandler(QueueHandler(queue)) logger.addHandler(QueueHandler(queue))
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)
# self.syng_client = threading.Thread(
# target=create_async_and_start_client, args=[config, queue, self.client]
# )
# self.syng_client.start()
self.notification_label.setText("") self.notification_label.setText("")
self.timer.start(500) self.timer.start(500)
self.set_client_button_stop() self.set_client_button_stop()
else: else:
self.client.quit_callback() self.client.quit_callback()
# self.syng_client.join(1.0)
self.set_client_button_start() self.set_client_button_start()
# def start_syng_server(self) -> None:
# if self.syng_server is None:
# root_path = os.path.join(os.path.dirname(__file__), "static")
# self.syng_server = multiprocessing.Process(
# target=run_server,
# args=[
# Namespace(
# host="0.0.0.0",
# port=8080,
# registration_keyfile=None,
# root_folder=root_path,
# private=False,
# restricted=False,
# )
# ],
# )
# self.syng_server.start()
# self.startsyng_serverbutton.setText("Stop Local Server")
# else:
# self.syng_server.terminate()
# self.syng_server.join()
# self.syng_server = None
# self.startsyng_serverbutton.setText("Start Local Server")
def change_qr(self, data: str) -> None: def change_qr(self, data: str) -> None:
qr = QRCode(box_size=10, border=2) qr = QRCode(box_size=10, border=2)
qr.add_data(data) qr.add_data(data)
@ -876,7 +814,6 @@ def run_gui() -> None:
app.setDesktopFileName("rocks.syng.Syng") app.setDesktopFileName("rocks.syng.Syng")
window = SyngGui() window = SyngGui()
window.show() window.show()
# app.exec()
with event_loop: with event_loop:
event_loop.run_forever() event_loop.run_forever()

View file

@ -477,8 +477,8 @@ class Server:
"source": data["source"], "source": data["source"],
"performer": data["performer"], "performer": data["performer"],
"ident": data["ident"], "ident": data["ident"],
"artist": data["artist"], "artist": data.get("artist", None),
"title": data["title"], "title": data.get("title", None),
}, },
"old_entry": { "old_entry": {
"artist": old_entry.artist, "artist": old_entry.artist,
@ -493,7 +493,10 @@ class Server:
source_obj = state.client.sources[data["source"]] source_obj = state.client.sources[data["source"]]
entry = await source_obj.get_entry( entry = await source_obj.get_entry(
data["performer"], data["ident"], artist=data["artist"], title=data["title"] data["performer"],
data["ident"],
artist=data.get("artist", None),
title=data.get("title", None),
) )
if entry is None: if entry is None:

View file

@ -127,40 +127,11 @@ class Source(ABC):
""" """
self.downloaded_files: defaultdict[str, DLFilesEntry] = defaultdict(DLFilesEntry) self.downloaded_files: defaultdict[str, DLFilesEntry] = defaultdict(DLFilesEntry)
self._masterlock: asyncio.Lock = asyncio.Lock() self._masterlock: asyncio.Lock = asyncio.Lock()
# self.player: Optional[asyncio.subprocess.Process] = None
self._index: list[str] = config["index"] if "index" in config else [] self._index: list[str] = config["index"] if "index" in config else []
self.extra_mpv_arguments: list[str] = [] self.extra_mpv_arguments: list[str] = []
self.extra_mpv_options: dict[str, str] = {} self.extra_mpv_options: dict[str, str] = {}
self._skip_next = False self._skip_next = False
@staticmethod
async def play_mpv(
video: str, audio: Optional[str], /, *options: str
) -> asyncio.subprocess.Process:
"""
Create a mpv process to play a song in full screen.
:param video: Location of the video part.
:type video: str
:param audio: Location of the audio part, if it exists.
:type audio: Optional[str]
:param options: Extra arguments forwarded to the mpv player
:type options: str
:returns: An async reference to the process
:rtype: asyncio.subprocess.Process
"""
args = ["--fullscreen", *options, video] + ([f"--audio-file={audio}"] if audio else [])
# print(f"File is {video=} and {audio=}")
mpv_process = asyncio.create_subprocess_exec(
"mpv",
*args,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
return await mpv_process
async def get_entry( async def get_entry(
self, self,
performer: str, performer: str,
@ -299,22 +270,10 @@ class Source(ABC):
entry.skip = True entry.skip = True
return return
extra_options = (
(self.extra_mpv_arguments + [mpv_options])
if mpv_options
else self.extra_mpv_arguments
)
# self.player = await self.play_mpv(
# self.downloaded_files[entry.ident].video,
# self.downloaded_files[entry.ident].audio,
# *extra_options,
# )
await player.play( await player.play(
self.downloaded_files[entry.ident].video, self.downloaded_files[entry.ident].audio self.downloaded_files[entry.ident].video, self.downloaded_files[entry.ident].audio
) )
# await self.player.wait()
# self.player = None
if self._skip_next: if self._skip_next:
self._skip_next = False self._skip_next = False
entry.skip = True entry.skip = True
@ -339,9 +298,6 @@ class Source(ABC):
buffer_task.cancel() buffer_task.cancel()
self.downloaded_files[entry.ident].ready.set() self.downloaded_files[entry.ident].ready.set()
# if self.player is not None:
# self.player.kill()
async def ensure_playable(self, entry: Entry) -> tuple[str, Optional[str]]: async def ensure_playable(self, entry: Entry) -> tuple[str, Optional[str]]:
""" """
Guaranties that the given entry can be played. Guaranties that the given entry can be played.

View file

@ -103,12 +103,6 @@ class YouTube:
:type search_result: dict[str, Any] :type search_result: dict[str, Any]
""" """
url = search_result["url"] url = search_result["url"]
# cls.__cache__[url] = {
# "duration": int(search_result["duration"]),
# "title": search_result["title"],
# "channel": search_result["channel"],
# "url": url,
# }
return cls(url, info=search_result) return cls(url, info=search_result)
@ -255,15 +249,6 @@ class YoutubeSource(Source):
:rtype: None :rtype: None
""" """
if self.start_streaming and not self.downloaded_files[entry.ident].complete: if self.start_streaming and not self.downloaded_files[entry.ident].complete:
# self.player = await self.play_mpv(
# entry.ident,
# None,
# "--script-opts=ytdl_hook-ytdl_path=yt-dlp,ytdl_hook-exclude='%.pls$'",
# f"--ytdl-format={self.formatstring}",
# "--fullscreen",
# mpv_options,
# )
# await self.player.wait()
await player.play(entry.ident) await player.play(entry.ident)
else: else:
await super().play(entry, player, mpv_options) await super().play(entry, player, mpv_options)

View file

@ -1,4 +1,17 @@
from asyncio import AbstractEventLoop from types import TracebackType
from typing import Optional
import PyQt6.QtWidgets
from asyncio import BaseEventLoop
class QApplication: class QApplication(PyQt6.QtWidgets.QApplication):
def __init__(self, argv: list[str]) -> None: ... def __init__(self, argv: list[str]) -> None: ...
class QEventLoop(BaseEventLoop):
def __init__(self, app: QApplication) -> None: ...
def __enter__(self) -> None: ...
def __exit__(
self,
exc_type: Optional[type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None: ...