Merge branch 'dev' into debugging

This commit is contained in:
Matthew Stratford 2020-11-01 03:02:51 +00:00
commit 6e79af3bbd
No known key found for this signature in database
GPG key ID: 9E53C8B3F0B57395
18 changed files with 5481 additions and 37 deletions

2
.gitignore vendored
View file

@ -21,6 +21,8 @@ build/build/BAPSicle/
build/output/
build/build-exe-pyinstaller-command.sh
logs/
*.mp3

View file

@ -1,7 +1,7 @@
# BAPSicle
### a.k.a. The Next-Gen BAPS server
!["BAPSicle logo, a pink melting ice lolly."](/dev/logo.png "BAPSicle Logo")
!["BAPSicle logo, a pink melting ice lolly."](docs/images/logo.png "BAPSicle Logo")
Welcome! This is BAPS. More acurately, this is yet another attempt at a BAPS3 server.
@ -16,32 +16,48 @@ Currently there's just a batch script. Simply run ``install.bat`` as administrat
This will:
* Copy BAPSicle into ``C:\Program Files\BAPSicle``
* Install BAPSicle.exe as a Windows Service with NSSM.
* If all goes well, open [http://localhost:5000](localhost:5000) for the server UI.
* If all goes well, open [http://localhost:13500](localhost:13500) for the server UI.
### Linux
Installed service for linux is comming soon. Testing is primarily on Ubuntu 20.04. Your milage with other distros will vary.
Installed service for linux is coming soon. Testing is primarily on Ubuntu 20.04. Your milage with other distros will vary.
### MacOS
Support for MacOS will be the last to come, sorry about that.
Currently there's no installer for MacOS, so you'll have to move the ``build/output/BAPSicle.app`` you've built and make it start automatically (if you want).
Starting and stopping the server, as well as UI links, are available in the System Menu once opening the app.
!["BAPSicle in the MacOS System Menu"](docs/images/system-menu.png "System Menu")
## Developing
### Requirements
On all platforms:
* Python 3.7 (3.8 may also work, 3.9 is unlikely to.)
* Git (Obviously)
On MacOS:
* Homebrew (To install command line Platypus)
### Running
To just run the server standaline without installing, run ``python ./launch_standalone.py``.
### Building
## Building
Currently mostly Windows focused.
### Windows
To build a BAPSicle.exe, run ``build\build-exe.py``. The resulting file will appear in ``build\output``. You can then use the install instructions above to install it, or just run it standalone.
To build a ``BAPSicle.exe``, run ``build\build-exe.bat``. The resulting file will appear in ``build\output``. You can then use the install instructions above to install it, or just run it standalone.
### Linux
Coming soon...
### MacOS
To build a ``BAPSicle.app``, run ``build/build-macos.sh``. The resulting file will appear in ``build/output``.
### Other bits
Provided is a VScode debug config to let you debug live, as well as ``dev\install-githook.bat`` that will help git to clean your code up as you're committing!
Provided is a VScode debug config to let you debug live, as well as ``dev\install-githook.{bat,sh}`` that will help git to clean your code up as you're committing!

5297
build/BAPSicle.platypus Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@
},
{
"optionDest": "filenames",
"value": "\\launch_standalone.py"
"value": "/launch_standalone.py"
},
{
"optionDest": "onefile",
@ -15,11 +15,15 @@
},
{
"optionDest": "console",
"value": true
"value": false
},
{
"optionDest": "exclude-module",
"value": "tkinter"
},
{
"optionDest": "icon_file",
"value": "\\build\\icon.ico"
"value": "/build/icon.ico"
},
{
"optionDest": "name",
@ -63,15 +67,15 @@
},
{
"optionDest": "datas",
"value": "\\templates;templates/"
"value": "/templates;templates/"
},
{
"optionDest": "datas",
"value": "\\ui-static;ui-static/"
"value": "/ui-static;ui-static/"
}
],
"nonPyinstallerOptions": {
"increaseRecursionLimit": false,
"manualArguments": ""
}
}
}

View file

