From e6e93f41ad6470be3811e594752e7695be8addec Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Sun, 25 Oct 2020 01:23:24 +0000 Subject: [PATCH] Switch to building an exe for Windows. --- .gitignore | 8 +++ build/build-exe-config.template.json | 77 +++++++++++++++++++++++++++ build/build-exe.bat | 10 ++++ build/generate-build-exe-config.py | 19 +++++++ build/icon.ico | Bin 0 -> 113764 bytes build/requirements-windows.txt | 2 + {install => build}/requirements.txt | 3 +- install/requirements-windows.txt | 1 - launch_standalone.py | 13 +++++ player.py | 36 +++++++------ server.py | 3 ++ 11 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 build/build-exe-config.template.json create mode 100644 build/build-exe.bat create mode 100644 build/generate-build-exe-config.py create mode 100644 build/icon.ico create mode 100644 build/requirements-windows.txt rename {install => build}/requirements.txt (68%) delete mode 100644 install/requirements-windows.txt create mode 100644 launch_standalone.py diff --git a/.gitignore b/.gitignore index e84f8e1..e27003f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,11 @@ __pycache__/ state/ + +*.egg-info/ + +build/build-exe-config.json + +*.exe + +*.pyo diff --git a/build/build-exe-config.template.json b/build/build-exe-config.template.json new file mode 100644 index 0000000..2ac7d21 --- /dev/null +++ b/build/build-exe-config.template.json @@ -0,0 +1,77 @@ +{ + "version": "auto-py-to-exe-configuration_v1", + "pyinstallerOptions": [ + { + "optionDest": "noconfirm", + "value": true + }, + { + "optionDest": "filenames", + "value": "../launch_standalone.py" + }, + { + "optionDest": "onefile", + "value": true + }, + { + "optionDest": "console", + "value": true + }, + { + "optionDest": "icon_file", + "value": "\\icon.ico" + }, + { + "optionDest": "name", + "value": "BAPSicle" + }, + { + "optionDest": "ascii", + "value": false + }, + { + "optionDest": "clean_build", + "value": true + }, + { + "optionDest": "strip", + "value": false + }, + { + "optionDest": "noupx", + "value": false + }, + { + "optionDest": "uac_admin", + "value": true + }, + { + "optionDest": "uac_uiaccess", + "value": false + }, + { + "optionDest": "win_private_assemblies", + "value": false + }, + { + "optionDest": "win_no_prefer_redirects", + "value": false + }, + { + "optionDest": "bootloader_ignore_signals", + "value": false + }, + { + "optionDest": "datas", + "value": "\\templates;templates/" + }, + { + "optionDest": "datas", + "value": "\\ui-static;ui-static/" + } + ], + "nonPyinstallerOptions": { + "increaseRecursionLimit": false, + "manualArguments": "" + } +} \ No newline at end of file diff --git a/build/build-exe.bat b/build/build-exe.bat new file mode 100644 index 0000000..a494ff4 --- /dev/null +++ b/build/build-exe.bat @@ -0,0 +1,10 @@ +cd /D "%~dp0" +pip install -r requirements.txt +pip install -r requirements-windows.txt +pip install -e ..\ + +python generate-build-exe-config.py + +auto-py-to-exe -c build-exe-config.json -o ./ + +TIMEOUT 5 \ No newline at end of file diff --git a/build/generate-build-exe-config.py b/build/generate-build-exe-config.py new file mode 100644 index 0000000..b84a501 --- /dev/null +++ b/build/generate-build-exe-config.py @@ -0,0 +1,19 @@ +import json +import os + +dir_path = os.path.dirname(os.path.realpath(__file__)) +parent_path = os.path.dirname(dir_path) + +in_file = open('build-exe-config.template.json', 'r') +config = json.loads(in_file.read()) +in_file.close() + +for option in config["pyinstallerOptions"]: + if option["optionDest"] == "icon_file": + option["value"] = dir_path + option["value"] + if option["optionDest"] == "datas": + option["value"] = parent_path + option["value"] + +out_file = open('build-exe-config.json', 'w') +out_file.write(json.dumps(config, indent=2)) +out_file.close() diff --git a/build/icon.ico b/build/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..240c4fda6c92f377c615b05c2e44ec07214ac7c6 GIT binary patch literal 113764 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 literal 0 HcmV?d00001 diff --git a/build/requirements-windows.txt b/build/requirements-windows.txt new file mode 100644 index 0000000..8af44a0 --- /dev/null +++ b/build/requirements-windows.txt @@ -0,0 +1,2 @@ +pywin32 +auto-py-to-exe \ No newline at end of file diff --git a/install/requirements.txt b/build/requirements.txt similarity index 68% rename from install/requirements.txt rename to build/requirements.txt index a6b9845..0e55fef 100644 --- a/install/requirements.txt +++ b/build/requirements.txt @@ -2,4 +2,5 @@ pygame==2.0.0.dev20 flask mutagen sounddevice -autopep8 \ No newline at end of file +autopep8 +setproctitle \ No newline at end of file diff --git a/install/requirements-windows.txt b/install/requirements-windows.txt deleted file mode 100644 index afd24f6..0000000 --- a/install/requirements-windows.txt +++ /dev/null @@ -1 +0,0 @@ -pywin32 \ No newline at end of file diff --git a/launch_standalone.py b/launch_standalone.py new file mode 100644 index 0000000..2681aff --- /dev/null +++ b/launch_standalone.py @@ -0,0 +1,13 @@ +import multiprocessing +import time + +from server import BAPSicleServer + +if __name__ == '__main__': + # On Windows calling this function is necessary. + # Causes all kinds of loops if not present. + multiprocessing.freeze_support() + server = multiprocessing.Process(target=BAPSicleServer).start() + while True: + time.sleep(1) + pass diff --git a/player.py b/player.py index d119954..53126c5 100644 --- a/player.py +++ b/player.py @@ -1,10 +1,13 @@ -import pygame +from state_manager import StateManager +from mutagen.mp3 import MP3 +from pygame import mixer import time import json -from mutagen.mp3 import MP3 import copy +import os +import setproctitle -from state_manager import StateManager +os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" class Player(): @@ -24,54 +27,54 @@ class Player(): def isInit(self): try: - pygame.mixer.music.get_busy() + mixer.music.get_busy() except: return False else: return True def isPlaying(self): - return bool(pygame.mixer.music.get_busy()) + return bool(mixer.music.get_busy()) def play(self): - pygame.mixer.music.play(0) + mixer.music.play(0) def pause(self): - pygame.mixer.music.pause() + mixer.music.pause() def unpause(self): - pygame.mixer.music.play(0, self.state.state["pos"]) + mixer.music.play(0, self.state.state["pos"]) def stop(self): - pygame.mixer.music.stop() + mixer.music.stop() def seek(self, pos): if self.isPlaying(): - pygame.mixer.music.play(0, pos) + mixer.music.play(0, pos) else: self.updateState(pos) def load(self, filename): if not self.isPlaying(): self.state.update("filename", filename) - pygame.mixer.music.load(filename) + mixer.music.load(filename) if ".mp3" in filename: song = MP3(filename) self.state.update("length", song.info.length) else: - self.state.update("length", pygame.mixer.Sound(filename).get_length()/1000) + self.state.update("length", mixer.Sound(filename).get_length()/1000) def quit(self): - pygame.mixer.quit() + mixer.quit() def output(self, name=None): self.quit() try: if name: - pygame.mixer.init(44100, -16, 1, 1024, devicename=name) + mixer.init(44100, -16, 1, 1024, devicename=name) else: - pygame.mixer.init(44100, -16, 1, 1024) + mixer.init(44100, -16, 1, 1024) except: return "FAIL:Failed to init mixer, check sound devices." else: @@ -84,7 +87,7 @@ class Player(): if (pos): self.state.update("pos", max(0, pos)) else: - self.state.update("pos", max(0, pygame.mixer.music.get_pos()/1000)) + self.state.update("pos", max(0, mixer.music.get_pos()/1000)) self.state.update("remaining", self.state.state["length"] - self.state.state["pos"]) def getDetails(self): @@ -93,6 +96,7 @@ class Player(): def __init__(self, channel, in_q, out_q): self.running = True + setproctitle.setproctitle("BAPSicle - Player " + str(channel)) self.state = StateManager("channel" + str(channel), self.__default_state) diff --git a/server.py b/server.py index e03680c..302fbb9 100644 --- a/server.py +++ b/server.py @@ -3,6 +3,9 @@ import player from flask import Flask, render_template, send_from_directory, request import json import sounddevice as sd +import setproctitle + +setproctitle.setproctitle("BAPSicle - Server") class BAPSicleServer():