Added Offline Mode - automatically turned on and off based on connectivity to server. Version bumped to 0.1a5. default tip
authorDan
Fri, 12 Jun 2009 13:50:13 -0400
changeset 78 08f8a72b1f7b
parent 77 e5f1f45ea7e2
Added Offline Mode - automatically turned on and off based on connectivity to server. Version bumped to 0.1a5.
greyhound.php
offlinemode.png
offlinemodelarge.png
scripts/ajax.js
scripts/position.js
scripts/volume.js
themes/grey/config.tpl
themes/grey/login.css
themes/grey/playlist.tpl
themes/grey/style.css
uiconfig.php
--- a/greyhound.php	Fri Jun 12 13:49:22 2009 -0400
+++ b/greyhound.php	Fri Jun 12 13:50:13 2009 -0400
@@ -12,7 +12,7 @@
  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
  */
 
-define('GREY_VERSION', '0.1a4');
+define('GREY_VERSION', '0.1a5');
 
 // Try to trap termination signals to cleanly close the socket when needed
 // AmaroK sends a SIGTERM when it is shut down or the user requests to stop
@@ -25,7 +25,6 @@
   // trap SIGTERM
   pcntl_signal(SIGTERM, 'sigterm');
   pcntl_signal(SIGINT,  'sigterm');
-  pcntl_signal(SIGHUP, 'handle_refresh_signal');
 }
 
 @ini_set('display_errors', 'on');
@@ -175,7 +174,10 @@
   $httpd->add_handler('apple-touch-icon.png', 'file',     GREY_ROOT . '/apple-touch-icon.png');
   $httpd->add_handler('spacer.gif',           'file',     GREY_ROOT . '/spacer.gif');
   $httpd->add_handler('trans80.png',          'file',     GREY_ROOT . '/trans80.png');
+  $httpd->add_handler('offlinemode.png',      'file',     GREY_ROOT . '/offlinemode.png');
+  $httpd->add_handler('offlinemodelarge.png', 'file',     GREY_ROOT . '/offlinemodelarge.png');
   $httpd->threader->ipc_register('reloadconfig', 'grey_reload_config');
+  $httpd->threader->ipc_register('reloadplaylist', 'rebuild_playlist_ipc');
   // load all themes if forking is enabled
   // Themes are loaded when the playlist is requested. This is fine for
   // single-threaded operation, but if the playlist handler is only loaded
@@ -212,14 +214,3 @@
   burnout("Exception caught while running webserver:\n$e");
 }
 
-function handle_refresh_signal()
-{
-  global $httpd;
-  if ( !is_object($httpd) )
-    // we're not serving yet.
-    return false;
-    
-  // we've got an httpd instance; rebuild the playlist
-  rebuild_playlist();
-}
-
Binary file offlinemode.png has changed
Binary file offlinemodelarge.png has changed
--- a/scripts/ajax.js	Fri Jun 12 13:49:22 2009 -0400
+++ b/scripts/ajax.js	Fri Jun 12 13:50:13 2009 -0400
@@ -12,7 +12,7 @@
  */
 
 var ajax;
-var is_playing = false, current_track = -1, current_track_length, current_track_pos, ct_advance_timeout = false, ct_counter = false, playlist_md5 = false, first_load = true;
+var is_playing = false, current_track = -1, current_track_length, current_track_pos, ct_advance_timeout = false, ct_counter = false, playlist_md5 = false, first_load = true, offline_mode = false;
 
 var onload_hooks = new Array();
 
@@ -47,11 +47,74 @@
   }
 }
 
