Merging changes from nighthawk; added support for dumb terminals
authorDan
Fri, 15 Aug 2008 23:49:00 -0400
changeset 34 3b817b961984
parent 33 3b4aef1efff6 (current diff)
parent 31 48004643a8a5 (diff)
child 35 8040903d25de
Merging changes from nighthawk; added support for dumb terminals
functions.php
greyhound.php
webserver.php
--- a/functions.php	Fri Aug 15 23:31:37 2008 -0400
+++ b/functions.php	Fri Aug 15 23:49:00 2008 -0400
@@ -24,23 +24,39 @@
 
 function burnout($msg)
 {
+  global $use_colors;
   $h = @fopen('php://stderr', 'w');
-  fwrite($h, "\x1B[31;1m[Greyhound] fatal: \x1B[37;1m");
-  fwrite($h, "$msg\x1B[0m\n");
+  if ( $use_colors )
+  {
+    fwrite($h, "\x1B[31;1m[Greyhound] fatal: \x1B[37;1m");
+    fwrite($h, "$msg\x1B[0m\n");
+  }
+  else
+  {
+    fwrite($h, "[Greyhound] fatal: $msg\n");
+  }
   fclose($h);
   exit(1);
 }
 
 /**
- * Print a stylized status message, compatible with Linux consoles
+ * Print a stylized status message, compatible with Linux consoles. Falls back to no control characters if on unsupported terminal.
  * @param string Status message
  */
 
 function status($msg)
 {
+  global $use_colors;
   $h = @fopen('php://stderr', 'w');
   $label = ( defined('HTTPD_WS_CHILD') ) ? 'Child ' . substr(strval(getmypid()), -3) : 'Greyhound';
-  fwrite($h, "\x1B[32;1m[$label] \x1B[32;0m$msg\x1B[0m\n");
+  if ( $use_colors )
+  {
+    fwrite($h, "\x1B[32;1m[$label] \x1B[32;0m$msg\x1B[0m\n");
+  }
+  else
+  {
+    fwrite($h, "[$label] $msg\n");
+  }
   fclose($h);
 }
 
@@ -51,8 +67,16 @@
 
 function warning($msg)
 {
+  global $use_colors;
   $h = @fopen('php://stderr', 'w');
-  fwrite($h, "\x1B[33;1m[Greyhound] \x1B[0m\x1B[33mWarning:\x1B[0m $msg\n");
+  if ( $use_colors )
+  {
+    fwrite($h, "\x1B[33;1m[Greyhound] \x1B[0m\x1B[33mWarning:\x1B[0m $msg\n");
+  }
+  else
+  {
+    fwrite($h, "[Greyhound] Warning: $msg\n");
+  }
   fclose($h);
 }
 
--- a/greyhound.php	Fri Aug 15 23:31:37 2008 -0400
+++ b/greyhound.php	Fri Aug 15 23:49:00 2008 -0400
@@ -37,7 +37,7 @@
 // set this to false, it will only display the playlist.
 $allowcontrol = true;
 // The default theme. This should be a name of a directory in ./themes.
-$theme = 'funkymonkey';
+$theme = 'grey';
 // Allow forking when an HTTP request is received. This has advantages
 // and disadvantages. If this experimental option is enabled, it will
 // result in faster responses and load times but more memory usage.
@@ -64,6 +64,9 @@
 // create directories
 @mkdir('./compiled');
 
+// what kind of terminal do we have?
+$use_colors = ( @in_array(@$_SERVER['TERM'], array('linux', 'xterm', 'vt100')) ) ? true : false;
+
 // start up...
 
 status('Starting Greyhound Web Control v0.1a3');
@@ -85,6 +88,18 @@
   exit(0);
 }
 
