Tu's in die Cloud, höhöhö....
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

atmorepl.py 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #!/usr/bin/env python3
  2. import socketserver
  3. import threading
  4. class WrongArgumentCount(Exception):
  5. def __init__(self, command, expected, found):
  6. if isinstance(expected, list):
  7. expected = "{} to {}".format(**expected)
  8. msg = "Command {} expects {} arguments, found {}".format(
  9. command, expected, found
  10. )
  11. super(WrongArgumentCount, self).__init__(msg)
  12. class CommandNotFound(Exception):
  13. def __init__(self, command):
  14. msg = "Command {} does not exist".format(command)
  15. super(CommandNotFound, self).__init__(msg)
  16. class AtmoClientHandler(socketserver.StreamRequestHandler):
  17. def setup(self):
  18. super(AtmoClientHandler, self).setup()
  19. self.authenticated = True
  20. self.quit = False
  21. def handle(self):
  22. try:
  23. while not self.quit:
  24. self.send(">>> ", end="")
  25. data = self.rfile.readline().decode().strip()
  26. tokens = data.split()
  27. if len(tokens) > 0:
  28. self.handle_command(tokens)
  29. except BrokenPipeError:
  30. print(" >> Client disconnected")
  31. def handle_command(self, tokens):
  32. cmd = tokens[0].lower()
  33. args = tokens[1:]
  34. try:
  35. if cmd == "reload":
  36. self._ensure_args(cmd, 0, args)
  37. self.atmo.load_sounds()
  38. self.send("Sounds reloaded")
  39. elif cmd == "status":
  40. self._ensure_args(cmd, 0, args)
  41. self.send("Currently playing:")
  42. s = self.atmo.get_status()
  43. for line in s.splitlines():
  44. self.send(line)
  45. elif cmd == "pause":
  46. self._ensure_args(cmd, 0, args)
  47. is_paused = self.atmo.pause()
  48. self.send("Paused" if is_paused else "Resume")
  49. elif cmd == "help":
  50. self._ensure_args(cmd, 0, args)
  51. self.send_usage()
  52. elif cmd == "exit":
  53. self.quit = True
  54. self.send("Bye")
  55. else:
  56. raise CommandNotFound(cmd)
  57. except CommandNotFound as e:
  58. self.send("Error: {}".format(e))
  59. self.send_usage()
  60. except WrongArgumentCount as e:
  61. self.send("Error: {}".format(e))
  62. def send_usage(self):
  63. self.send("The following commands are available: ")
  64. self.send(" reload")
  65. self.send(" status")
  66. self.send(" pause")
  67. @staticmethod
  68. def _ensure_args(cmd, expected, args):
  69. if isinstance(expected, list):
  70. if not expected[0] <= len(args) <= expected[1]:
  71. raise WrongArgumentCount(cmd, expected, len(args))
  72. else:
  73. if len(args) != expected:
  74. raise WrongArgumentCount(cmd, expected, len(args))
  75. def send(self, line, end="\n"):
  76. self.wfile.write("{}{}".format(line, end).encode())
  77. class ReusableThreadingTCPServer(socketserver.ThreadingTCPServer):
  78. daemon_threads = True
  79. allow_reuse_address = True
  80. class AtmoReplRunner(threading.Thread):
  81. def __init__(self, atmo, host="0.0.0.0", port=7723):
  82. super(AtmoReplRunner, self).__init__()
  83. AtmoClientHandler.atmo = atmo
  84. self._client = ReusableThreadingTCPServer((host, port), AtmoClientHandler)
  85. self.daemon = True
  86. def run(self):
  87. self._client.serve_forever()