+// preload "offline mode" large icon
+var preload = ['/offlinemodelarge.png', 'trans80.png']
+for ( var i = 0; i < preload.length; i++ )
+{
+  var img = new Image();
+  img.src = preload[i];
+}
+
+function offline_mode_on()
+{
+  unsetAjaxLoading();
+  document.getElementById('offlinemode').style.display = 'block';
+  offline_mode = true;
+}
+
+function offline_mode_off()
+{
+  document.getElementById('offlinemode').style.display = 'none';
+  offline_mode = false;
+}
+
+function verify_online()
+{
+  if ( !offline_mode )
+    return true;
+  
+  flash_offline();
+  return false;
+}
+
+function flash_offline()
+{
+  if ( document.getElementById('offlinebox') )
+    return false;
+  
+  var box = document.createElement('div');
+  $(box)
+    .css('position', 'absolute')
+    .css('padding', '50px 80px')
+    .css('background-image', 'url(/trans80.png)')
+    .css('color', 'black')
+    .css('text-align', 'center')
+    .attr('id', 'offlinebox')
+    .opacity(0);
+  
+  var image = document.createElement('img');
+  image.src = '/offlinemodelarge.png';
+  box.appendChild(image);
+  
+  document.body.appendChild(box);
+  
+  $(box)
+    .css('top',  (( getHeight() / 2 ) - ( $(box).Height() / 2 ) + getScrollOffset()) + 'px')
+    .css('left', (( $(document.body).Width() / 2 ) - ( $(box).Width() / 2 )) + 'px');
+  
+  $(box).fadeIn(250);
+  window.setTimeout(function()
+    {
+      $(box).fadeOut(250);
+      window.setTimeout(function()
+        {
+          document.body.removeChild(box);
+        }, 250);
+    }, 1000);
+}
+
 function ajaxGet(uri, f)
 {
-  if ( ajax_panicked )
-    return false;
-  
   if (window.XMLHttpRequest)
   {
     ajax = new XMLHttpRequest();
@@ -75,9 +138,6 @@
 
 function ajaxPost(uri, parms, f)
 {
-  if ( ajax_panicked )
-    return false;
-  
   if (window.XMLHttpRequest)
   {
     ajax = new XMLHttpRequest();
@@ -127,8 +187,9 @@
   setAjaxLoading();
   ajaxGet('/action.json/refresh', function()
     {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
+      if ( ajax.readyState == 4 && ajax.status == 200 && ( (window.location.hash != '#offlinemode' && !first_load ) || first_load ) )
       {
+        offline_mode_off();
         unsetAjaxLoading();
         var response = (' ' + ajax.responseText).substr(1);
         // quickie JSON parser :)
@@ -144,123 +205,111 @@
           }
         }
         playlist_md5 = response.playlist_hash;
-        // update track number
-        if ( response.current_track != current_track )
-        {
-          var ot_id = 'track_' + current_track;
-          var nt_id = 'track_' + response.current_track;
-          current_track = response.current_track;
-          if ( $(ot_id).hasClass('current') )
-          {
-            $(ot_id).rmClass('current');
-          }
-          if ( ! $(nt_id).hasClass('current') )
-          {
-            $(nt_id).addClass('current');
-          }
-          pulsar_reset();
-        }
-        // update playing status
-        is_playing = response.is_playing;
-        if ( allow_control )
-        {
-          var img = $('btn_playpause').object.getElementsByTagName('img')[0];
-          if ( is_playing )
-          {
-            img.src = img_pause;
-          }
-          else
-          {
-            img.src = img_play;
-          }
-        }
-        // update volume
-        if ( response.volume != current_volume )
-        {
-          set_volume_fill(response.volume);
-          current_volume = response.volume;
-        }
-        // auto-refresh on track advance
-        if ( ct_advance_timeout )
-        {
-          clearTimeout(ct_advance_timeout);
-        }
-        // countdown/up timer
-        var time_remaining = response.current_track_length - response.current_track_pos;
-        current_track_length = response.current_track_length;
-        current_track_pos = response.current_track_pos;
-        if ( ct_counter )
-          clearInterval(ct_counter);
-        update_clock();
         
-        // set page title
-        updateTitle(response.current_track_artist, response.current_track_album, response.current_track_title);
-        
-        // if not playing, set the position slider to zero
-        if ( !is_playing && !response.is_paused )
-        {
-          posslide_set_position(0);
-        }
-        
-        // set advance timer
-        if ( is_playing && time_remaining > 0 )
-        {
-          ct_advance_timeout = setTimeout(refresh_playlist, ( 1000 * time_remaining ));
-          ct_counter = setInterval(update_clock, 1000);
-        }
-        if ( first_load )
-        {
-          first_load = false;
-          jump_current_track();
-        }
+        update_timers(response);
       }
-      else if ( ajax.readyState == 4 && ajax.status != 200 )
+      else if ( ajax.readyState == 4 && ( ajax.status != 200 || window.location.hash == '#offlinemode' ) )
       {
-        ajax_panic();
-        console.debug(ajax);
+        if ( !offline_mode )
+          ajax_panic();
+        else
+          unsetAjaxLoading();
       }
     });
 }
 
-var ajax_panicked = false;
+function update_timers(response)
+{
+  // update track number
+  if ( response.current_track != current_track )
+  {
+    var ot_id = 'track_' + current_track;
+    var nt_id = 'track_' + response.current_track;
+    current_track = response.current_track;
+    if ( $(ot_id).hasClass('current') )
+    {
+      $(ot_id).rmClass('current');
+    }
+    if ( ! $(nt_id).hasClass('current') )
+    {
+      $(nt_id).addClass('current');
+    }
+    pulsar_reset();
+  }
+  // update playing status
+  is_playing = response.is_playing;
+  if ( allow_control )
+  {
+    var img = $('btn_playpause').object.getElementsByTagName('img')[0];
+    if ( is_playing )
+    {
+      img.src = img_pause;
+    }
+    else
+    {
+      img.src = img_play;
+    }
+  }
+  // update volume
+  if ( response.volume != current_volume )
+  {
+    set_volume_fill(response.volume);
+    current_volume = response.volume;
+  }
+  // auto-refresh on track advance
+  if ( ct_advance_timeout )
+  {
+    clearTimeout(ct_advance_timeout);
+  }
+  // countdown/up timer
+  var time_remaining = response.current_track_length - response.current_track_pos;
+  current_track_length = response.current_track_length;
+  current_track_pos = response.current_track_pos;
+  if ( ct_counter )
+    clearInterval(ct_counter);
+  update_clock();
+  
+  // set page title
+  updateTitle(response.current_track_artist, response.current_track_album, response.current_track_title);
+  
+  // if not playing, set the position slider to zero
+  if ( !is_playing && !response.is_paused )
+  {
+    posslide_set_position(0);
+  }
+  
+  // set advance timer
+  if ( is_playing && time_remaining > 0 )
+  {
+    ct_advance_timeout = setTimeout(refresh_playlist, ( 1000 * time_remaining ));
+    ct_counter = setInterval(update_clock, 1000);
+  }
+  if ( first_load )
+  {
+    first_load = false;
+    if ( window.iPhone )
+    {
+      setTimeout('jump_current_track();', 1000);
+    }
+    else
+    {
+      jump_current_track();
+    }
+  }
+}
 
 function ajax_panic()
 {
-  // set error flag
-  ajax_panicked = true;
-  
-  // scroll to the top
-  window.scroll(0, 0);
-  
-  // stop events
-  pulsar_reset();
-  window.clearInterval(pl_refresh_id);
-  if ( ct_counter )
-    window.clearInterval(ct_counter);
-  if ( pulsar_interval_id )
-    window.clearInterval(pulsar_interval_id);
-  
-  // show error message
-  var floater = document.createElement('div');
-  floater.style.backgroundColor = '#ffffff';
-  floater.style.opacity = 0.7;
-  floater.style.filter = 'alpha(opacity=70)';
-  floater.style.textAlign = 'center';
-  floater.style.paddingTop = '120px';
-  floater.style.position = 'fixed';
-  floater.style.zIndex = '999';
-  floater.style.width = '100%';
-  floater.style.height = '100%';
-  floater.style.top = '0px';
-  floater.style.left = '0px';
-  floater.style.color = '#000000';
-  floater.innerHTML = 'There was a problem with a refresh request to the server. Please reload the page.';
-  var body = document.getElementsByTagName('body')[0];
-  body.appendChild(floater);
+  offline_mode_on();
 }
 
 function player_action(action)
 {
+  if ( !verify_online() )
+  {
+    return false;
+  }
+  
   var act2 = action;
   setAjaxLoading();
   ajaxGet('/action.json/' + action, function()
@@ -275,6 +324,11 @@
 
 function jump_to_song(tid)
 {
+  if ( !verify_online() )
+  {
+    return false;
+  }
+  
   setAjaxLoading();
   if ( tid == current_track )
     return false;
@@ -345,8 +399,32 @@
     });
 }
 
+function offline_advance_track()
+{
+  var new_track = current_track + 1;
+  if ( !document.getElementById('track_' + new_track) )
+    new_track = 0;
+  
+  update_timers({
+    is_playing: is_playing,
+    is_paused: false,
+    volume: current_volume,
+    current_track: new_track,
+    current_track_length: document.getElementById('track_' + new_track).getAttribute('amarok:length_sec'),
+    current_track_pos: 0,
+    current_track_artist: 'FIXME artist',
+    current_track_album: 'FIXME album',
+    current_track_title: 'FIXME title'
+  })
+}
+
 function update_clock()
 {
+  if ( offline_mode && current_track_pos > current_track_length )
+  {
+    offline_advance_track();
+  }
+  
   posslide_set_position((100 * (current_track_pos / current_track_length)));
   var str = secs_to_string(current_track_pos) + '/' + secs_to_string(current_track_length);
   $('playmeter').object.innerHTML = str;
--- a/scripts/position.js	Fri Jun 12 13:49:22 2009 -0400
+++ b/scripts/position.js	Fri Jun 12 13:50:13 2009 -0400
@@ -41,6 +41,12 @@
 var posslide_handle_dragend = function(x, y)
 {
   pos_in_drag = false;
+  if ( !verify_online() )
+  {
+    posslide_set_position(current_track_pos);
+    return false;
+  }
+  
   var inner = document.getElementById('playhead-filler');
   var base = document.getElementById('playhead');
   var multiplier = $(base).Width() - 13;
@@ -51,6 +57,9 @@
 
 var posslide_handle_click = function(e)
 {
+  if ( !verify_online() )
+    return false;
+  
   e = Drag.fixE(e);
   var base = document.getElementById('playhead');
   var val = e.clientX - $(base).Left();
--- a/scripts/volume.js	Fri Jun 12 13:49:22 2009 -0400
+++ b/scripts/volume.js	Fri Jun 12 13:50:13 2009 -0400
@@ -46,6 +46,11 @@
 
 function set_volume(level)
 {
+  if ( !verify_online() )
+  {
+    return false;
+  }
+  
   setAjaxLoading();
   if ( level == current_volume )
     return false;
@@ -125,6 +130,32 @@
       }
     }
   }
+  if ( window.iPhone )
+  {
+    // iPhone Safari can't do keyup events
+    field.onblur = function()
+    {
+      if ( this.value == '' )
+      {
+        $(this.parentNode).fadeOut(250, function(o)
+          {
+            o.parentNode.removeChild(o);
+          });
+      }
+      else if ( !this.value.match(/^[0-9]+$/) )
+      {
+        $(this.parentNode).insertBR().insertText('please enter a number');
+      }
+      else
+      {
+        set_volume(parseInt(this.value));
+        $(this.parentNode).fadeOut(250, function(o)
+          {
+            o.parentNode.removeChild(o);
+          });
+      }
+    }
+  }
 }
 
 addOnloadHook(function()
--- a/themes/grey/config.tpl	Fri Jun 12 13:49:22 2009 -0400
+++ b/themes/grey/config.tpl	Fri Jun 12 13:50:13 2009 -0400
@@ -19,7 +19,7 @@
     <link rel="favorite icon" type="image/ico" href="/favicon.ico" />
     <script type="text/javascript" src="/scripts/config.js"></script>
   </head>
-  <body>
+  <body style="width: 80%; margin: 0 auto; min-width: 900px;">
   
     <div class="greylogo"></div>
     <a class="backlink" href="/index">&laquo; Return to playlist</a>
@@ -45,6 +45,14 @@
           </tr>
           {/if}
           
+          {if $message}
+          <tr>
+            <td class="row3" colspan="2">
+              <p class="success">{$message|nl2br}</p>
+            </td>
+          </tr>
+          {/if}
+          
           <!-- field - current config pass -->
           {if $needpass}
           <tr>
@@ -139,6 +147,33 @@
             </td>
           </tr>
           
+          <!-- section - diagnostics -->
+          
+          <tr>
+            <th colspan="2">
+              Diagnostics
+            </th>
+          </tr>
+          
+          <!-- clear artwork -->
+          <tr>
+            <td class="row2" style="text-align: center;">
+              <a href="/config/rebuild/artwork" class="backlink inline">Clear artwork cache</a>
+            </td>
+            <td class="row1">
+              Use this to rebuild Greyhound's sprites it generates of your album artwork.
+            </td>
+          </tr>
+          
+          <tr>
+            <td class="row2" style="text-align: center;">
+              <a href="/config/rebuild/playlist" class="backlink inline">Refresh playlist</a>
+            </td>
+            <td class="row1">
+              If you changed your playlist but the changes did not show up in Greyhound, use this to force Greyhound to re-read the playlist.
+            </td>
+          </tr>
+          
           <!-- section - auth config -->
           <tr>
             <th colspan="2">
--- a/themes/grey/login.css	Fri Jun 12 13:49:22 2009 -0400
+++ b/themes/grey/login.css	Fri Jun 12 13:50:13 2009 -0400
@@ -13,7 +13,7 @@
 
 body {
   font-family: sans-serif;
-  background-color: #363636;
+  background-color: #262626;
   color: #ffffff;
   padding: 0;
 }
--- a/themes/grey/playlist.tpl	Fri Jun 12 13:49:22 2009 -0400
+++ b/themes/grey/playlist.tpl	Fri Jun 12 13:50:13 2009 -0400
@@ -33,6 +33,7 @@
       <div class="playbar-inner">
         <div id="playhead"><div id="playhead-filler">&nbsp;</div></div>
         <div id="playhead-button">&nbsp;</div>
+        <div id="offlinemode"><img title="No network connection; running in offline mode" src="/offlinemode.png" width="22" height="22" /></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/themes/grey/style.css	Fri Jun 12 13:49:22 2009 -0400
+++ b/themes/grey/style.css	Fri Jun 12 13:50:13 2009 -0400
@@ -104,6 +104,12 @@
   border-color: #d0d0d0;
 }
 
+div#offlinemode {
+  float: right;
+  margin-left: 4px;
+  display: none;
+}
+
 /* Position slider (playhead) */
 
 div#playhead {
