Mercurial > code > home > repos > light9
view light9/ascoltami/webapp.py @ 1954:3ae1e7f8db23
vidref playback smoothness, autodelete short clips, manual-delete clips, vidref keyboard shortcuts
Ignore-this: 6daccf686fd66561029f3252ed4dbafd
author | Drew Perttula <drewp@bigasterisk.com> |
---|---|
date | Thu, 06 Jun 2019 02:28:28 +0000 |
parents | 9f0f2b39ad95 |
children | 5154f5a23e85 |
line wrap: on
line source
import json, socket, subprocess, os, logging, time from cyclone import template from rdflib import URIRef import cyclone.web, cyclone.websocket from greplin.scales.cyclonehandler import StatsHandler from cycloneerr import PrettyErrorHandler from light9.namespaces import L9 from light9.showconfig import getSongsFromShow, songOnDisk from twisted.internet import reactor _songUris = {} # locationUri : song log = logging.getLogger() loader = template.Loader(os.path.dirname(__file__)) def songLocation(graph, songUri): loc = URIRef("file://%s" % songOnDisk(songUri)) _songUris[loc] = songUri return loc def songUri(graph, locationUri): return _songUris[locationUri] class root(PrettyErrorHandler, cyclone.web.RequestHandler): def get(self): self.set_header("Content-Type", "application/xhtml+xml") self.write( loader.load('index.html').generate(host=socket.gethostname(), times=json.dumps({ 'intro': 4, 'post': 4 }))) def playerSongUri(graph, player): """or None""" playingLocation = player.getSong() if playingLocation: return songUri(graph, URIRef(playingLocation)) else: return None def currentState(graph, player): if player.isAutostopped(): nextAction = 'finish' elif player.isPlaying(): nextAction = 'disabled' else: nextAction = 'play' return { "song": playerSongUri(graph, player), "started": player.playStartTime, "duration": player.duration(), "playing": player.isPlaying(), "t": player.currentTime(), "state": player.states(), "next": nextAction, } class timeResource(PrettyErrorHandler, cyclone.web.RequestHandler): def get(self): player = self.settings.app.player graph = self.settings.app.graph self.set_header("Content-Type", "application/json") self.write(json.dumps(currentState(graph, player))) def post(self): """ post a json object with {pause: true} or {resume: true} if you want those actions. Use {t: <seconds>} to seek, optionally with a pause/resume command too. """ params = json.loads(self.request.body) player = self.settings.app.player if params.get('pause', False): player.pause() if params.get('resume', False): player.resume() if 't' in params: player.seek(params['t']) self.set_header("Content-Type", "text/plain") self.write("ok") class timeStreamResource(cyclone.websocket.WebSocketHandler): def connectionMade(self, *args, **kwargs) -> None: self.lastSent = None self.lastSentTime = 0. self.loop() def loop(self): now = time.time() msg = currentState(self.settings.app.graph, self.settings.app.player) if msg != self.lastSent or now > self.lastSentTime + 2: self.sendMessage(json.dumps(msg)) self.lastSent = msg self.lastSentTime = now if self.transport.connected: reactor.callLater(.2, self.loop) def connectionLost(self, reason): log.info("bye ws client %r: %s", self, reason) class songs(PrettyErrorHandler, cyclone.web.RequestHandler): def get(self): graph = self.settings.app.graph songs = getSongsFromShow(graph, self.settings.app.show) self.set_header("Content-Type", "application/json") self.write( json.dumps({ "songs": [{ "uri": s, "path": graph.value(s, L9['showPath']), "label": graph.label(s) } for s in songs] })) class songResource(PrettyErrorHandler, cyclone.web.RequestHandler): def post(self): """post a uri of song to switch to (and start playing)""" graph = self.settings.app.graph self.settings.app.player.setSong( songLocation(graph, URIRef(self.request.body.decode('utf8')))) self.set_header("Content-Type", "text/plain") self.write("ok") class seekPlayOrPause(PrettyErrorHandler, cyclone.web.RequestHandler): """curveCalc's ctrl-p or a vidref scrub""" def post(self): player = self.settings.app.player data = json.loads(self.request.body) if 'scrub' in data: player.pause() player.seek(data['scrub']) return if 'action' in data: if data['action'] == 'play': player.resume() elif data['action'] == 'pause': player.pause() else: raise NotImplementedError return if player.isPlaying(): player.pause() else: player.seek(data['t']) player.resume() class output(PrettyErrorHandler, cyclone.web.RequestHandler): def post(self): d = json.loads(self.request.body) subprocess.check_call(["bin/movesinks", str(d['sink'])]) class goButton(PrettyErrorHandler, cyclone.web.RequestHandler): def post(self): """ if music is playing, this silently does nothing. """ player = self.settings.app.player if player.isAutostopped(): player.resume() elif player.isPlaying(): pass else: player.resume() self.set_header("Content-Type", "text/plain") self.write("ok") def makeWebApp(app): return cyclone.web.Application(handlers=[ (r"/", root), (r"/time", timeResource), (r"/time/stream", timeStreamResource), (r"/song", songResource), (r"/songs", songs), (r"/seekPlayOrPause", seekPlayOrPause), (r"/output", output), (r"/go", goButton), (r'/stats/(.*)', StatsHandler, { 'serverName': 'ascoltami' }), ], app=app)