+status('doing PHP capabilities check');
+if ( !function_exists('pcntl_signal') )
+{
+  warning('System does not support POSIX functions. Termination signals will result in unclean shutdown.');
+}
+
+if ( !function_exists('simplexml_load_file') )
+{
+  warning('Can\'t find support for SimpleXML, which is needed to parse the Amarok playlist file.');
+  burnout('SimpleXML required to continue. You may have an outdated version of PHP; most versions of PHP 5 have SimpleXML built-in. Check your distribution\'s documentation to find out how to enable PHP\'s SimpleXML support.');
+}
+
 status('initializing playlist');
 
 // init playlist object
@@ -99,11 +114,6 @@
 try
 {
   status('starting PhpHttpd');
-  status('doing PHP capabilities check');
-  if ( !function_exists('pcntl_signal') )
-  {
-    warning('System does not support POSIX functions. Termination signals will result in unclean shutdown.');
-  }
   $httpd = new WebServer($ip, $port);
   
   // setup handlers
@@ -143,6 +153,10 @@
 }
 catch( Exception $e )
 {
+  if ( strstr(strval($e), "Could not bind") )
+  {
+    burnout("Could not bind to the port $ip:$port. Is Greyhound already running? Sometimes browsers don't close off their connections until Greyhound has been dead for about a minute, so try starting Greyhound again in roughly 60 seconds. If that doesn't work, type \"killall -9 php\" at a terminal and try starting Greyhound again in 60 seconds.");
+  }
   burnout("Exception caught while running webserver:\n$e");
 }
 
