Mercurial > code > home > repos > light9
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