Changeset - cbfed4e684ef
[Not reviewed]
default
0 2 0
drewp@bigasterisk.com - 13 years ago 2012-06-30 06:29:23
drewp@bigasterisk.com
redo asco web server to use twisted/cyclone. mainloop is now glib inside twisted. seems ok (and lower cpu usage) but not well tested
Ignore-this: 24ae0b173599a0d99e465f1b5a10f39b
2 files changed with 72 insertions and 66 deletions:
0 comments (0 inline, 0 general)
bin/ascoltami2
Show inline comments
 
#!bin/python
 
from twisted.internet import gtk2reactor, reactor
 
import web, thread, gobject, sys, optparse, logging
 
from rdflib import URIRef
 
sys.path.append(".")
 
@@ -8,10 +9,6 @@ from light9.ascoltami.webapp import make
 
from light9 import networking, showconfig
 

	
 

	
 
import BaseHTTPServer
 
BaseHTTPServer.BaseHTTPRequestHandler.log_message = \
 
                     lambda self, format, *args: None
 

	
 
class App:
 
    def __init__(self, graph, show):
 
        self.graph = graph
 
@@ -22,12 +19,10 @@ class App:
 
    def run(self, musicPort):
 
        # the cherrypy server would wedge when vidref pounds on it; this
 
        # one seems to run
 
        thread.start_new(web.httpserver.runbasic,
 
                         (makeWebApp(self).wsgifunc(),
 
                          ('0.0.0.0', musicPort)))
 

	
 
        mainloop = gobject.MainLoop()
 
        mainloop.run()
 
        #gtk2reactor.install(useGtk=False)
 
        reactor.listenTCP(musicPort, makeWebApp(self))
 
        log.info("listening on %s" % musicPort)
 
        reactor.run()
 

	
 
    def onEOS(self, song):
 
        self.player.pause()
 
@@ -45,7 +40,7 @@ class App:
 
if __name__ == "__main__":
 
    logging.basicConfig()
 
    log = logging.getLogger()
 
    gobject.threads_init()
 
    gobject.threads_init() # this is in gtk2reactor too
 

	
 
    parser = optparse.OptionParser()
 
    parser.add_option('--show',
light9/ascoltami/webapp.py
Show inline comments
 
import web, json, socket, subprocess
 
import json, socket, subprocess, cyclone.web, os
 
from twisted.python.util import sibpath
 
from twisted.python.filepath import FilePath
 
from light9.namespaces import L9
 
from light9.showconfig import getSongsFromShow, songOnDisk
 
from rdflib import URIRef
 
from web.contrib.template import render_genshi
 
render = render_genshi([sibpath(__file__, ".")])
 
app = None
 

	
 
try:
 
    import sys
 
    sys.path.append("../homeauto/lib")
 
    from cycloneerr import PrettyErrorHandler
 
except ImportError:
 
    class PrettyErrorHandler(object):
 
        pass
 

	
 
_songUris = {} # locationUri : song
 
def songLocation(graph, songUri):
 
@@ -17,12 +24,12 @@ def songLocation(graph, songUri):
 
def songUri(graph, locationUri):
 
    return _songUris[locationUri]
 

	
 
class root(object):
 
    def GET(self):
 
        web.header("Content-type", "application/xhtml+xml")
 
class root(PrettyErrorHandler, cyclone.web.RequestHandler):
 
    def get(self):
 
        self.set_header("Content-Type", "application/xhtml+xml")
 
        # todo: use a template; embed the show name and the intro/post
 
        # times into the page
 
        return render.index(host=socket.gethostname())
 
        self.write(render.index(host=socket.gethostname()))
 

	
 
def playerSongUri(graph, player):
 
    """or None"""
 
@@ -33,100 +40,104 @@ def playerSongUri(graph, player):
 
    else:
 
        return None
 

	
 
class timeResource(object):
 
    def GET(self):
 
        player = app.player
 
        graph = app.graph
 
        web.header("content-type", "application/json")
 
        return json.dumps({
 
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({
 
            "song" : playerSongUri(graph, player),
 
            "started" : player.playStartTime,
 
            "duration" : player.duration(),
 
            "playing" : player.isPlaying(),
 
            "t" : player.currentTime(),
 
            "state" : player.states(),
 
            })
 
            }))
 

	
 
    def POST(self):
 
    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(web.data())
 
        player = app.player
 
        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'])
 
        web.header("content-type", "text/plain")
 
        return "ok"
 
        self.set_header("Content-Type", "text/plain")
 
        self.write("ok")
 

	
 
