Changeset - c2faa69099e6
[Not reviewed]
default
0 4 0
drewp@bigasterisk.com - 12 years ago 2013-06-12 23:47:12
drewp@bigasterisk.com
asco: display update frequency and dim when updates stop. run slower updates on tablets and phones
Ignore-this: 2439d0f540c9eae00c4f18e26411c100
4 files changed with 50 insertions and 14 deletions:
0 comments (0 inline, 0 general)
bin/ascoltami2
Show inline comments
 
@@ -21,36 +21,41 @@ class App(object):
 
        self.playlist = Playlist.fromShow(graph, show)
 

	
 
    def onEOS(self, song):
 
        self.player.pause()
 
        self.player.seek(0)
 

	
 
        thisSongUri = songUri(graph, URIRef(song))
 

	
 
        try:
 
            nextSong = self.playlist.nextSong(thisSongUri)
 
        except NoSuchSong: # we're at the end of the playlist
 
            return
 

	
 
        self.player.setSong(songLocation(graph, nextSong), play=False)
 

	
 
if __name__ == "__main__":
 
    GObject.threads_init()
 
    Gst.init(None)
 

	
 
    parser = optparse.OptionParser()
 
    parser.add_option('--show',
 
        help='show URI, like http://light9.bigasterisk.com/show/dance2008', default=showconfig.showUri())
 
    parser.add_option("-v", "--verbose", action="store_true",
 
                      help="logging.DEBUG")
 
    parser.add_option("--twistedlog", action="store_true",
 
                      help="twisted logging")
 
    (options, args) = parser.parse_args()
 

	
 
    log.setLevel(logging.DEBUG if options.verbose else logging.INFO)
 

	
 
    if not options.show:
 
        raise ValueError("missing --show http://...")
 
            
 
    graph = showconfig.getGraph()
 
    app = App(graph, URIRef(options.show))
 
    if options.twistedlog:
 
        from twisted.python import log as twlog
 
        twlog.startLogging(sys.stderr)
 
    reactor.listenTCP(networking.musicPlayer.port, makeWebApp(app))
 
    log.info("listening on %s" % networking.musicPlayer.port)
 
    reactor.run()
light9/ascoltami/index.html
Show inline comments
 
<?xml version="1.0" encoding="iso-8859-1"?>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
 
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" 
 
      xmlns:py="http://genshi.edgewall.org/">
 
  <head>
 
    <title>ascoltami on ${host}</title>
 
    <script type="text/javascript" src="static/jquery-1.4.2.min.js"></script>
 
      <script type="text/javascript" src="static/jquery-ui-1.8.2.custom/js/jquery-ui-1.8.2.custom.min.js"></script>
 
      <link rel="Stylesheet" type="text/css" href="static/jquery-ui-1.8.2.custom/css/smoothness/jquery-ui-1.8.2.custom.css"/>
 
      <link rel="Stylesheet" type="text/css" href="static/style.css"/>
 
  </head>
 
  <body>
 
    <h1>ascoltami on ${host}</h1>
 
    <div class="songs"/>
 

	
 
    <div>Song: <span id="currentSong"/></div>
 
    <div>Time: <span id="currentTime"/></div>
 
    <div>Left: <span id="leftTime"/></div>
 
    <div>Until autostop: <span id="leftAutoStopTime"/></div>
 
    <div>States: <span id="states"/></div>
 
    <div class="timeRow">
 
      <div id="timeSlider"/>
 
    <div class="dimStalled">
 
      <div>Song: <span id="currentSong"/></div>
 
      <div>Time: <span id="currentTime"/></div>
 
      <div>Left: <span id="leftTime"/></div>
 
      <div>Until autostop: <span id="leftAutoStopTime"/></div>
 
      <div>Update freq: requested <span id="updateReq"/>, actual <span id="updateActual"/></div>
 
      <div>States: <span id="states"/></div>
 
      <div class="timeRow">
 
        <div id="timeSlider"/>
 
      </div>
 
    </div>
 

	
 
    <div class="commands">
 
      <button id="cmd-stop" class="playMode">Stop<div class="key">s</div></button>
 
      <button id="cmd-play" class="playMode">Play <div class="key">p</div></button>
 
      <button id="cmd-intro">Skip intro <div class="key">i</div></button>
 
      <button id="cmd-post">Skip to Post <div class="key">t</div></button>
 
      <button id="cmd-go">Go  <div class="key">space</div></button>
 

	
 
      <button id="cmd-out0">Output 0</button>
 
      <button id="cmd-out1">Output 1</button>
 
    </div>
 

	
 
    
 
    <p>Running on <span id="nav"/></p>
 
    todo: display next action
 
    <p><button onclick="window.open('/', '_blank', 'scrollbars=1,resizable=1,titlebar=0,location=0')">reopen this in a simpler window</button></p>
 
    
 
    todo: go button actions, display next action
 
    <a href="">reload</a>
 
    <p><a href="">reload</a></p>
 

	
 