Binary file themes/grey/images/ajax.gif has changed
Binary file themes/grey/images/amarok.gif has changed
Binary file themes/grey/images/next.png has changed
Binary file themes/grey/images/pause.png has changed
Binary file themes/grey/images/play.png has changed
Binary file themes/grey/images/playbar-shadow.gif has changed
Binary file themes/grey/images/playbar.gif has changed
Binary file themes/grey/images/playhead.png has changed
Binary file themes/grey/images/position-empty.png has changed
Binary file themes/grey/images/position-full.png has changed
Binary file themes/grey/images/prev.png has changed
Binary file themes/grey/images/src/playbar-shadow.xcf has changed
Binary file themes/grey/images/src/playbar.xcf has changed
Binary file themes/grey/images/src/playhead.xcf has changed
Binary file themes/grey/images/src/position-empty.xcf has changed
Binary file themes/grey/images/src/position-full.xcf has changed
Binary file themes/grey/images/stop.png has changed
Binary file themes/grey/images/volume.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/themes/grey/playlist.tpl	Fri Aug 15 23:49:00 2008 -0400
@@ -0,0 +1,92 @@
+{**
+ * Template file for default Funky Monkey theme
+ * Web control interface script for Amarok
+ * Written by Dan Fuhry - 2008
+ *
+ * This script is in the public domain. Use it for good, not evil.
+ *}
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html>
+  <head>
+    <title>AmaroK playlist</title>
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <link rel="stylesheet" type="text/css" href="/themes/{$theme|escape}/style.css" />
+    <link rel="favorite icon" type="image/ico" href="/favicon.ico" />
+    <script type="text/javascript">
+    var img_play = '/themes/{$theme|escape}/images/play.png';
+    var img_pause = '/themes/{$theme|escape}/images/pause.png';
+    var img_ajax = '/themes/{$theme|escape}/images/ajax.gif';
+    var class_current = 'current';
+    var allow_control = {if $allow_control}true{else}false{/if};
+    </script>
+    {foreach from=$scripts item=script}
+    <script type="text/javascript" src="/scripts/{$script}"></script>
+    {/foreach}
+  </head>
+  <body>
+    <div id="playbar">
+      <div class="playbar-inner">
+        <div id="playhead"><div id="playhead-filler">&nbsp;</div></div>
+        <div id="playhead-button">&nbsp;</div>
+        <img alt=" " id="ajax_status" style="display: none; float: right; margin: 3px 0;" src="about:blank" />
+        <img alt="AmaroK web control" src="/themes/{$theme|escape}/images/amarok.gif" style="margin-right: 20px;" />
+        {if $allow_control}
+        <a href="#action:prev" onclick="player_action('prev'); return false;"><img alt="&laquo; PrevTrk" src="/themes/{$theme|escape}/images/prev.png" /></a>
+        <a href="#action:play" onclick="player_action('play'); return false;" id="btn_playpause"><img alt="Play" src="/themes/{$theme|escape}/images/play.png" /></a>
+        <a href="#action:stop" onclick="player_action('stop'); return false;"><img alt="Stop" src="/themes/{$theme|escape}/images/stop.png" /></a>
+        <a href="#action:next" onclick="player_action('next'); return false;"><img alt="NextTrk &raquo;" src="/themes/{$theme|escape}/images/next.png" /></a>
+        {/if}
+        <span style="margin-right: 50px;">&nbsp;</span>
+        <span id="playmeter">--:--/--:--</span>
+        {if $allow_control}
+        <span style="margin-right: 50px;">&nbsp;</span>
+        <img hspace="4" alt="Volume: " src="/themes/{$theme|escape}/images/volume.png" />
+        <span id="volume_wrap"><a
+          class="volume_button" href="#volume:0"   onmouseover="volume_over(0);"   onmouseout="volume_out();" id="volbtn_0"   onclick="set_volume(0);   return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:10"  onmouseover="volume_over(10);"  onmouseout="volume_out();" id="volbtn_10"  onclick="set_volume(10);  return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:20"  onmouseover="volume_over(20);"  onmouseout="volume_out();" id="volbtn_20"  onclick="set_volume(20);  return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:30"  onmouseover="volume_over(30);"  onmouseout="volume_out();" id="volbtn_30"  onclick="set_volume(30);  return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:40"  onmouseover="volume_over(40);"  onmouseout="volume_out();" id="volbtn_40"  onclick="set_volume(40);  return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:50"  onmouseover="volume_over(50);"  onmouseout="volume_out();" id="volbtn_50"  onclick="set_volume(50);  return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:60"  onmouseover="volume_over(60);"  onmouseout="volume_out();" id="volbtn_60"  onclick="set_volume(60);  return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:70"  onmouseover="volume_over(70);"  onmouseout="volume_out();" id="volbtn_70"  onclick="set_volume(70);  return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:80"  onmouseover="volume_over(80);"  onmouseout="volume_out();" id="volbtn_80"  onclick="set_volume(80);  return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:90"  onmouseover="volume_over(90);"  onmouseout="volume_out();" id="volbtn_90"  onclick="set_volume(90);  return false;">&nbsp;</a><a
+          class="volume_button" href="#volume:100" onmouseover="volume_over(100);" onmouseout="volume_out();" id="volbtn_100" onclick="set_volume(100); return false;">&nbsp;</a>
+        </span>
+        {/if}
+      </div>
+    </div>
+    <div class="tblholder" id="playlist">
+      <table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
+        <tr>
+          <th>TrackNo</th>
+          <th>Track</th>
+          <th>Artist</th>
+          <th>Album</th>
+          <th>Length</th>
+        </tr>
+        {foreach key=tid item=track from=$playlist}
+        {strip}
+        <tr class="{cycle values="row1,row2"}{if $active == $tid} current{/if}" id="track_{$tid}" amarok:length_sec="{$track.length_int}">
+          <td style="text-align: center;">{$tid+1}</td>
+          <td>
+            <a class="tracklink" href="#action:jump;tid:{$tid}" onclick="jump_to_song({$tid}); return false;">
+              {$track.title|escape}
+            </a>
+          </td>
+          <td>{$track.artist|escape}</td>
+          <td>{$track.album|escape}</td>
+          <td style="text-align: center;">{$track.length|escape}</td>
+        </tr>
+        {/strip}
+        {/foreach}
+      </table>
+    </div>
+    <div class="poweredby">
+      Powered by <a onclick="window.open(this.href); return false;" href="http://greyhound.enanocms.org/">Greyhound</a>
+    </div>
+  </body>
+</html>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/themes/grey/style.css	Fri Aug 15 23:49:00 2008 -0400
@@ -0,0 +1,146 @@
+/**
+ * Based upon the AmaroK WebControl interface by:
+ *    Jonas Christian Drewsen ( kde at xspect dot dk )
+ *    André Kelpe ( fs111 at web dot de )
+ *    Peter C. Ndikuwera ( pndiku at gmail dot com )
+ */
+
+body {
+  font-family: sans-serif;
+  background-color: #262626;
+  color: #ffffff;
+  padding: 0 8px;
+  background-image: url(images/playbar-shadow.gif);
+  background-repeat: repeat-x;
+}
+
+div.tblholder {
+  padding: 1px;
+  background-color: #b0b0b0;
+  border: 1px solid #000000;
+}
+
+div.tblholder table {
+  background-color: #606060;
+}
+
+tr th {
+  background-color: #505050;
+}
+
+tr.row1 td {
+  background-color: #383838;
+}
+
+tr.row2 td {
+  background-color: #424242;
+}
+
+tr.current td {
+  background-color: #303030;
+  color: #ffff00;
+}
+
+div#playbar {
+  position: fixed;
+  top: 0;
+  left: 0; 
+  width: 100%;
+  height: 32px;
+  padding: 0;
+  margin: 0;
+  background-image: url(images/playbar.gif);
+  background-repeat: repeat-x;
+  background-color: #383f61;
+  border-bottom: 1px solid #000000;
+}
+
+div.playbar-inner {
+  padding: 5px;
+  line-height: 22px;
+}
+
+a img {
+  border-width: 0;
+}
+
+div#playlist {
+  margin-top: 48px;
+}
+
+a.tracklink {
+  text-decoration: none;
+  color: white;
+}
+
+div#playlist tr.current a.tracklink {
+  color: #ffff00;
+}
+
+span#playmeter, span#volume_wrap {
+  position: relative;
+  top: -7px;
+}
+
+a.volume_button {
+  padding-right: 10px;
+  margin-right: 1px;
+  background-color: #262626;
+  border: 1px solid #909090;
+  text-decoration: none;
+}
+
+a.volume_button_active {
+  background-color: #404040;
+  border-color: #d0d0d0;
+}
+
+/* Position slider (playhead) */
+
+div#playhead {
+  background-image: url(images/position-empty.png);
+  width: 250px;
+  background-repeat: no-repeat;
+  background-position: center center;
+  float: right;
+  margin-left: 10px;
+}
+
+div#playhead-filler {
+  background-image: url(images/position-full.png);
+  width: 150px;
+  background-repeat: no-repeat;
+  background-position: left center;
+}
+
+div#playhead-button {
+  background-image: url(images/playhead.png);
+  width: 16px;
+  height: 16px;
+  font-size: 1px;
+  position: absolute;
+  background-repeat: no-repeat;
+  background-position: center center;
+}
+
+/* The list of colors that will be cycled through as playback takes place */
+tr.pulsar0 td { background-color: #303030; }
+tr.pulsar1 td { background-color: #333333; }
+tr.pulsar2 td { background-color: #363636; }
+tr.pulsar3 td { background-color: #393939; }
+tr.pulsar4 td { background-color: #3c3c3c; }
+tr.pulsar5 td { background-color: #3f3f3f; }
+tr.pulsar6 td { background-color: #424242; }
+tr.pulsar7 td { background-color: #454545; }
+tr.pulsar8 td { background-color: #484848; }
+tr.pulsar9 td { background-color: #4b4b4b; }
+
+div.poweredby {
+  font-size: smaller;
+  text-align: center;
+  margin: 10px 0;
+}
+
+div.poweredby a {
+  color: #57608a;
+}
--- a/webserver.php	Fri Aug 15 23:31:37 2008 -0400
+++ b/webserver.php	Fri Aug 15 23:49:00 2008 -0400
@@ -18,7 +18,7 @@
  * @const string
  */
 
-define('HTTPD_VERSION', '0.1b1');
+define('HTTPD_VERSION', '0.1b4');
 
 /**
  * Length of keep-alive connections
@@ -33,7 +33,7 @@
 
 define('HTTPD_ICON_SCRIPT', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGSSURBVCjPVVFNSwJhEF78Ad79Cf6PvXQRsotUlzKICosuRYmR2RJR0KE6lBFFZVEbpFBSqKu2rum6llFS9HHI4iUhT153n6ZtIWMOM+/MM88z7wwH7s9Ub16SJcnbmrNcxVm2q7Z8/QPvEOtntpj92NkCqITLepEpjix7xQtiLOoQ2b6+E7YAN/5nfOEJ2WbKqOIOJ4bYVMEQx4LfBBQDsvFMhUcCVU1/CxVXmDBGA5ZETrhDCQVcYAPbyEJBhvrnBVPiSpNr6cYDNCQwo4zzU/ySckkgDYuNuVpI42T9k4gLKGMPs/xPzzovQiY2hQYe0jlJfyNNhTqiWDYBq/wBMcSRpnyPzu1oS7WtxjVBSthU1vgVksiQ3Dn6Gp5ah2YOKQo5GiuHPA6xT1EKpxQNCNYejgIR457KKio0S56YckjSa9jo//3mrj+BV0QQagqGTOo+Y7gZIf1puP3WHoLhEb2PjTlCTCWGXtbp8DCX3hZuOdaIc9A+aQvWk4ihq95p67a7nP+u+Ws+r0dql9z/zv0NCYhdCPKZ7oYAAAAASUVORK5CYII=');
 define('HTTPD_ICON_FOLDER', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGrSURBVDjLxZO7ihRBFIa/6u0ZW7GHBUV0UQQTZzd3QdhMQxOfwMRXEANBMNQX0MzAzFAwEzHwARbNFDdwEd31Mj3X7a6uOr9BtzNjYjKBJ6nicP7v3KqcJFaxhBVtZUAK8OHlld2st7Xl3DJPVONP+zEUV4HqL5UDYHr5xvuQAjgl/Qs7TzvOOVAjxjlC+ePSwe6DfbVegLVuT4r14eTr6zvA8xSAoBLzx6pvj4l+DZIezuVkG9fY2H7YRQIMZIBwycmzH1/s3F8AapfIPNF3kQk7+kw9PWBy+IZOdg5Ug3mkAATy/t0usovzGeCUWTjCz0B+Sj0ekfdvkZ3abBv+U4GaCtJ1iEm6ANQJ6fEzrG/engcKw/wXQvEKxSEKQxRGKE7Izt+DSiwBJMUSm71rguMYhQKrBygOIRStf4TiFFRBvbRGKiQLWP29yRSHKBTtfdBmHs0BUpgvtgF4yRFR+NUKi0XZcYjCeCG2smkzLAHkbRBmP0/Uk26O5YnUActBp1GsAI+S5nRJJJal5K1aAMrq0d6Tm9uI6zjyf75dAe6tx/SsWeD//o2/Ab6IH3/h25pOAAAAAElFTkSuQmCC');
-define('HTTPD_ICON_FILE', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAINSURBVBgZBcG/r55zGAfg6/4+z3va01NHlYgzEfE7MdCIGISFgS4Gk8ViYyM2Mdlsko4GSf8Do0FLRCIkghhYJA3aVBtEz3nP89wf11VJvPDepdd390+8Nso5nESBQoq0pfvXm9fzWf19453LF85vASqJlz748vInb517dIw6EyYBIIG49u+xi9/c9MdvR//99MPPZ7+4cP4IZhhTPbwzT2d+vGoaVRRp1rRliVvHq+cfvM3TD82+7mun0o/ceO7NT+/4/KOXjwZU1ekk0840bAZzMQ2mooqh0A72d5x/6sB9D5zYnff3PoYBoWBgFKPKqDKqjCpjKr//dcu9p489dra88cydps30KswACfNEKanSaxhlntjJ8Mv12Paie+vZ+0+oeSwwQ0Iw1xAR1CiFNJkGO4wu3ZMY1AAzBI0qSgmCNJsJUEOtJSMaCTBDLyQ0CknAGOgyTyFFiLI2awMzdEcSQgSAAKVUmAeNkxvWJWCGtVlDmgYQ0GFtgg4pNtOwbBcwQy/Rife/2yrRRVI0qYCEBly8Z+P4qMEMy7JaVw72N568e+iwhrXoECQkfH91kY7jwwXMsBx1L93ZruqrK6uuiAIdSnTIKKPLPFcvay8ww/Hh+ufeznTXu49v95IMoQG3784gYXdTqvRmqn/Wpa/ADFX58MW3L71SVU9ETgEIQQQIOOzub+fhIvwPRDgeVjWDahIAAAAASUVORK5CYII=');
+define('HTTPD_ICON_FILE',   'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAINSURBVBgZBcG/r55zGAfg6/4+z3va01NHlYgzEfE7MdCIGISFgS4Gk8ViYyM2Mdlsko4GSf8Do0FLRCIkghhYJA3aVBtEz3nP89wf11VJvPDepdd390+8Nso5nESBQoq0pfvXm9fzWf19453LF85vASqJlz748vInb517dIw6EyYBIIG49u+xi9/c9MdvR//99MPPZ7+4cP4IZhhTPbwzT2d+vGoaVRRp1rRliVvHq+cfvM3TD82+7mun0o/ceO7NT+/4/KOXjwZU1ekk0840bAZzMQ2mooqh0A72d5x/6sB9D5zYnff3PoYBoWBgFKPKqDKqjCpjKr//dcu9p489dra88cydps30KswACfNEKanSaxhlntjJ8Mv12Paie+vZ+0+oeSwwQ0Iw1xAR1CiFNJkGO4wu3ZMY1AAzBI0qSgmCNJsJUEOtJSMaCTBDLyQ0CknAGOgyTyFFiLI2awMzdEcSQgSAAKVUmAeNkxvWJWCGtVlDmgYQ0GFtgg4pNtOwbBcwQy/Rife/2yrRRVI0qYCEBly8Z+P4qMEMy7JaVw72N568e+iwhrXoECQkfH91kY7jwwXMsBx1L93ZruqrK6uuiAIdSnTIKKPLPFcvay8ww/Hh+ufeznTXu49v95IMoQG3784gYXdTqvRmqn/Wpa/ADFX58MW3L71SVU9ETgEIQQQIOOzub+fhIvwPRDgeVjWDahIAAAAASUVORK5CYII=');
 
 /**
  * Simple but full-featured embedded web server written in PHP.
@@ -404,6 +404,13 @@
       
       // parse request
       $client_headers = trim($client_headers);
+      
+      if ( isset($last_finish_time) && empty($client_headers) && defined('HTTPD_WS_CHILD') && $last_finish_time + HTTPD_KEEP_ALIVE_TIMEOUT < microtime(true) )
+      {
+        status('[debug] keep-alive connection timed out (checkpoint 2)');
+        continue; // will jump back to the start of the loop and kill the child process
+      }
+      
       $client_headers = explode("\n", $client_headers);
       
       // first line
@@ -697,7 +704,7 @@
         // if ( defined('HTTPD_WS_CHILD') )
         //   status('Continuing connection');
         // @socket_write($remote, "\r\n\r\n");
-        $last_finish_time = time();
+        $last_finish_time = microtime(true);
       }
       else
       {