changeset 531:e8f6722a3881

ascoltami2 web one is in good shape. main missing piece is the auto-go button Ignore-this: 2a43f2b25b7b011437e8011bbab15954
author drewp@bigasterisk.com
date Sat, 12 Jun 2010 07:00:54 +0000
parents 77b17dc36834
children 8d6f6d8a4719
files light9/ascoltami/index.html light9/ascoltami/player.py light9/ascoltami/webapp.py static/style.css
diffstat 4 files changed, 158 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/light9/ascoltami/index.html	Fri Jun 11 07:16:46 2010 +0000
+++ b/light9/ascoltami/index.html	Sat Jun 12 07:00:54 2010 +0000
@@ -11,50 +11,69 @@
   </head>
   <body>
 
-
-    <div class="songs">
-    </div>
-
+    <div class="songs"/>
 
     <div>Song: <span id="currentSong"/></div>
     <div>Time: <span id="currentTime"/></div>
     <div>Left: <span id="leftTime"/></div>
-    <div>
+    <div>Until autostop: <span id="leftAutoStopTime"/></div>
+    <div class="timeRow">
       <div id="timeSlider"/>
     </div>
 
     <div class="commands">
-      <button id="cmd-stop">Stop C-s</button>
-      <button id="cmd-play">Play C-p</button>
-      <button id="cmd-intro">Skip intro C-i</button>
-      <button id="cmd-post">Skip to Post C-t</button>
-      <button id="cmd-go">Go  space</button>
+      <button id="cmd-stop" class="playMode">Stop<div class="key">C-s</div></button>
+      <button id="cmd-play" class="playMode">Play <div class="key">C-p</div></button>
+      <button id="cmd-intro">Skip intro <div class="key">C-i</div></button>
+      <button id="cmd-post">Skip to Post <div class="key">C-t</div></button>
+      <button id="cmd-go">Go  <div class="key">space</div></button>
     </div>
 
-bind keys, spacebar, css work
+todo: go button actions, display next action
 
 <script type="text/javascript">
 // <![CDATA[
 $(function () {
 
-    var times = {
+    var times = { // need to get these from server
 	intro: 4,
-	post: 10
+	post: 4
     };
+
     var currentDuration = 0;
-
+    var currentHighlightedSong = "";
+    var lastPlaying;
     function updateCurrent() {
 	$.getJSON("time", {}, function (data, status) {
 	    $("#currentSong").text(data.song);
-	    $("#currentTime").text(data.t);
-	    $("#leftTime").text(data.duration - data.t);
+	    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));
 	    currentDuration = data.duration;
 	    $("#timeSlider").slider({value: data.t,
 				     max: data.duration});
-
+	    if (data.playing != lastPlaying) {
+		$(".playMode").removeClass("active");
+		$(data.playing ? "#cmd-play" : "#cmd-stop").addClass("active");
+		lastPlaying = data.playing;
+	    }
 	});
     }
-
+    function showCurrentSong(uri) {
+	$(".songs div").each(function (i, row) {
+	    row = $(row);
+	    if (row.find("button").data("path") == uri) {
+		row.addClass("currentSong");
+	    } else {
+		row.removeClass("currentSong");
+	    }
+	});
+	currentHighlightedSong = uri;
+    }
     $.getJSON("songs", {}, function (data, status) {
 	$.each(data.songs, function (i, song) {
 	    var button = $("<button>");
@@ -63,7 +82,7 @@
 	    button.click(function () {
 		$.post("song", button.data("path"), 
 		       function (data, textStatus, xhr) {
-		    
+			   showCurrentSong(song.uri);
 		       });
 	    });
 	    $(".songs").append($("<div>").append(button));
@@ -72,14 +91,27 @@
 
     var tojs = JSON.stringify;
 
+    $(document).keypress(function (ev) {
+	if (1 || ev.ctrlKey) {
+	    if (ev.which == 115) { $("#cmd-stop").click(); return false; }
+	    if (ev.which == 112) { $("#cmd-play").click(); return false; }
+	    if (ev.which == 105) { $("#cmd-intro").click(); return false; }
+	    if (ev.which == 116) { $("#cmd-post").click(); return false; }
+	}
+	if (ev.which == 32) { $("#cmd-go").click(); return false; }
+	return true;
+    });
+
     $("#cmd-stop").click(function () { $.post("time", tojs({pause: true})); });
     $("#cmd-play").click(function () { $.post("time", tojs({resume: true})); });
-    $("#cmd-intro").click(function () { $.post("time", tojs({t: times.intro}))});
+    $("#cmd-intro").click(function () { 
+	$.post("time", tojs({t: times.intro, resume: true}))
+    });
     $("#cmd-post").click(function () { 
-	$.post("time", tojs({t: currentDuration - times.post}))
+	$.post("time", tojs({t: currentDuration - times.post, resume: true}))
     });
     $("#cmd-go").click(function () {
-	
+	// todo
     });
 
     var pendingSlide = false;
@@ -95,11 +127,11 @@
 		       pendingSlide = false;
 		   });
 	},
-});
+    });
     
     function updateLoop() {
 	updateCurrent();
-	setTimeout(updateLoop, 300);
+	setTimeout(updateLoop, 200);
     }
     updateLoop();
 });