class songs(object):
 
    def GET(self):
 
        graph = app.graph
 
class songs(PrettyErrorHandler, cyclone.web.RequestHandler):
 
    def get(self):
 
        graph = self.settings.app.graph
 

	
 
        songs = getSongsFromShow(graph, app.show)
 
        songs = getSongsFromShow(graph, self.settings.app.show)
 

	
 
        web.header("Content-type", "application/json")
 
        return json.dumps({"songs" : [
 
        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]})
 
             "label" : graph.label(s)} for s in songs]}))
 

	
 
class songResource(object):
 
    def POST(self):
 
class songResource(PrettyErrorHandler, cyclone.web.RequestHandler):
 
    def post(self):
 
        """post a uri of song to switch to (and start playing)"""
 
        graph = app.graph
 
        graph = self.settings.app.graph
 

	
 
        app.player.setSong(songLocation(graph, URIRef(web.data())))
 
        web.header("content-type", "text/plain")
 
        return "ok"
 
        self.settings.app.player.setSong(songLocation(graph, URIRef(self.request.body)))
 
        self.set_header("Content-Type", "text/plain")
 
        self.write("ok")
 
    
 
class seekPlayOrPause(object):
 
    def POST(self):
 
        player = app.player
 
class seekPlayOrPause(PrettyErrorHandler, cyclone.web.RequestHandler):
 
    def post(self):
 
        player = self.settings.app.player
 

	
 
        data = json.loads(web.data())
 
        data = json.loads(self.request.body)
 
        if player.isPlaying():
 
            player.pause()
 
        else:
 
            player.seek(data['t'])
 
            player.resume()
 

	
 
class output(object):
 
    def POST(self):
 
        d = json.loads(web.data())
 
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(object):
 
    def POST(self):
 
class goButton(PrettyErrorHandler, cyclone.web.RequestHandler):
 
    def post(self):
 
        """
 
        if music is playing, this silently does nothing.
 
        """
 
        graph, player = app.graph, app.player
 
        graph, player = self.settings.app.graph, self.settings.app.player
 

	
 
        if player.isPlaying():
 
            pass
 
        else:
 
            player.resume()
 
            
 
        web.header("content-type", "text/plain")
 
        return "ok"
 
        self.set_header("Content-Type", "text/plain")
 
        self.write("ok")
 

	
 
def makeWebApp(theApp):
 
    global app
 
    app = theApp
 
class static(PrettyErrorHandler, cyclone.web.RequestHandler):
 
    def get(self, name):
 
        if name.endswith('.css'):
 
            self.set_header("Content-Type", "text/css")
 
        self.write(FilePath("static").preauthChild(name).open().read())
 

	
 
    urls = (r"/", "root",
 
            r"/time", "timeResource",
 
            r"/song", "songResource",
 
            r"/songs", "songs",
 
            r"/seekPlayOrPause", "seekPlayOrPause",
 
            r"/output", "output",
 
            r"/go", "goButton",
 
            )
 
def makeWebApp(app):
 
    return cyclone.web.Application(handlers=[
 
        (r"/", root),
 
        (r"/time", timeResource),
 
        (r"/song", songResource),
 
        (r"/songs", songs),
 
        (r"/seekPlayOrPause", seekPlayOrPause),
 
        (r"/output", output),
 
        (r"/go", goButton),
 
        (r"/static/(.*)", static),
 
        ], app=app)
 

	
 
    return web.application(urls, globals(), autoreload=False)
0 comments (0 inline, 0 general)