@@ -186,6 +192,21 @@
   background-color: #505050;
 }
 
+a.backlink.inline {
+  max-width: none;
+  background-color: #282828;
+  margin: 5px 10px;
+}
+
+a.backlink.inline:hover {
+  background-color: #303030;
+}
+
+span.clearer {
+  display: block;
+  clear: both;
+}
+
 div.greylogo {
   width: 120px;
   height: 33px;
@@ -198,3 +219,4 @@
 div.clearer {
   clear: both;
 }
+
--- a/uiconfig.php	Fri Jun 12 13:49:22 2009 -0400
+++ b/uiconfig.php	Fri Jun 12 13:50:13 2009 -0400
@@ -90,6 +90,7 @@
       {
         $auth_data = array();
         $auth_changed = true;
+        $use_auth = false;
       } 
     }
     if ( isset($_POST['users_add']) )
@@ -188,6 +189,36 @@
     }
   }
   
+  $message = false;
+  
+  // Rebuild options
+  $pathinfo = @substr(@substr($_SERVER['REQUEST_URI'], 1), @strpos(@substr($_SERVER['REQUEST_URI'], 1), '/')+1);
+  switch($pathinfo)
+  {
+    case 'rebuild/artwork':
+      global $amarok_home;
+      $artwork_dir = "$amarok_home/albumcovers";
+      
+      if ( $dp = opendir($artwork_dir) )
+      {
+        while ( $dh = @readdir($dp) )
+        {
+          if ( preg_match('/^collage_[0-9]+\.(?:map|png)$/', $dh) )
+          {
+            unlink("$artwork_dir/$dh");
+          }
+        }
+        closedir($dp);
+      }
+      
+      $message = 'Successfully cleared album art cache';
+      break;
+    case 'rebuild/playlist':
+      rebuild_playlist(true);
+      $message = 'Successfully rebuilt playlist cache';
+      break;
+  }
+  
   global $theme;
   $iphone = ( ( strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') ||
        strpos($_SERVER['HTTP_USER_AGENT'], 'iPod') ||
@@ -196,12 +227,13 @@
        && !isset($_GET['f'])
        );
   $theme_id = ( $iphone ) ? 'iphone' : $theme;
+  
   $smarty = load_theme($theme_id);
-  
   $smarty->assign('theme', $theme_id);
   $smarty->assign('greyhound_version', GREY_VERSION);
   $smarty->assign('tried', $tried);
   $smarty->assign('success', $success);
+  $smarty->assign('message', $message);
   $smarty->assign('needpass', $needpass);
   $smarty->assign('use_auth', $use_auth);
   $smarty->assign('public', $GLOBALS['public']);