--- a/light9/ascoltami/player.py	Fri Jun 11 07:16:46 2010 +0000
+++ b/light9/ascoltami/player.py	Sat Jun 12 07:00:54 2010 +0000
@@ -4,38 +4,51 @@
 alternate to the mpd music player, for ascoltami
 """
 from __future__ import division
-import time, logging
-import gst
+import time, logging, traceback
+import gst, gobject
 log = logging.getLogger()
 
 class Player(object):
-    def __init__(self):
-
-        self.playbin = self.pipeline = gst.parse_launch("playbin2 name=b")
+    def __init__(self, autoStopOffset=4):
+        self.autoStopOffset = autoStopOffset
+        self.playbin = self.pipeline = gst.parse_launch("playbin2")
         self.playStartTime = 0
-        self._duration = 0
-        self.pauseTime = 0
+        self.lastWatchTime = 0
+        self.autoStopTime = 0
 
-        self.setSong("file:///my/proj/light9/show/dance2010/music/07-jacksonmix-complete.wav")
-        
+        # before playbin2:
         #self.pipeline = gst.parse_launch("filesrc name=file location=%s ! wavparse name=src ! audioconvert ! alsasink name=out" % songFile)
 
+        gobject.timeout_add(50, self.watchTime)
+
+        bus = self.pipeline.get_bus()
+        bus.add_signal_watch()
+
         def on_any(bus, msg):
             print bus, msg
-
-        bus = self.pipeline.get_bus()
-        bus.add_signal_watch()
         #bus.connect('message', on_any)
 
         def onStreamStatus(bus, message):
             (statusType, _elem) = message.parse_stream_status()
             if statusType == gst.STREAM_STATUS_TYPE_ENTER:
                 self.setupAutostop()
+        bus.connect('message::stream-status', onStreamStatus)
 
-            # we should run our own poller looking for crosses over the autostop threshold and pausing. When i used the pipeline.seek end-time, it caused lots of unwanted other pausing and was hard to turn off.
-                
-            #print message, bus
-        bus.connect('message::stream-status', onStreamStatus)
+    def watchTime(self):
+        try:
+            try:
+                t = self.currentTime()
+            except gst.QueryError:
+                return True
+            log.debug("watch %s < %s < %s",
+                      self.lastWatchTime, self.autoStopTime, t)
+            if self.lastWatchTime < self.autoStopTime < t:
+                log.info("autostop")
+                self.pause()
+            self.lastWatchTime = t
+        except:
+            traceback.print_exc()
+        return True
 
     def seek(self, t):
         assert self.playbin.seek_simple(
@@ -54,17 +67,18 @@
         self.pipeline.set_state(gst.STATE_PLAYING)
         self.playStartTime = time.time()
 
-
-
-#        GST_MESSAGE_DURATION
-        
-
     def currentTime(self):
-        cur, _format = self.playbin.query_position(gst.FORMAT_TIME)
+        try:
+            cur, _format = self.playbin.query_position(gst.FORMAT_TIME)
+        except gst.QueryError:
+            return 0
         return cur / gst.SECOND
 
     def duration(self):
-        return self.playbin.query_duration(gst.FORMAT_TIME)[0] / gst.SECOND
+        try:
+            return self.playbin.query_duration(gst.FORMAT_TIME)[0] / gst.SECOND
+        except gst.QueryError:
+            return 0
         
     def pause(self):
         self.pipeline.set_state(gst.STATE_PAUSED)
@@ -72,32 +86,21 @@
     def resume(self):
         self.pipeline.set_state(gst.STATE_PLAYING)
         pos = self.currentTime()
-        autoStop = self.duration() - 10
+        autoStop = self.duration() - self.autoStopOffset
         if abs(pos - autoStop) < .01:
             self.releaseAutostop()
 
-
     def setupAutostop(self):
-        return
         dur = self.duration()
-        autoStop = (dur - 10.0)
-        log.info("autostop will be at %s", autoStop)
-        print "seek", self.pipeline.seek(1.0, gst.FORMAT_TIME,
-                           gst.SEEK_FLAG_ACCURATE,
-                           gst.SEEK_TYPE_NONE, 0,
-                           gst.SEEK_TYPE_SET, autoStop * gst.SECOND)
+        self.autoStopTime = (dur - self.autoStopOffset)
+        log.info("autostop will be at %s", self.autoStopTime)
+        # pipeline.seek can take a stop time, but using that wasn't
+        # working out well. I'd get pauses at other times that were
+        # hard to remove.
 
-    def releaseAutostop(self):
-        log.info("release autostop")
-        
-        print "seek", self.pipeline.seek(
-            1.0, gst.FORMAT_TIME,
-            gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE | gst.SEEK_FLAG_SKIP,
-            gst.SEEK_TYPE_NONE, 0,
-            gst.SEEK_TYPE_END, 0)
-        print self.pipeline.get_state()
-        self.pipeline.set_state(gst.STATE_PLAYING)
-
-                           
+    def isPlaying(self):
+        _, state, _ = self.pipeline.get_state()
+        return state == gst.STATE_PLAYING
+                  
                            
         
--- a/light9/ascoltami/webapp.py	Fri Jun 11 07:16:46 2010 +0000
+++ b/light9/ascoltami/webapp.py	Sat Jun 12 07:00:54 2010 +0000
@@ -16,6 +16,7 @@
         return jsonlib.write({"song" : player.playbin.get_property("uri"),
                               "started" : player.playStartTime,
                               "duration" : player.duration(),
+                              "playing" : player.isPlaying(),
                               "t" : player.currentTime()})
 
     def POST(self):
--- a/static/style.css	Fri Jun 11 07:16:46 2010 +0000
+++ b/static/style.css	Sat Jun 12 07:00:54 2010 +0000
@@ -1,9 +1,58 @@
+body { 
+    background: black;
+    color: white;
+    font-family: sans-serif;
+}
+.songs {
+    -moz-column-width:205px;
+    -webkit-column-width:205px;
+}
 .songs button {
-  display: inline-block;
-  width: 300px;
-height: 30px;
-  text-align: left;
+    display: inline-block;
+    width: 200px;
+    height: 50px;
+    text-align: left;
+    background: black;
+    color: white;
+    margin: 2px;
+font-size: 115%;
 }
 .songs button:hover {
-  background: yellow;
+    color: black;
+    background: yellow;
+}
+body { 
+    background: black;
+    color: white;
+    font-style: sans-serif;
+}
+.commands button {
+    background: black;
+    color: white;
+    padding: 20px
+}
+.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;
 }
\ No newline at end of file