Sunday, June 9, 2013

Bash Download of M3U MP3 Playlist


I have a list of MP3’s I regularly listen to which are all pulled from a website via a “*.m3u” list.  My wife wanted the songs, but she wants to listen to them on her IPhone.  So I whipped up this Bash script to take a “*.m3u” file as an argument, and pull all the tracks in the playlist down to the current directory.

If you look at the m3u playlist in Itunes, you may see some streaming files that are of duration “continuous”.  Those will not download correctly using this method.

#!/bin/bash

# Takes a list of MP3's (generally an m3u file) and downloads all to current directory
# www.klugedeforce.com
#
if [ $# != 1 ] ; then
  echo "usage $0 mp3_list_file.txt"
  echo "file list can have any extension, but should contain list of files one per line:"
  echo ""
  echo "http://downloads.hpmgl.net/mp3song_file1.mp3"
  echo "http://downloads.hpmgl.net/song_file2.mp3"
  echo "http://downloads.hpmgl.net/song_file3.mp3"

elif [ -e $1 ] ; then
   IFS=$'\r\n'
   for line in $(cat $1); do
       trimmed_line="$(echo ${line} | sed 's/^[ \t\r\n]*//g' | sed 's/[ \t\r\n]*$//g')"
       # trimmed_line=$line | sed 's/^[ \t\r\n]*//g' | sed 's/[ \t\r\n]*$/b/g'
       outfile=${trimmed_line##http://*/}
       if [ -n "$outfile" ] ; then
       `curl "${trimmed_line}" > "${outfile}"`;
          echo $outfile;
       fi
   done
else
   echo "Filename $1 not found"
fi          


Wednesday, June 5, 2013

Magento XMLRPC Credit Memo List with Filter


Magento XMLRPC Credit Memo List with Filter

There are some examples of using credit memos on the Magento API documentation web page:


But the only examples there are for the SOAP implementation.  However, the Magento API also supports the XMLRPC protocol. 

The following code works as is for the Zend_XmlRpc_Client, but the key thing is the parameter formatting, which is not platform specific.

$params = array( array(‘order_id’ => ‘12345678’),
                  array(‘whatever_field’ => $target_val),
                  array(‘another_field’ => $another_val),
          );

$this->client = new Zend_XmlRpc_Client($apiUrl);

$session = $this->client->call('login', array($this->apiUser, $this->apiKey));

$response = $this->client->call('call',
                 array($session, 'sales_order_creditmemo.list', $params));

Saturday, April 20, 2013

Bypass Kinetico Plus Vx Gx MACguard PureMometer





Luckily, it has a rubber o-ring in it, so it’s very easy to put back provided you don’t do something foolish and damage it.  After studying it a bit, I came to the conclusion that there must be something in the center hole of the final filter cartridge which pushes the plastic pin from the PureMometer up.  I tried putting a wooden skewer down the center hole of the cartridge.  I hit some slight resistance, and then I punched through.  When I put the filter back on, instead of a trickle, we had virtually nothing.  So it seems I made it worse.

The mechanism looks like this internally:


I think this thing works as follows.  As the water comes in from the tank, it passes over the water wheel.  The water wheel has a gear on it which hits the large gear on the top of the nearest gear stack.  That in turn has a smaller gear on it that hits the top large gear on the opposite gear stack.  And they ping pong back and forth downwards over three sets of gears.  Finally the bottom gear on the opposite stack drives the white gear in the foreground of the picture and spins the blue PureMometer valve around…very, very slowly.  The blue PureMometer valve is spring loaded.  When nothing is pressing up on it, it comes down and cuts the water flow off nearly completely. 

So the blue PureMometer pin is spring loaded and it turns.  The bottom of it is also fluted and shaped a bit like a drill bit.  I think it is pressing down on something or some substance in the center hole of the filter, and it slowly drills through it over time.  Once it gets through, the spring pushes the blue pin all the way down and no more water.

So what we need is something to press that blue pin back up into the housing and allow the water to flow.  After looking around for a bit, I came up with a simple paperclip bent as follows:



I then pressed it down into the top of the filter as shown here:





I was concerned that drill end of the spinning PureMometer pin might catch on the paperclip, and with that massive gear reduction, it could easily snap off that rather spindly pin.  So I also pulled the center gear out so the water wheel motion could not transfer to the blue pin. 



Now the blue pin is pushed up by the paperclip, and it is no longer able to spin.  I think that with a new cartridge, even in the absence of the paperclip, removing the gear should keep the drill bit from spinning, and the cartridge wouldn’t wear out, but because I don’t have the new cartridge, I can’t inspect it closely enough to be sure.  As you can see, the paperclip fully resets the pin.



Here is a shot of the faucet.  It’s running like a champ.



So I successfully bypassed it.  Now what about the water quality?  It stands to reason that the filter is designed to clean 500 gallons of pretty much any household water.  We already have a salt-based whole house Kinetico softener/filter system ahead of this filter anyway.  And we’ve got fairly decent city water.  Without the whole house filter, the water smells of chlorine, but otherwise, it’s fine.  And it’s just statistically improbable we’d have the worst case water in the nation.  We likely have fairly average water.  If you figure this filter can handle 500 gallons of worst case water, it should not have a problem with 1000 gallons of ours.  So I figure to extend this from three months to six months.  But now you do need to keep an eye on it since it will never shut off on its own. 

I bypassed this because I suspect this rather clever mechanism is almost surely for the benefit of Kinetico, not really for me, the consumer.  It probably easily doubles the filter revenue they take in.  Once the quality of the water noticeably deteriorates, then I will drop the $90 on a new filter at my discretion.  I don’t feel inclined to let Kinetico force me to do it before I think it’s necessary.

Your mileage may vary, and it’s up to you to make sure your water is still clean should you choose to bypass this shutoff mechanism.  I take no responsibility for you, your actions, Kinetico’s revenue, etc.  So proceed with care and good luck.

Saturday, November 3, 2012

AJAX File Download with PHP


You would think this would be reasonably easy, but it isn’t.  It might be possible to do it with a raw XMLHttpRequest, and then again, it might not be.  The problem we have is that we want the browser to interpret it as a new page.  I initially attempted to do this with something like the following using JQuery:

$('#target-iframe).load('page.ajax' + requestParams');

where “target-iframe” is, of course, the contents of an iframe.  But I pretty quickly concluded it was never going to work.

However, I came up with an almost equally simple method that works very well.  It’s not really AJAX per se, but the difference is imperceptible to the end user.  The following short Javascript snippet will immediately start a download, assuming of course that the target URL returns the expected download headers.

function initiateFileDownload() {
   var requestParams = "/f_download/1";
   requestParams += getPageFilters('', '');
   $("#target-iframe").attr('src','page.ajax' + requestParams);
}

<iframe id="target-iframe" name="target-iframe" src="#" style="width:600;height:100;border:0px solid #fff;display:none"></iframe>

In this case, I absolutely had to do an Ajax type loader because I was trying to download results from a page that the user had already filtered down.  I could have replicated the filter code and regenerated the page, but then I have to worry about keeping the filter logic in synch between my downloader and the actual page, which is a scenario best avoided.

Here is my PHP download code that would sit somewhere such that it gets called by “page.ajax”.

ob_clean();
header('Content-Description: File Transfer');
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="'.$filename.'";');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . strlen($data));
echo $data;
ob_flush();
flush();

Saturday, October 13, 2012

Semaphore class for PHP



PHP has built in support for semaphores via the sem_get(), sem_acquire(), and sem_release() commands. But support for semaphores must be compiled into PHP, and it is not enabled by default.  So it sometimes happens that you are trying to develop for a system where it has not been included, and you cannot just recompile the PHP (like in the case you are trying to develop for company X’s production servers).  In a case like this if you need semaphores, you have no choice but to roll your own.

Note the “maxLockMinutes” parameter in the “getSemaphore()” method.  If something happens and you fail to clean up at the end of this method, you will not end up locking yourself out of the resource permanently.  Once the process that has created the semaphore terminates, the lock on the file will be released.  But if the process hangs or otherwise lingers around, you need some way to break the lock.  The “maxLockMinutes=5” parameter tells the semaphore class to ignore any file lock that is older than X minutes.  I have defaulted it to 5 minutes here but you’ll want to tune it for your own system. 

The bad thing about this is that the calling program that knows how long it needs to lock the resource for cannot dictate it.  We could embed the lock time in the filename or possibly write it to the file if that is a requirement. 

I left the Semaphore class embedded in the page I used to test it.


<html>

<body>

<?php

define("UNIQUE_KEY", '123456');
define("BASE_DIRECTORY", '/tmp/');


class Semaphore {

   /* var stream */
   private $fp;

   /* var string */
   private $filename;

   /**
    * Constructor
    */
   public function __construct() {
   }

   /**
·       Attempt to get an exclusive lock on this semaphore
·        
    * @param string $baseDirectory
    * @param string $uniqueKey
    * @param int $maxLockMinutes
    *
    * @return bool
    */
   public function getSemaphore($baseDirectory, $uniqueKey, $maxLockMinutes=5) {
      $this->filename = $baseDirectory . 'semlock-' . $uniqueKey . '.sem';
      $this->releaseExpiredSemaphore($maxLockMinutes);
      $this->fp = fopen($this->filename, 'w');
      return (flock($this->fp, LOCK_EX | LOCK_NB));
   }

   /**
·       Release expired semaphore
    *
    * @param int $maxValidLockMinutes
    */
   private function releaseExpiredSemaphore(($maxValidLockMinutes) {
      if (file_exists($this->filename)) {
         $fileDate = filemtime($this->filename);
         if (time() - $fileDate > ($maxValidLockMinutes  * 60) {
            unlink($this->filename);
         }
      }
   }

   /**
    * Release semaphore
    */
   public function releaseSemaphore() {
      flock($this->fp, LOCK_UN);
      fclose($this->fp);
      unlink($this->filename);
   }
}

$semTest = new Semaphore();

$sem = $semTest->getSemaphore(BASE_DIRECTORY, UNIQUE_KEY, 5);
if($sem) {
   echo "Got lock <br>";
} else {
   echo "Failed to get lock. Boohoo! <br />";
   die();
}
for($i = 0; $i < 60; $i++) {
   sleep(5);
   echo ("sleeping " . $i * 5 . " hold you horses...<br />");
}
$semTest->releaseSemaphore();

?>


</body>
</html>