cleaning up, preparing for windows tests
This commit is contained in:
parent
e7b895888b
commit
55bedf7aa3
8 changed files with 35 additions and 140 deletions
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
69
syng/gui.py
69
syng/gui.py
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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: ...
|
||||||
|
|
Loading…
Add table
Reference in a new issue