<script type="text/javascript">
 
// <![CDATA[
 
$(function () {
 

	
 
    $("#nav").text(navigator.userAgent);
 
    var updateFreq = (navigator.userAgent.indexOf("Linux") != -1) ? 10 : 2;
 
    $("#updateReq").text(updateFreq);
 

	
 
    var times = { // need to get these from server
 
	intro: 4,
 
	post: 4
 
    };
 

	
 
    var currentDuration = 0;
 
    var currentHighlightedSong = "";
 
    var lastPlaying;
 
    function updateCurrent(doneCallback) {
 
	$.getJSON("time", {}, function (data, status) {
 
	    $("#currentSong").text(data.song);
 
	    if (data.song != currentHighlightedSong) {
 
		showCurrentSong(data.song);
 
	    }
 
	    $("#currentTime").text(data.t.toFixed(1));
 
	    $("#leftTime").text((data.duration - data.t).toFixed(1));
 
	    $("#leftAutoStopTime").text(
 
		Math.max(0, data.duration - times.post - data.t).toFixed(1));
 
	    $("#states").text(JSON.stringify(data.state));
 
	    currentDuration = data.duration;
 
	    $("#timeSlider").slider({value: data.t,
 
				     max: data.duration});
 
	    if (data.playing != lastPlaying) {
 
		$(".playMode").removeClass("active");
 
@@ -124,41 +130,63 @@
 
	$.post("go");
 
    });
 
    $("#cmd-out0").click(function () { $.post("output", tojs({sink: "0"})); })
 
    $("#cmd-out1").click(function () { $.post("output", tojs({sink: "1"})); })
 

	
 
    var pendingSlide = false;
 
    $("#timeSlider").slider({
 
	step: .01,
 
	slide: function (event, ui) {
 
	    if (pendingSlide) {
 
		return;
 
	    }
 
	    pendingSlide = true;
 
	    $.post("time", '{"t" : '+ui.value+'}', 
 
		   function (data, status, xhr) {
 
		       pendingSlide = false;
 
		   });
 
	},
 
    });
 
    
 
    var raf = window.requestAnimationFrame ||
 
	window.mozRequestAnimationFrame || 
 
	window.webkitRequestAnimationFrame;
 

	
 
    var recentUpdates = [];
 
    function onUpdate() {
 
        recentUpdates.push(+new Date());
 
        recentUpdates = recentUpdates.slice(Math.max(recentUpdates.length - 5, 0));
 
        refreshUpdateFreqs();
 
    }
 

	
 
    function refreshUpdateFreqs() {
 
        if (recentUpdates.length > 1) {
 
          if (+new Date() - recentUpdates[recentUpdates.length - 1] > 1000) {
 
            $("#updateActual").text("(stalled)");
 
            $(".dimStalled").addClass("stalled");
 
            return;
 
          }
 

	
 
          var avgMs = (recentUpdates[recentUpdates.length - 1] - recentUpdates[0]) / (recentUpdates.length - 1);
 
          $("#updateActual").text(Math.round(1000 / avgMs));
 
        }
 
    }
 
    setInterval(refreshUpdateFreqs, 2000);
 

	
 
    function updateLoop() {
 
	var whenDone = function () {
 
	    setTimeout(function () { 
 
		raf(updateLoop);
 
	    }, 50);
 
	    }, 1000 / updateFreq);
 
	};
 
        onUpdate();
 
	updateCurrent(whenDone);
 
    }
 
    updateLoop();
 

	
 
});
 
// ]]>
 
</script>
 

	
 

	
 
  </body>
 
</html>
light9/ascoltami/webapp.py
Show inline comments
 
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__, ".")])
 
render = render_genshi([sibpath(__file__, ".")], auto_reload=True)
 

	
 
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):
 
    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")
 
        # todo: use a template; embed the show name and the intro/post
 
        # times into the page
 
        self.write(render.index(host=socket.gethostname()))
static/style.css
Show inline comments
 
@@ -34,25 +34,28 @@ body {
 
.commands button.active {
 
    background: red;
 
}
 

	
 
.songs button:hover {
 
    color: black;
 
    background: yellow;
 
}
 
.commands button {
 
    background: black;
 
    color: white;
 
    padding: 20px
 
}
 
.commands button.active {
 
    background: #1C4054;
 
}
 
.key {
 
    color: #888;
 
}
 
.currentSong button {
 
    background: #55A5C9;
 
}
 
.timeRow {
 
    margin: 14px;
 
}
 
.stalled {
 
    opacity: .5;
 
}
 
\ No newline at end of file
0 comments (0 inline, 0 general)