@ -32,12 +32,13 @@ for option in config["pyinstallerOptions"]:
cmd_str += "--" + str(option_dest) + ' "' + str(option["value"]) + '" '
command = open('build-exe-pyinstaller-command.bat', 'w')
for format in [".bat", ".sh"]:
command = open('build-exe-pyinstaller-command'+format, 'w')
if filename == "":
print("No filename data was found in json file.")
command.write("")
else:
command.write(cmd_str + ' --distpath "output/" --workpath "build/" "' + filename + '"')
if filename == "":
print("No filename data was found in json file.")
command.write("")
else:
command.write(cmd_str + ' --distpath "output/" --workpath "build/" "' + filename + '"')
command.close()
command.close()

19
build/build-macos.sh Executable file
View file

@ -0,0 +1,19 @@
#!/bin/bash
cd "$(dirname "$0")"
pip3 install -r requirements.txt
pip3 install -r requirements-macos.txt
pip3 install -e ..\
python3 ./generate-build-exe-config.py
python3 ./build-exe.py
bash ./build-exe-pyinstaller-command.sh
rm ./*.spec
brew install platypus
platypus --load-profile ./BAPSicle.platypus --overwrite ./output/BAPSicle.app

View file

@ -1,5 +1,6 @@
import json
import os
from helpers.os_environment import isWindows
dir_path = os.path.dirname(os.path.realpath(__file__))
parent_path = os.path.dirname(dir_path)
@ -11,6 +12,8 @@ in_file.close()
for option in config["pyinstallerOptions"]:
if option["optionDest"] in ["datas", "filenames", "icon_file"]:
option["value"] = os.path.abspath(parent_path + option["value"])
if not isWindows():
option["value"] = option["value"].replace(";",":")
out_file = open('build-exe-config.json', 'w')
out_file.write(json.dumps(config, indent=2))

BIN
build/icon.icns Normal file

Binary file not shown.

BIN
build/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

27
build/macos-platypus.sh Normal file
View file

@ -0,0 +1,27 @@
#!/bin/bash
if [ "$1" == "" ]
then
echo "DISABLED|BAPSicle Server"
echo "----"
if curl --output /dev/null --silent --head --fail --max-time 1 "http://localhost:13500"
then
echo "Status"
echo "Config"
echo "Logs"
echo "----"
echo "Stop Server"
else
echo "DISABLED|Status"
echo "DISABLED|Config"
echo "DISABLED|Logs"
echo "----"
echo "Start Server"
fi
exit
fi
if [ "$1" == "Stop Server" ]
then
curl "http://localhost:13500/quit"
else
./BAPSicle "$1"
fi

View file

@ -0,0 +1 @@
pyinstaller

4
dev/install-githook.sh Normal file
View file

@ -0,0 +1,4 @@
#!/bin/bash
cd "$(dirname "$0")"
cp "./pre-commit" "../.git/hooks/"

View file

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

BIN
docs/images/system-menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -28,7 +28,7 @@ nssm set %service_name% AppStopMethodConsole 5000
nssm set %service_name% AppStopMethodWindow 5000
nssm set %service_name% AppStopMethodThreads 5000
nssm set %service_name% DisplayName "BAPSicle Server"
nssm set %service_name% Description "The next gen Broadcast and Presenting Suite server! Access settings on port 5000."
nssm set %service_name% Description "The next gen Broadcast and Presenting Suite server! Access settings on port 13500."
nssm set %service_name% ObjectName LocalSystem
nssm set %service_name% Start SERVICE_AUTO_START
nssm set %service_name% Type SERVICE_INTERACTIVE_PROCESS
@ -41,4 +41,4 @@ nssm start %service_name%
timeout 4 /nobreak
explorer "http://localhost:5000/"
explorer "http://localhost:13500/"

View file

@ -1,13 +1,48 @@
import multiprocessing
import time
import sys
import webbrowser
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()
def startServer():
server = multiprocessing.Process(target=BAPSicleServer)
server.start()
while True:
time.sleep(1)
pass
time.sleep(5)
if server and server.is_alive():
pass
else:
print("Server dead. Exiting.")
sys.exit(0)
if __name__ == '__main__':
# On Windows, calling this function is necessary.
# Causes all kinds of loops if not present.
# IT HAS TO BE RIGHT HERE, AT THE TOP OF __MAIN__
# NOT INSIDE AN IF STATEMENT. RIGHT. HERE.
# If it's not here, multiprocessing just doesn't run in the package.
# Freeze support refers to being packaged with Pyinstaller.
multiprocessing.freeze_support()
if len(sys.argv) > 1:
# We got an argument! It's probably Platypus's UI.
try:
if (sys.argv[1]) == "Start Server":
print("NOTIFICATION:Welcome to BAPSicle!")
webbrowser.open("http://localhost:13500/")
startServer()
if (sys.argv[1] == "Status"):
webbrowser.open("http://localhost:13500/status")
if (sys.argv[1] == "Config"):
webbrowser.open("http://localhost:13500/config")
if (sys.argv[1] == "Logs"):
webbrowser.open("http://localhost:13500/logs")
except Exception as e:
print("ALERT:BAPSicle failed with exception:\n", e)
sys.exit(0)
else:
startServer()

View file

@ -10,16 +10,15 @@ import setproctitle
import copy
import json
import time
import sys
# Stop the Pygame Hello message.
import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
from pygame import mixer
from mutagen.mp3 import MP3
from helpers.state_manager import StateManager
from helpers.logging_manager import LoggingManager
from mutagen.mp3 import MP3
class Player():
state = None
@ -155,6 +154,11 @@ class Player():
def load(self, filename):
if not self.isPlaying:
self.unload()
# Fix any OS specific / or \'s
if os.path.sep == "/":
filename = filename.replace("\\", '/')
else:
filename = filename.replace("/", '\\')
self.state.update("filename", filename)
@ -346,6 +350,7 @@ class Player():
self.logger.log.info("Quiting player ", channel)
self.quit()
self._retMsg("EXIT")
sys.exit(0)
def showOutput(in_q, out_q):
@ -357,6 +362,8 @@ def showOutput(in_q, out_q):
if __name__ == "__main__":
if isMacOS:
multiprocessing.set_start_method("spawn", True)
in_q = multiprocessing.Queue()
out_q = multiprocessing.Queue()

View file

@ -4,6 +4,8 @@ from flask import Flask, render_template, send_from_directory, request
import json
import sounddevice as sd
import setproctitle
import logging
from helpers.os_environment import isMacOS
setproctitle.setproctitle("BAPSicle - Server")
@ -24,10 +26,15 @@ class BAPSicleServer():
app = Flask(__name__, static_url_path='')
log = logging.getLogger('werkzeug')
log.disabled = True
app.logger.disabled = True
channel_to_q = []
channel_from_q = []
channel_p = []
stopping = False
@app.errorhandler(404)
def page_not_found(e):
@ -154,6 +161,12 @@ def status(channel):
return response
@app.route("/quit")
def quit():
stopServer()
return "Shutting down..."
@app.route("/player/all/stop")
def all_stop():
for channel in channel_to_q:
@ -167,7 +180,10 @@ def send_static(path):
def startServer():
if isMacOS():
multiprocessing.set_start_method("spawn", True)
for channel in range(3):
channel_to_q.append(multiprocessing.Queue())
channel_from_q.append(multiprocessing.Queue())
channel_p.append(
@ -180,7 +196,7 @@ def startServer():
channel_p[channel].start()
# Don't use reloader, it causes Nested Processes!
app.run(host='0.0.0.0', port=5000, debug=True, use_reloader=False)
app.run(host='0.0.0.0', port=13500, debug=True, use_reloader=False)
def stopServer():
@ -188,9 +204,21 @@ def stopServer():
for q in channel_to_q:
q.put("QUIT")
for player in channel_p:
player.join()
global app
app = None
try:
player.join()
except:
pass
print("Stopped all players.")
global stopping
if stopping == False:
stopping = True
shutdown = request.environ.get('werkzeug.server.shutdown')
if shutdown is None:
print("Shutting down Server.")
else:
print("Shutting down Flask.")
shutdown()
if __name__ == "__main__":