dnd-music-console/croaker/server.py

97 lines
3.0 KiB
Python
Raw Normal View History

2024-03-01 01:00:17 -08:00
import os
import logging
import queue
import socketserver
2024-03-01 01:00:17 -08:00
from pathlib import Path
import daemon
from croaker import path
2024-03-01 01:00:17 -08:00
from croaker.pidfile import pidfile
from croaker.controller import Controller
class RequestHandler(socketserver.StreamRequestHandler):
supported_commands = {
'PLAY': "$PLAYLIST_NAME - Switch to the specified playlist.",
'FFWD': " - Skip to the next track in the playlist.",
'HELP': " - Display command help.",
'KTHX': " - Close the current connection.",
'STOP': " - Stop Croaker.",
}
def handle(self):
while True:
self.data = self.rfile.readline().strip().decode()
logging.debug(f"{self.data = }")
try:
cmd = self.data[0:4].strip().upper()
args = self.data[5:]
except IndexError:
self.send(f"ERR Command not understood '{cmd}'")
if cmd not in self.supported_commands:
self.send(f"ERR Unknown Command '{cmd}'")
if cmd == 'KTHX':
return self.send('KBAI')
handler = getattr(self, f"handle_{cmd}", None)
if handler:
handler(args)
else:
self.default_handler(cmd, args)
def send(self, msg):
return self.wfile.write(msg.encode() + b'\n')
def default_handler(self, cmd, args):
self.server.tell_controller(f"{cmd} {args}")
return self.send('OK')
def handle_HELP(self, args):
return self.send('\n'.join(
f"{cmd} {txt}" for cmd, txt in self.supported_commands.items()
))
def handle_STOP(self, args):
self.send("Shutting down.")
self.server.stop()
2024-03-01 01:00:17 -08:00
class CroakerServer(socketserver.TCPServer):
allow_reuse_address = True
2024-03-01 01:00:17 -08:00
def __init__(self):
self._context = daemon.DaemonContext()
self._queue = queue.Queue()
self.controller = Controller(self._queue)
2024-03-01 01:00:17 -08:00
def _pidfile(self, terminate_if_running: bool = True):
return pidfile(path.root() / "croaker.pid", terminate_if_running=terminate_if_running)
2024-03-01 01:00:17 -08:00
def tell_controller(self, msg):
self._queue.put(msg)
2024-03-01 01:00:17 -08:00
def daemonize(self) -> None:
logging.info(f"Daemonizing controller; pidfile and output in {path.root()}")
super().__init__((os.environ['HOST'], int(os.environ['PORT'])), RequestHandler)
2024-03-01 01:00:17 -08:00
self._context.pidfile = self._pidfile()
self._context.stdout = open(path.root() / Path("croaker.out"), "wb")
self._context.stderr = open(path.root() / Path("croaker.err"), "wb", buffering=0)
self._context.files_preserve = [self.fileno()]
self._context.open()
try:
self.controller.start()
self.serve_forever()
except KeyboardInterrupt:
logging.info("Shutting down.")
self.stop()
2024-03-01 01:00:17 -08:00
def stop(self) -> None:
self._pidfile()
2024-03-01 01:00:17 -08:00
server = CroakerServer()