|
@@ -0,0 +1,156 @@
|
|
|
|
+#!./bin/python3
|
|
|
|
+
|
|
|
|
+import os
|
|
|
|
+import sys
|
|
|
|
+import threading
|
|
|
|
+import time
|
|
|
|
+import traceback
|
|
|
|
+from typing import Dict
|
|
|
|
+
|
|
|
|
+import pygame # type: ignore
|
|
|
|
+import tornado.ioloop, tornado.web
|
|
|
|
+import asyncio
|
|
|
|
+
|
|
|
|
+from display import Display
|
|
|
|
+from player import Player
|
|
|
|
+
|
|
|
|
+class WebHandler(tornado.web.RequestHandler):
|
|
|
|
+
|
|
|
|
+ def initialize(self, musikautomat):
|
|
|
|
+ self.musikautomat = musikautomat
|
|
|
|
+
|
|
|
|
+ def get(self, a):
|
|
|
|
+ if a.startswith("play"):
|
|
|
|
+
|
|
|
|
+ if a == "play/dir":
|
|
|
|
+ path = self.get_argument('path')
|
|
|
|
+ self.musikautomat.play({
|
|
|
|
+ "type" : "dir",
|
|
|
|
+ "name" : self.get_argument('name', path),
|
|
|
|
+ "path" : path
|
|
|
|
+ }, start=True)
|
|
|
|
+
|
|
|
|
+class Musikautomat:
|
|
|
|
+
|
|
|
|
+ def __init__(self, pydsp, config: Dict):
|
|
|
|
+
|
|
|
|
+ # Initialise state objects
|
|
|
|
+
|
|
|
|
+ self._pydsp = pydsp
|
|
|
|
+ self._display = Display(config, self._pydsp)
|
|
|
|
+ self._display.update()
|
|
|
|
+ self._eventSink = self._display
|
|
|
|
+ self._player = Player(config, self._pydsp)
|
|
|
|
+ self._dx = config.get("dimx", 128)
|
|
|
|
+
|
|
|
|
+ threading.Thread(target=self.runWebServer, daemon=True).start()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def runWebServer(self):
|
|
|
|
+ '''
|
|
|
|
+ Run event loop for web interface.
|
|
|
|
+ '''
|
|
|
|
+ urls = [
|
|
|
|
+ (r"/api/(.*)", WebHandler, dict(musikautomat=self)),
|
|
|
|
+ (r"/(.*)", tornado.web.StaticFileHandler, {"path" : "./web",
|
|
|
|
+ "default_filename" : "index.html" }),
|
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
+ asyncio.set_event_loop(asyncio.new_event_loop())
|
|
|
|
+ app = tornado.web.Application(urls, debug=False)
|
|
|
|
+ app.listen(8080)
|
|
|
|
+ tornado.ioloop.IOLoop.instance().start()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def runDisplay(self):
|
|
|
|
+ '''
|
|
|
|
+ Run event loop for display and buttons.
|
|
|
|
+ '''
|
|
|
|
+ clk = pygame.time.Clock()
|
|
|
|
+ while True:
|
|
|
|
+ clk.tick(10) # We only need 10 FPS
|
|
|
|
+
|
|
|
|
+ try:
|
|
|
|
+
|
|
|
|
+ for event in pygame.event.get():
|
|
|
|
+
|
|
|
|
+ # Handle exit event
|
|
|
|
+
|
|
|
|
+ if event.type == pygame.QUIT:
|
|
|
|
+ pygame.quit()
|
|
|
|
+ sys.exit()
|
|
|
|
+
|
|
|
|
+ # Handle key events
|
|
|
|
+
|
|
|
|
+ if event.type == pygame.KEYDOWN:
|
|
|
|
+
|
|
|
|
+ if event.key == pygame.K_DOWN:
|
|
|
|
+ self._eventSink.action("down")
|
|
|
|
+
|
|
|
|
+ elif event.key == pygame.K_UP:
|
|
|
|
+ self._eventSink.action("up")
|
|
|
|
+
|
|
|
|
+ elif event.key == pygame.K_RIGHT:
|
|
|
|
+
|
|
|
|
+ if self._eventSink == self._player:
|
|
|
|
+ self._player.togglePlay()
|
|
|
|
+ else:
|
|
|
|
+ self.play(self._display.currentItem())
|
|
|
|
+
|
|
|
|
+ elif event.key == pygame.K_LEFT:
|
|
|
|
+
|
|
|
|
+ # Get back to display
|
|
|
|
+
|
|
|
|
+ self._eventSink = self._display
|
|
|
|
+ self._display.update()
|
|
|
|
+
|
|
|
|
+ except Exception as e:
|
|
|
|
+
|
|
|
|
+ # Display error
|
|
|
|
+ traceback.print_exc()
|
|
|
|
+
|
|
|
|
+ self._pydsp.fill((0,0,0))
|
|
|
|
+ font = pygame.font.Font('freesansbold.ttf', 12)
|
|
|
|
+
|
|
|
|
+ s = str(e)
|
|
|
|
+ n = int(self._dx / 6)
|
|
|
|
+ for i, start in enumerate(range(0, len(s), n)):
|
|
|
|
+ text = font.render(s[start:start+n], True, (255, 0, 0) , (0, 0, 0))
|
|
|
|
+ self._pydsp.blit(text, (0, i * 12))
|
|
|
|
+
|
|
|
|
+ self._eventSink = self._display
|
|
|
|
+
|
|
|
|
+ pygame.display.update()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def play(self, item, start=False):
|
|
|
|
+ '''
|
|
|
|
+ Play an item.
|
|
|
|
+ '''
|
|
|
|
+ print("Playing:", item)
|
|
|
|
+
|
|
|
|
+ self._eventSink = self._player
|
|
|
|
+
|
|
|
|
+ t = item.get("type")
|
|
|
|
+ if t == "dir":
|
|
|
|
+ self.playDir(item)
|
|
|
|
+ else:
|
|
|
|
+ raise Exception("Unknown type: %s" % t)
|
|
|
|
+
|
|
|
|
+ if start:
|
|
|
|
+ self._player.togglePlay()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def playDir(self, item):
|
|
|
|
+ '''
|
|
|
|
+ Play all files in a directory.
|
|
|
|
+ '''
|
|
|
|
+ path = item.get("path")
|
|
|
|
+ files = os.listdir(path)
|
|
|
|
+
|
|
|
|
+ self._player.setPlaylist(sorted([{
|
|
|
|
+ "name" : f,
|
|
|
|
+ "path" : os.path.join(path, f)
|
|
|
|
+ } for f in files], key=lambda i: i["name"]))
|
|
|
|
+
|
|
|
|
+ self._player.update()
|