Merge branch 'dev' into debugging
This commit is contained in:
commit
6e79af3bbd
18 changed files with 5481 additions and 37 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -21,6 +21,8 @@ build/build/BAPSicle/
|
||||||
|
|
||||||
build/output/
|
build/output/
|
||||||
|
|
||||||
|
build/build-exe-pyinstaller-command.sh
|
||||||
|
|
||||||
logs/
|
logs/
|
||||||
|
|
||||||
*.mp3
|
*.mp3
|
||||||
|
|
32
README.md
32
README.md
|
@ -1,7 +1,7 @@
|
||||||
# BAPSicle
|
# BAPSicle
|
||||||
### a.k.a. The Next-Gen BAPS server
|
### 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.
|
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:
|
This will:
|
||||||
* Copy BAPSicle into ``C:\Program Files\BAPSicle``
|
* Copy BAPSicle into ``C:\Program Files\BAPSicle``
|
||||||
* Install BAPSicle.exe as a Windows Service with NSSM.
|
* 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
|
### 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
|
### 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
|
## Developing
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
|
On all platforms:
|
||||||
* Python 3.7 (3.8 may also work, 3.9 is unlikely to.)
|
* Python 3.7 (3.8 may also work, 3.9 is unlikely to.)
|
||||||
* Git (Obviously)
|
* Git (Obviously)
|
||||||
|
|
||||||
|
On MacOS:
|
||||||
|
* Homebrew (To install command line Platypus)
|
||||||
|
|
||||||
### Running
|
### Running
|
||||||
To just run the server standaline without installing, run ``python ./launch_standalone.py``.
|
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
|
### 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
5297
build/BAPSicle.platypus
Normal file
File diff suppressed because it is too large
Load diff
|
@ -7,7 +7,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"optionDest": "filenames",
|
"optionDest": "filenames",
|
||||||
"value": "\\launch_standalone.py"
|
"value": "/launch_standalone.py"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"optionDest": "onefile",
|
"optionDest": "onefile",
|
||||||
|
@ -15,11 +15,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"optionDest": "console",
|
"optionDest": "console",
|
||||||
"value": true
|
"value": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"optionDest": "exclude-module",
|
||||||
|
"value": "tkinter"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"optionDest": "icon_file",
|
"optionDest": "icon_file",
|
||||||
"value": "\\build\\icon.ico"
|
"value": "/build/icon.ico"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"optionDest": "name",
|
"optionDest": "name",
|
||||||
|
@ -63,15 +67,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"optionDest": "datas",
|
"optionDest": "datas",
|
||||||
"value": "\\templates;templates/"
|
"value": "/templates;templates/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"optionDest": "datas",
|
"optionDest": "datas",
|
||||||
"value": "\\ui-static;ui-static/"
|
"value": "/ui-static;ui-static/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nonPyinstallerOptions": {
|
"nonPyinstallerOptions": {
|
||||||
"increaseRecursionLimit": false,
|
"increaseRecursionLimit": false,
|
||||||
"manualArguments": ""
|
"manualArguments": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,13 @@ for option in config["pyinstallerOptions"]:
|
||||||
cmd_str += "--" + str(option_dest) + ' "' + str(option["value"]) + '" '
|
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 == "":
|
if filename == "":
|
||||||
print("No filename data was found in json file.")
|
print("No filename data was found in json file.")
|
||||||
command.write("")
|
command.write("")
|
||||||
else:
|
else:
|
||||||
command.write(cmd_str + ' --distpath "output/" --workpath "build/" "' + filename + '"')
|
command.write(cmd_str + ' --distpath "output/" --workpath "build/" "' + filename + '"')
|
||||||
|
|
||||||
command.close()
|
command.close()
|
||||||
|
|
19
build/build-macos.sh
Executable file
19
build/build-macos.sh
Executable 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
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from helpers.os_environment import isWindows
|
||||||
|
|
||||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
parent_path = os.path.dirname(dir_path)
|
parent_path = os.path.dirname(dir_path)
|
||||||
|
@ -11,6 +12,8 @@ in_file.close()
|
||||||
for option in config["pyinstallerOptions"]:
|
for option in config["pyinstallerOptions"]:
|
||||||
if option["optionDest"] in ["datas", "filenames", "icon_file"]:
|
if option["optionDest"] in ["datas", "filenames", "icon_file"]:
|
||||||
option["value"] = os.path.abspath(parent_path + option["value"])
|
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 = open('build-exe-config.json', 'w')
|
||||||
out_file.write(json.dumps(config, indent=2))
|
out_file.write(json.dumps(config, indent=2))
|
||||||
|
|
BIN
build/icon.icns
Normal file
BIN
build/icon.icns
Normal file
Binary file not shown.
BIN
build/icon.png
Normal file
BIN
build/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
27
build/macos-platypus.sh
Normal file
27
build/macos-platypus.sh
Normal 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
|
1
build/requirements-macos.txt
Normal file
1
build/requirements-macos.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pyinstaller
|
4
dev/install-githook.sh
Normal file
4
dev/install-githook.sh
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
cp "./pre-commit" "../.git/hooks/"
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
BIN
docs/images/system-menu.png
Normal file
BIN
docs/images/system-menu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
|
@ -28,7 +28,7 @@ nssm set %service_name% AppStopMethodConsole 5000
|
||||||
nssm set %service_name% AppStopMethodWindow 5000
|
nssm set %service_name% AppStopMethodWindow 5000
|
||||||
nssm set %service_name% AppStopMethodThreads 5000
|
nssm set %service_name% AppStopMethodThreads 5000
|
||||||
nssm set %service_name% DisplayName "BAPSicle Server"
|
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% ObjectName LocalSystem
|
||||||
nssm set %service_name% Start SERVICE_AUTO_START
|
nssm set %service_name% Start SERVICE_AUTO_START
|
||||||
nssm set %service_name% Type SERVICE_INTERACTIVE_PROCESS
|
nssm set %service_name% Type SERVICE_INTERACTIVE_PROCESS
|
||||||
|
@ -41,4 +41,4 @@ nssm start %service_name%
|
||||||
|
|
||||||
timeout 4 /nobreak
|
timeout 4 /nobreak
|
||||||
|
|
||||||
explorer "http://localhost:5000/"
|
explorer "http://localhost:13500/"
|
||||||
|
|
|
@ -1,13 +1,48 @@
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import time
|
import time
|
||||||
|
import sys
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
from server import BAPSicleServer
|
from server import BAPSicleServer
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# On Windows calling this function is necessary.
|
def startServer():
|
||||||
# Causes all kinds of loops if not present.
|
server = multiprocessing.Process(target=BAPSicleServer)
|
||||||
multiprocessing.freeze_support()
|
server.start()
|
||||||
server = multiprocessing.Process(target=BAPSicleServer).start()
|
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(5)
|
||||||
pass
|
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()
|
||||||
|
|
13
player.py
13
player.py
|
@ -10,16 +10,15 @@ import setproctitle
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
import sys
|
||||||
# Stop the Pygame Hello message.
|
# Stop the Pygame Hello message.
|
||||||
import os
|
import os
|
||||||
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
|
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
|
||||||
from pygame import mixer
|
from pygame import mixer
|
||||||
|
from mutagen.mp3 import MP3
|
||||||
|
|
||||||
from helpers.state_manager import StateManager
|
from helpers.state_manager import StateManager
|
||||||
from helpers.logging_manager import LoggingManager
|
from helpers.logging_manager import LoggingManager
|
||||||
from mutagen.mp3 import MP3
|
|
||||||
|
|
||||||
|
|
||||||
class Player():
|
class Player():
|
||||||
state = None
|
state = None
|
||||||
|
@ -155,6 +154,11 @@ class Player():
|
||||||
def load(self, filename):
|
def load(self, filename):
|
||||||
if not self.isPlaying:
|
if not self.isPlaying:
|
||||||
self.unload()
|
self.unload()
|
||||||
|
# Fix any OS specific / or \'s
|
||||||
|
if os.path.sep == "/":
|
||||||
|
filename = filename.replace("\\", '/')
|
||||||
|
else:
|
||||||
|
filename = filename.replace("/", '\\')
|
||||||
|
|
||||||
self.state.update("filename", filename)
|
self.state.update("filename", filename)
|
||||||
|
|
||||||
|
@ -346,6 +350,7 @@ class Player():
|
||||||
self.logger.log.info("Quiting player ", channel)
|
self.logger.log.info("Quiting player ", channel)
|
||||||
self.quit()
|
self.quit()
|
||||||
self._retMsg("EXIT")
|
self._retMsg("EXIT")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def showOutput(in_q, out_q):
|
def showOutput(in_q, out_q):
|
||||||
|
@ -357,6 +362,8 @@ def showOutput(in_q, out_q):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
if isMacOS:
|
||||||
|
multiprocessing.set_start_method("spawn", True)
|
||||||
|
|
||||||
in_q = multiprocessing.Queue()
|
in_q = multiprocessing.Queue()
|
||||||
out_q = multiprocessing.Queue()
|
out_q = multiprocessing.Queue()
|
||||||
|
|
36
server.py
36
server.py
|
@ -4,6 +4,8 @@ from flask import Flask, render_template, send_from_directory, request
|
||||||
import json
|
import json
|
||||||
import sounddevice as sd
|
import sounddevice as sd
|
||||||
import setproctitle
|
import setproctitle
|
||||||
|
import logging
|
||||||
|
from helpers.os_environment import isMacOS
|
||||||
|
|
||||||
setproctitle.setproctitle("BAPSicle - Server")
|
setproctitle.setproctitle("BAPSicle - Server")
|
||||||
|
|
||||||
|
@ -24,10 +26,15 @@ class BAPSicleServer():
|
||||||
|
|
||||||
app = Flask(__name__, static_url_path='')
|
app = Flask(__name__, static_url_path='')
|
||||||
|
|
||||||
|
log = logging.getLogger('werkzeug')
|
||||||
|
log.disabled = True
|
||||||
|
app.logger.disabled = True
|
||||||
|
|
||||||
channel_to_q = []
|
channel_to_q = []
|
||||||
channel_from_q = []
|
channel_from_q = []
|
||||||
channel_p = []
|
channel_p = []
|
||||||
|
|
||||||
|
stopping = False
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
|
@ -154,6 +161,12 @@ def status(channel):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/quit")
|
||||||
|
def quit():
|
||||||
|
stopServer()
|
||||||
|
return "Shutting down..."
|
||||||
|
|
||||||
|
|
||||||
@app.route("/player/all/stop")
|
@app.route("/player/all/stop")
|
||||||
def all_stop():
|
def all_stop():
|
||||||
for channel in channel_to_q:
|
for channel in channel_to_q:
|
||||||
|
@ -167,7 +180,10 @@ def send_static(path):
|
||||||
|
|
||||||
|
|
||||||
def startServer():
|
def startServer():
|
||||||
|
if isMacOS():
|
||||||
|
multiprocessing.set_start_method("spawn", True)
|
||||||
for channel in range(3):
|
for channel in range(3):
|
||||||
|
|
||||||
channel_to_q.append(multiprocessing.Queue())
|
channel_to_q.append(multiprocessing.Queue())
|
||||||
channel_from_q.append(multiprocessing.Queue())
|
channel_from_q.append(multiprocessing.Queue())
|
||||||
channel_p.append(
|
channel_p.append(
|
||||||
|
@ -180,7 +196,7 @@ def startServer():
|
||||||
channel_p[channel].start()
|
channel_p[channel].start()
|
||||||
|
|
||||||
# Don't use reloader, it causes Nested Processes!
|
# 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():
|
def stopServer():
|
||||||
|
@ -188,9 +204,21 @@ def stopServer():
|
||||||
for q in channel_to_q:
|
for q in channel_to_q:
|
||||||
q.put("QUIT")
|
q.put("QUIT")
|
||||||
for player in channel_p:
|
for player in channel_p:
|
||||||
player.join()
|
try:
|
||||||
global app
|
player.join()
|
||||||
app = None
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Reference in a new issue