Gigantour

Last Friday night, I had the extreme pleasure of going to Boston to see Gigantour, which is a summer metal band tour created by Dave Mustaine of Megadeth. The show took place at the Bank of America Pavillion, which is a great venue in Boston. The only problem this venue has is the unreasonable curfew imposed by the city, which limited the number of bands that were supposed to play. On this particular night, the following bands played:

  • Nevermore
  • Dillinger Escape Plan
  • Fear Factory
  • Dream Theater
  • Megadeth

Right off the bat, I have to say how disappointed I was that Symphony X was not playing. Billed as a "second stage" band, the curfew prevented them from being part of the show, which is a huge mistake considering that Symphony X is one of the most dominating and talented bands in the progressive metal scene today.

The first band to go on was Nevermore. I had only heard a few songs of theirs, and I was really impressed by how good they were in a live setting. Their set was very short, but they managed to play a range of songs from all of their albums.

Next up was Dillinger Escape Plan, which is quite possibly the world's worst band ever. I cannot stress enough how bad this band is. If it hadn't been the fact that they were thrashing around the stage, throwing objects at the audience, destroying equipment, and bleeding on me, they might have been okay. The lead singer, a walking hunk of meat, managed to almost injure two photographers with a microphone stand, and in the process, sliced his hand open. After smearing blood all over his face, he promptly bled on me and my companion who were in the front row. Smashing guitars causes strings to break and instruments to go out of tune, and in this endeavour, the band excelled. That's all the press I am willing to give this bunch of idiots.

Third on the docket was Fear Factory. I have been listening to this group for several years now, and they are one of my favorite metal groups. Not only did they sound good, but they were very entertaining and really worked the crowd. Before ending their set, the band gave tribute to Pantera's fallen leader, Dimebag Darrell. Again, the short sets prevented a full experience, but this is one group I definitely plan on seeing live again.

The fourth band was Dream Theater, my all-time favorite band. I have seen this band on every tour since 1997, and I never get tired of seeing them or hearing their music. Dream Theater is perhaps the most intelligent band in the progressive metal scene, and they can play a huge range of styles from blues to straight metal. While their sound has become more experimental lately with discordant jazzy sections, they still crank out the metal when appropriate. If you haven't heard Dream Theater before, go visit iTunes and download a song or two. You won't be disappointed.

The last band to play was Megadeth. As the old-school ambassadors of metal, there are few groups who are still in the game producing classic metal like these guys. The best part about this band was that they realized that their set was shorter than it would have been on a full tour, so there was very little talking and time off between songs - it was one song right after another in machine gun fashion, with songs spanning a 20-year career in the business. Their act was slickly produced too, with chain link fencing around the amps, explosions and flames, and a really cool steel drum stage. Dave Mustaine speaks for all the disenfranchised, the misfits, the outcasts, and other miscreants of society:

What do you mean, "I don't believe in God"?
I talk to him every day.
What do you mean, "I don't support your system"?
I go to court when I have to.
What do you mean, "I can't get to work on time"?
I got nothing better to do
And, what do you mean, "I don't pay my bills"?
Why do you think I'm broke? Huh?

If there's a new way,
I'll be the first in line.
But, it better work this time.

What do you mean, "I hurt your feelings"?
I didn't know you had any feelings.
What do you mean, "I ain't kind"?
I'm just not your kind.
What do you mean, "I couldn't be president, of the United States of America"?
Tell me something, it's still "We the people", right?

If there's a new way
I'll be the first in line,
But, it better work this time.

Can you put a price on peace?
More information about the tour and other photos can be found at the Official Gigantour Website.

The Death of Property Rights

It's amazing how something so fundamental as property rights can be dissolved so easily, all in the name of "public good".

Today, the US Supreme Court ruled in favor of Big Business with a 5-4 opinion that granted private developers the power to demolish people's homes in favor of business parks, malls, and gyms for the purpose of economic development and increased tax revenues.

Any why wouldn't the government be in favor of increased tax revenues?

The poor souls in New London, CT who are poised to lose their homes (some of which have been in their family's posession for generations) will be "fairly compensated," which means that a 80-year-old Victorian will get market value.  This rarely enables the owners to find new adequate housing, because of the hyper-inflated housing costs in the Northeast.  So what's a person to do?  Chain yourself to your front door.  Gather 400 friends and family to form a solid human wall around your property and prevent the government from taking your home.

It's clearly up to you to protect what's yours; when the government fails to uphold the very ideals upon which this country was founded, the responsibility fails squarely upon the shoulders of the citizens.

Seven states allow private business development on seized property:  Connecticut, Kansas, Maryland, Michigan, Minnesota, New York and North Dakota.

It should be noted that New Hampshire and Massachusetts have indicated they probably will find this alarming new power to be unconstitutional.  I URGE you to contact your representatives and senators to outlaw the ability to seize property for private development.

Otherwise... Don't get too comfortable.  Your home may be next.

Senator Judd Gregg (R)
393 Russell Senate Office Building
Washington, DC 20510-2904
Phone: (202) 224-3324
Official website

Senator John Sununu (R)
111 Russell Senate Office Building
Washington, DC 20510-2903
Phone: (202) 224-2841
Official website

Integrating Winamp With a Website

This took me some time to figure out, and based on the number of posts in online forums, I can tell I am not th eonly person interested in integrating my Winamp playlists with a website.

In case you haven't noticed, I have added a block on the right-hand side of this website entitled "Now Playing...".  The contents of this block reflects what is currently on my Winamp media player.  There was a trick to getting that to work properly, but the other trick which has little to do with Winamp, is the display of the CD cover art.

There are several steps to this project:

Step 1: Data has to be sent from Winamp to the website.  In order to do this, I used a plugin called "DoSomething" http://www.oddsock.org/tools/dosomething, which has the capability to export ID3 metadata to files, which can then be sent to the website via FTP.  Once you have the plugin downloaded and installed, you need to configure it.

The first part of configuring the plugin is to create a template that the plugin can write data to.  I chose to use an XML format for my template, which looks like this:

<xmp>
<?xml version="1.0" encoding="iso-8859-1" ?>
<winamp>
<current>
<title>%%CURRENTSONGTITLE%%</title>
<artist>%%CURRENTARTIST%%</artist>
<album>%%CURRENTALBUM%%</album>
</current>
<second>
<title>%%SONG2%%</title>
<artist>%%ARTIST2%%</artist>
<album>%%ALBUM2%%</album>
</second>
<third><title>%%SONG3%%</title>
<artist>%%ARTIST3%%</artist>
<album>%%ALBUM3%%</album>
</third><lastupdated>%%LASTUPDATED%%</lastupdated>
</winamp>
</xmp>

I saved this template as C:\temp\winamp.template.txt.  In the DoSomething configuration dialog box, select "Generate HTML Playlist" from the Actions pulldown box.  In the "Template In" field, enter the path to your template file.  In the "Template Out" field, I entered C:\temp\winamp.xml.  Press the Add button to add this to the list of actions.

You'll probably want to enable Error Messages, as well as ID3 Info Gathering.

Next, select "FTP A File" from the Actions pulldown.  Configure your ftp host, username, and password.  Specify the local file as the path to your template out file (in my case, C:\temp\winamp.xml), and specify the path and file name of the remote file on your FTP server.

Now, everytime the song changes, this file will get updated and sent to your website via FTP.

Step 2:Now you have the data on your website, you have to do something with it.  My solution was to use PHP5 to parse the XML.  With PHP5, parsing XML is very very easy.  I placed the following code in a file called "nowPlaying.php":

<xmp>
/**
* Parse XML playlist
*
* This function parses an XML playlist file that was generated by Winamp, and
* uploaded to the webserver via FTP
*
* <a href="http://twitter.com/access">@access</a>    public
* <a href="http://twitter.com/param">@param</a>     string $file The path to the xml file to parse
* <a href="http://twitter.com/return">@return</a>    mixed Associative array containing the playlist data
*/
function parsePlaylist($file)
{
$playlist = array();
 
$dom = new DomDocument();
$dom->load($file);
 
// Loop through all the nodes
foreach ($dom->documentElement->childNodes as $entries)
{
// if node is an element
if ($entries->nodeType == 1)
{
// Loop through the inner set of nodes
foreach ($entries->childNodes  as $item)
{
// Populate the array
$playlist[$entries->nodeName][$item->nodeName] = $item->textContent;
}
}
}
return $playlist;
 
} // End Function
 
// Call the parsePlaylist function, which returns an array
$arrPlaylist = parsePlaylist("../winamp/winamp.xml");
 
$title = $arrPlaylist['current']['title'];
$artist = $arrPlaylist['current']['artist'];
$album = $arrPlaylist['current']['album'];
 
// Force the page to refresh every 60 seconds
print '<meta HTTP-EQUIV="refresh" content="60">';
 
if($title != "")
{
print '
<div style="color:#000;font-weight:bold;font-size:12pt;font-family:Helvetica;">
Title:</div>
'.
'
<div style="color:red;font-weight:bold;font-size:11pt;font-family:Helvetica;">
&nbsp;&nbsp;'.$title.
'</div>
';
}
 
if($artist != "")
{
print '
<div style="color:#000;font-weight:bold;font-size:12pt;font-family:Helvetica;">
Artist:</div>
'.
'
<div style="color:red;font-weight:bold;font-size:11pt;font-family:Helvetica;">
&nbsp;&nbsp;<i>'.$artist.'</i></div>
';
}
 
if($album != "")
{
print '
<div style="color:#000;font-weight:bold;font-size:12pt;font-family:Helvetica;">
From the album:</div>
'.
'
<div style="color:red;font-weight:bold;font-size:11pt;font-family:Helvetica;">
&nbsp;&nbsp;'.$album.'</div>
';
}
</xmp>

It's fairly ugly to have all the css code within the print statements - these should be placed with an external css file as classes, and referenced as such within the

tags.

Step 3: Now that we have the playlist displayed on a webpage, we have to get the CD cover art.  This was fairly tricky to do, and took some trial and error, but it works fairly reliably.  The cover art is retrieved dynamically from Amazon.com using their search API.  I borrowed some code written by Calin Uioreanu at php9.com, which did a very good job of performing a search and returning a bunch of results.  However, I did not want multiple results; I only wanted the CD image.

There are three components to this approach:

- amazon_layout.php

- amazon_config.php

- amazon_class.php

The amazon_layout.php is what displays the actual image.  The code for it is as follows:

<xmp>
<?php
/**
* filename:     amazon_layout.php
* created:      7/17/2002, © 2002 php9.com Calin Uioreanu
* descripton:   display template for Amazon API
*/
 
// This images array is populated by the amazon class
$myImage = $ARRIMAGES[6][0];
 
// if Amazon returned an empty Image (1x1), do not display the HTML code
$arImageSize = getImageSize($myImage) ;
 
if ( is_array($arImageSize) && $arImageSize[1] == 1)
{
$sImageUrl = '';
}
else
{
$sImageUrl = '<img '. SMALL_IMAGE_PROPERTIES .' xsrc="'. $myImage .'" alt="'.
$PRODUCTNAME .'" hspace="5" vspace="5" border="0" align="left" />';
}
 
echo
'
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td rowspan="7" bgcolor="white">',
$sImageUrl,
'</td>
</tr>
</table>
',
'
'
;
?>
</xmp>

Again, this code was modified from Colin's original to use only the first image in the array of data, as opposed to displaying all the images returned from the Amazon.com search.

The next file, amazon_conf.php contains configuration code for accessing the Amazon API:

<xmp>
<?php
/**
* filename:   amazon_config.php
* created:    7/17/2002, © 2002 php9.com Calin Uioreanu
* descripton: configuration variables for Amazon Parser
*/
 
// if you have a valid Amazon associate Id, place it here to be
// rewarded for the traffic you generate to Amazon
define('ASSOCIATE_ID', 'php9comweblot-20');
 
// Amazon specific constants
define('IMAGEURLMEDIUM_HEIGHT',    140);
define('IMAGEURLMEDIUM_WIDTH',        107);
 
// Do not change this constant
define('DEVELOPER_TOKEN','D37FFQXOC3MRYZ');
 
//  XSL live transform Amazon data to HTML
define('PRODUCT_DETAIL_URL','http://xml.amazon.com/onca/xml3?t='.ASSOCIATE_ID.
'&dev-t='.DEVELOPER_TOKEN.
'&type=heavy&f=http://www.php9.com/php9-data-to-htmls.xsl&AsinSearch=');
 
// Set the default place to look
$sCurrentMode = 'music';
 
$arModes = array (
'baby' => 'baby (Baby)',
'books' => 'books (Books)',
'classical' => 'classical (Classical Music)',
'dvd' => 'dvd (DVD)',
'electronics' => 'electronics (Electronics)',
'garden' => 'garden (Outdoor Living)',
'kitchen' => 'kitchen (Kitchen & Housewares)',
'magazines' => 'magazines (Magazines)',
'music' => 'music (Popular Music)',
'pc-hardware' => 'pc-hardware (Computers)',
'photo' => 'photo (Camera & Photo)',
'software' => 'software (Software)',
'toys' => 'toys (Toys & Games)',
'universal' => 'universal (Tools & Hardware)',
'vhs' => 'vhs (Video)',
'videogames' => 'videogames (Computer & Video Games)'
);
 
 
// sort by salesRank by default
if (!$sCurrentModeSortType = $_GET['SortBy'])
{
$sCurrentModeSortType = '+salesrank';
}
 
// Sort Types
$arModeSortType = array (
'baby' => array(
'+pmrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical (A-Z)',
),
'books' => array(
'+pmrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+reviewrank' => 'Average Customer Review',
'+pricerank' => 'Price (Low to High)',
'+inverse-pricerank' => 'Price (High to Low)',
'+daterank' => 'Publication Date',
'+titlerank' => 'Alphabetical (A-Z)',
'-titlerank' => 'Alphabetical (Z-A)',
),
'classical' => array(
'+pmrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical (A-Z)',
),
'dvd' => array(
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical',
),
'electronics' => array(
'+pmrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical',
'+reviewrank' => 'Review',
),
'garden' => array(
'+psrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical (A-Z)',
'-titlerank' => 'Alphabetical (Z-A)',
'+manufactrank' => 'Manufacturer (A-Z)',
'-manufactrank' => 'Manufacturer (Z-A)',
'+price' => 'Price (Low to High)',
'-price' => 'Price (High to Low)',
),
'kitchen' => array(
'+psrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical (A-Z)',
'-titlerank' => 'Alphabetical (Z-A)',
'+manufactrank' => 'Manufacturer (A-Z)',
'-manufactrank' => 'Manufacturer (Z-A)',
'+price' => 'Price (Low to High)',
'-price' => 'Price (High to Low)',
),
'magazines' => array(
'+pmrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical (A-Z)',
),
'music' => array(
'+psrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+artistrank' => 'Artist Name',
'+orig-rel-date' => 'Original Release Date',
'+titlerank' => 'Alphabetical',
),
'pc-hardware' => array(
'+psrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical (A-Z)',
'-titlerank' => 'Alphabetical (Z-A)',
),
'photo' => array(
'+pmrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical (A-Z)',
'-titlerank' => 'Alphabetical (Z-A)',
),
'software' => array(
'+pmrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical',
'+price' => 'Price (Low to High)',
'+price' => 'Price (High to Low)',
),
'toys' => array(
'+pmrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical (A-Z)',
),
'universal' => array(
'+psrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical (A-Z)',
'-titlerank' => 'Alphabetical (Z-A)',
'+manufactrank' => 'Manufacturer (A-Z)',
'-manufactrank' => 'Manufacturer (Z-A)',
'+price' => 'Price (Low to High)',
'-price' => 'Price (High to Low)',
),
'vhs' => array(
'+psrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical',
),
'videogames' => array(
'+pmrank' => 'Featured Items',
'+salesrank' => 'Bestselling',
'+titlerank' => 'Alphabetical',
'+price' => 'Price (Low to High)',
'-price' => 'Price (High to Low)',
),
);
 
$sUrl  = 'http://xml.amazon.com/onca/xml3';
$sUrl .= '?t='. ASSOCIATE_ID;
$sUrl .= '&dev-t='. DEVELOPER_TOKEN;
$sUrl .= '&mode=' . $sCurrentMode;
$sUrl .= '&type=lite&page=1';
$sUrl .= '&f=xml';
$sUrl .= '&KeywordSearch=';
 
// The searchFor variable is set as a global variable in the calling php script
$sUrl .= urlencode($GLOBALS['searchFor']);
 
$sUrl .= '&sort='. $sCurrentModeSortType;
 
?>
</xmp>

At this point, you can see that the config file sets a bunch of variables that the Amazon class needs to use.  A very important piece of this is what you wish to search for.  This data comes from your calling script; in my case, it's defined in nowPlaying.php, which is listed in step 1.  You will need to add the following code to your calling script to set the global variables containing the search criteria:

global $searchFor;
$searchFor = $arrPlaylist['current']['album'].'+'.$arrPlaylist['current']['artist'];

Add this code somewhere after you call the parsePlaylist() function.  By combining the name of the album with the name of the artist, we increase the chances that we'll get accurate matches from Amazon.

Lastly, we have the actual class which is the heart and soul of this entire project:

<?php
 
/**
* amazon_class.php
*
* === Modification History ===
* 1.0.0  17-jul-2002  [cu]      initial version
* 1.0.1  06-jun-2005  [ecb]    modified parse function to store data in array
*
*/
 
/**
* Amazon_WebService
*
* webservice class definition for Amazon Parser
*
* <a href="http://twitter.com/version">@version</a>        1.0.1
* <a href="http://twitter.com/author">@author</a>        Calin Uioreanu
* <a href="http://twitter.com/copyright">@copyright</a>  &copy; 2002,
*/
 
// lock mechanism for amazon webservices servers
define('AWS_LOCK_FILE', 'aws_lock');
 
// if a query fails, we will not bother amazon webservices servers for a given time
define('AWS_LOCK_TIME', 30);
 
class Amazon_WebService
{
var $sData;
var $arAtribute;
var $iNumResults;
var $sTemplate;
 
/**
* void Amazon_WebService(void)
* constructor
*/
function Amazon_WebService()
{
$this->parser = xml_parser_create();
} // end func
 
/**
* boolean parse(void)
* parses a XML file
*/
function parse()
{
if (!$this->testLock())
{
// prevent html caching
$_GLOBALS['bFeedError'] = true;
return false;
}
 
// set the handlers
xml_set_object($this->parser, $this);
xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
xml_set_character_data_handler($this->parser, 'cdataHandler');
 
if (!is_resource($this->fp))
{
$this->createLock();
return false;
}
 
/**
* Read the XML file and store the chunks into an array.  This is done because
* the Amazon search will return many results, and we only want the first result
* which will be the most relevant (and most likely, the correct one)
*/
while ($data = fread($this->fp, 2048))
{
$chunks[] = $data;
}
 
// Close the xml file
fclose($this->fp);
 
// Parse the XML contained within the first chunk of data
$err = xml_parse($this->parser, $chunks[0]);
if (! $err)
{
return $err;
}
 
// free the parser resource
xml_parser_free($this->parser);
 
return true;
 
} // end func
 
 
/**
* void startHandler (obj , str , arr)
* Event handler called by the expat library when an element's begin tag is encountered.
*/
function startHandler($parser, $sTag, $arAttr)
{
//  Start with empty sData string.
$this->sData = '';
 
//  Put each attribute into the Data array.
foreach ($arAttr as $Key=> $Val)
{
$this->arAtribute["$sTag:$Key"] = trim($Val);
}
} // end func
 
/**
* void cdataHandler (obj, str)
* Event handler called by the expat library when Character Data are encountered.
*/
function cdataHandler($parser, $sTag)
{
$this->sData .= $sTag;
}// end func
 
/**
* void endHandler (obj, str)
* Event handler called by the expat library when an element's end tag is encountered.
*/
function endHandler($parser, $sTag)
{
static
$MODE,
$ASIN,
$PRODUCTNAME,
$CATALOG,
$AUTHORS,
$ARTISTS,
$STARRING,
$DIRECTORS,
$THEATRICALRELEASEDATE,
$RELEASEDATE,
$MANUFACTURER,
$IMAGEURLSMALL,
$IMAGEURLMEDIUM,
$IMAGEURLLARGE,
$ARRIMAGES,
$LISTPRICE,
$OURPRICE,
$USEDPRICE,
$REFURBISHEDPRICE,
$THIRDPARTYNEWPRICE,
$SALESRANK,
$LISTS,
$TRACKS,
$BROWSELIST,
$MEDIA,
$NUMMEDIA,
$ISBN,
$FEATURES,
$MPAARATING,
$PLATFORM,
$AVAILABILITY,
$UPC,
$ACCESSORIES,
$PRODUCTDESCRIPTION,
$REVIEWS,
$THIRDPARTYPRODUCTINFO,
$AVGCUSTOMERRATING,
$CUSTOMERREVIEW,
$THIRDPARTYPRODUCTDETAILS,
$SIMILARPRODUCTS,
$TOTALRESULTS
;
 
// put the $this->sData into a string.
$sData = $this->sData;
switch (strtoupper ($sTag))
{
case 'ACTOR':
// build the list
$STARRING[] = $sData;
break;
case 'ARTIST':
// build the list
$ARTISTS[] = $sData;
break;
case 'DIRECTOR':
// build the list
$DIRECTORS[] = $sData;
break;
case 'AUTHOR':
// build the Authors list
$AUTHORS .= ($AUTHORS?', ':'') . $sData;
break;
case 'LISTID':
// build the list
$LISTS[] = $sData;
break;
case 'TRACK':
// build the list
$TRACKS[] = $sData;
break;
case 'BROWSENAME':
// build the list
$BROWSELIST[] = $sData;
break;
case 'FEATURE':
// build the list
$FEATURES[] = $sData;
break;
case 'ACCESSORY':
// build the list
$ACCESSORIES[] = $sData;
break;
case 'PRODUCT':
// build the list
$SIMILARPRODUCTS[] = $sData;
break;
case 'AVGCUSTOMERRATING':
// build the list
$AVGCUSTOMERRATING = $sData;
break;
case 'CUSTOMERREVIEW':
// build the list
$REVIEWS[] = $CUSTOMERREVIEW;
break;
case 'THIRDPARTYPRODUCTDETAILS':
// build the list
$THIRDPARTYPRODUCTINFO[] = $THIRDPARTYPRODUCTDETAILS;
break;
case 'SELLERID':
case 'SELLERNICKNAME':
case 'EXCHANGEID':
case 'OFFERINGPRICE':
case 'CONDITION':
case 'CONDITIONTYPE':
case 'EXCHANGEAVAILABILITY':
case 'SELLERCOUNTRY':
case 'SELLERSTATE':
case 'SELLERRATING':
// build the list
$THIRDPARTYPRODUCTDETAILS[$sTag] = $sData;
break;
case 'RATING':
// build the list
$CUSTOMERREVIEW['Rating'] = $sData;
break;
case 'SUMMARY':
// build the list
$CUSTOMERREVIEW['Summary'] = $sData;
break;
case 'COMMENT':
// build the list
$CUSTOMERREVIEW['Comment'] = $sData;
break;
case 'ASIN':
case 'MODE':
case 'PRODUCTNAME':
case 'CATALOG':
case 'THEATRICALRELEASEDATE':
case 'MPAARATING':
case 'RELEASEDATE':
case 'MANUFACTURER':
case 'IMAGEURLSMALL':
case 'IMAGEURLMEDIUM':
$ARRIMAGES[][] = $sData;
break;
case 'IMAGEURLLARGE':
case 'LISTPRICE':
case 'OURPRICE':
case 'USEDPRICE':
case 'REFURBISHEDPRICE':
case 'THIRDPARTYNEWPRICE':
case 'SALESRANK':
case 'MEDIA':
case 'PRODUCTDESCRIPTION':
case 'NUMMEDIA':
case 'ISBN':
case 'PLATFORM':
case 'AVAILABILITY':
case 'UPC':
$$sTag = $sData;
break;
 
case 'ERRORMSG':
// if this is a global error, avoid caching the module
if (!$this->iNumResults)
{
global $bParseError;
$bParseError = true;
}
break;
 
case 'DETAILS':
// offer some details
$sBookUrl = PRODUCT_DETAIL_URL . $ASIN;
//Details finished
require($this->sTemplate);
 
// empty the product related information
$STARRING= array ();
$ARTISTS= array ();
$DIRECTORS= array ();
$AUTHORS = '';
$REVIEWS = array ();
$THIRDPARTYPRODUCTINFO = array();
$BROWSELIST= array ();
$FEATURES= array ();
$ACCESSORIES = array ();
$AVGCUSTOMERRATING = array ();
$CUSTOMERREVIEW = array ();
$SIMILARPRODUCTS = array ();
 
$ASIN='';
$PRODUCTNAME='';
$CATALOG='';
$AUTHORS='';
$THEATRICALRELEASEDATE='';
$RELEASEDATE='';
$MANUFACTURER='';
$IMAGEURLSMALL='';
$IMAGEURLMEDIUM='';
$IMAGEURLLARGE='';
$LISTPRICE='';
$OURPRICE='';
$USEDPRICE='';
$REFURBISHEDPRICE='';
$THIRDPARTYNEWPRICE='';
$SALESRANK='';
$MEDIA='';
$PRODUCTDESCRIPTION = '';
$NUMMEDIA='';
$ISBN='';
$MPAARATING='';
$PLATFORM='';
$AVAILABILITY='';
$UPC='';
 
// increase global counter
$this->iNumResults++;
break;
 
case 'TOTALRESULTS':
$this->arAtribute['TotalResults'] = $sData;
break;
 
case 'PRODUCTINFO':
break;
}
}
 
/**
* bool setInputUrl(str, int)
* tries to open a connection to the given url with the given timeout
*/
function setInputUrl($sUrl, $iTimeout)
{
if (!$this->testLock())
{
// prevent html caching
$_GLOBALS['bFeedError'] = true;
return false;
}
 
$arUrl = parse_url($sUrl);
$sHost = $arUrl['host'];
if (!@$iPort = $arUrl['port'])
{
$iPort = 80; // default HTTP port
}
$sFullPath = $arUrl['path'] . '?' . $arUrl['query'];
 
$fp = fsockopen($sHost, $iPort, $errno, $errstr, $iTimeout);
if(!is_resource($fp))
{
// return an error
echo '<!-- '. $sUrl . " issued: $errstr ($errno)\n" .' -->@';
$this->createLock();
return false;
}
else
{
fputs($fp,"GET $sFullPath HTTP/1.0\r\nHost: " . $sHost. "\r\n\r\n");
 
// win32
if (function_exists('socket_set_timeout'))
{
@socket_set_timeout($fp, $iTimeout);
}
 
// in blocking mode it will wait for data to become available on the socket
socket_set_blocking($fp, true);
 
// get the first line and determine the answer-code
$sLine = fgets($fp , 1024);
$iCode = preg_replace("/.*(\d\d\d).*/i" , "\\1" , $sLine);
 
// an error occurred if code is not between 200 and 399
$error = null;
if ($iCode != 200)
{
// prevent html caching
$_GLOBALS['bFeedError'] = true;
error_log (
"\n". time() .':'. $sUrl .':'. $sLine .':'. $_SERVER['REQUEST_URI'],
3,
'htmlcache/search/return_code_failures.log'
);
fclose($fp);
$this->createLock();
return false;
}
 
$sHeader = '';
// no error - now determine start of data (skipping header)
while (!feof($fp))
{
$sLine = fgets($fp , 1024);
if (strlen($sLine) < 3)
{
break;
}
}
 
$this->fp = $fp;
return true;
}
} // end func
 
/**
* createLock()
* lock
*/
function createLock()
{
if (!$handle = fopen(AWS_LOCK_FILE, 'w'))
{
echo 'cannot open lock';
return false;
}
 
fclose($handle);
return true;
} // end func
 
/**
* testLock()
* test lock for a given time, eventually release it
*/
function testLock()
{
if (!file_exists(AWS_LOCK_FILE))
{
return true;
}
 
if ((time() - filemtime(AWS_LOCK_FILE)) < AWS_LOCK_TIME)
{
return false;
}
 
// release lock
if (!$handle = unlink(AWS_LOCK_FILE))
{
return false;
}
 
return true;
} // end func
 
} // end class definition
 
?>

Step 4: Now that you have all of the code, you have to use it.  I added the following function to my calling script (nowPlaying.php):

<xmp>
/**
* Get the image
*
* This function uses the Amazon class to search for a CD for the purpose of displaying
* the cover art on a webpage
*
* <a href="http://twitter.com/access">@access</a>    public
* <a href="http://twitter.com/param">@param</a>      string $album The name of the album
* <a href="http://twitter.com/return">@return</a>  null
*/
function getCDImage($album)
{
// configuration variables
require_once('./amazon/amazon_config.php');
 
// webservice class definition
require_once('./amazon/amazon_class.php');
 
flush();
 
if($album != "")
{
$oAmazon = new Amazon_WebService();
 
if (!$oAmazon->setInputUrl($sUrl, 20))
{
die ('cannot open input file. exiting..' . '<a xhref='. $sUrl .'>@</a>');
}
 
// pass the output display template
$oAmazon->sTemplate = './amazon/amazon_layout.php';
 
if (!$oAmazon->parse())
{
die ('XMLParse failed');
}
}
else
{
print "
 
No CD artwork available for this album
";
}
}
</xmp>

Step 5:  The final step is to embed this script into a webpage.  The script prints a meta refresh tag which causes the page to reload every 60 seconds.  I did not want the entire page to reload, so I display the script within an iframe:

<xmp>
<iframe xsrc="/php/nowPlaying.php" frameborder=0 width="240px" height="360px"></iframe>
</xmp>

Now, only this small rectangular block of content will refresh.  I know that there seems to be a lot of steps involved with this integration, and perhaps the amazon stuff can all be merged into a single class (which it probably should...), but the results are worth the effort, in my opinion!

Feel free to contact me with problems!

Voice Your Opinion:: Judicial Filibusters

Please call your Senators and urge them to vote against invoking the nuclear option to end judicial filibusters. Let them know that if they vote to invoke this option, they're making a mistake that will be remembered when they come up for re-election. This removal of checks and balances between the majority and minority parties cannot be tolerated.

Senator Judd Gregg (R)
393 Russell Senate Office Building
Washington, DC 20510-2904
Phone: (202) 224-3324
Official website

Senator John Sununu (R)
111 Russell Senate Office Building
Washington, DC 20510-2903
Phone: (202) 224-2841
Official website

Tyrants In Skivvies

In yet another showing of America's sense of humor, member of our Armed Services serving in Iraq have anonymously released pictures of Saddam Hussein in his underwear to the British press. As if the US military didn't already have enough problems with following the rules set forth by the Geneva Convention, here is one more example of our troops degrading prisoners.

On the other hand, Saddam needs a little degradation, in my opinion. The man in charge of thousands of murders, rapes, and other human rights abuses ought to be publicly humiliated for the rest of his life. If nothing else, this serves as a potent reminder to the other tyrants and dictators of the world that they are not as safe and powerful as they think they are.

I sincerely hope that this image gets beamed across the global news networks into the living rooms of every dictator's residence. I just wish the image hadn't come from our military...

Upgrading to FreeBSD 5.4

As it turns out, upgrading FreeBSD is not as difficult as I thought it would be.  The process is downright simple for the most part.  The trickiest part is running mergemaster, and upgrading the contents of /etc.  The risk is somewhat mitigated if you backup /etc before you run the mergemaster command.

Here is how I upgraded my system from FreeBSD 5.3.

The first thing to do is to create a file for updating the FreeBSD source tree.  I called this file "standard-supfile", and placed it in /etc.  The file looks like this:

*default host=cvsup2.FreeBSD.org
*default base=/usr
*default prefix=/usr
*default release=cvs tag=RELENG_5_4
*default delete use-rel-suffix
*default compress
src-all

To update the source, run the following command:

onion# cvsup -g -L 2 /etc/standard-supfile

This takes a while, so get some coffee and get to work on that programming project you've been putting off...  Once the source has been updated, it's time to rebuild the system.

onion#  cd /usr/src/sys/i386/conf
onion#  cp -p GENERIC MYKERNAL
onion#  config MYKERNAL; cd ../compile/MYKERNAL; make depend; make;
onion#  make install;
onion#  tar cf /tmp/etc.tar /etc
onion#  mergemaster

I like to make a copy of the GENERIC file, because any changes I may make to it will get wiped out next time I update the source.  By making a copy of the file and adding my changes to the copy, those changes are preserved.

After running mergemaster, reboot and /etc/motd should be updated to display the current version of the system.

Where are the aliens?

The universe is expanding away from the origin of the Big Bang. This has been measured and quantified. Looking through space in the direction of the Big Bang is like looking back in time, in the same way that looking at stars is like looking back in time, since it takes billions of years for that light to get to us. What we see today is not what is currently happening.

Now, it has been theorized that one cannot travel faster than light. The speed of light may be a constant that cannot be broken. If this is true, it would be impossible for other life forms from distant galaxies to be able to reach us, since there is a finite period of time from the Big Bang to now, plus the time it takes for a species to evolve, plus the time it takes to
get to Earth from other galaxies.

None of this precludes the fact there is other life in the universe. Only humans are arrogant enough to believe that

A) all intelligent life wants to travel across space and time;
B) Earth is worth visiting;
C) Intelligent life is interested in seeking out other intelligent life;
D) Humans ought to be considered as an "intelligent species"

For all we know, in the eyes of another species, humans have the collective intelligence of pond scum.

About Erich

Erich is a web developer and a native New Englander who is passionate about life, the universe, and everything.

He is currently a senior Drupal developer at Harvard University, working on the IQSS OpenScholar project.  Prior to joining the team at Harvard, he was the engineering manager at CommonPlaces e-Solutions, in Hampstead, NH, contributing as the lead engineer on the Greenopolis.com and Twolia.com.

Erich is active in the Drupal community, having contributed modules and patches to the community. He presented at DrupalCon in Szeged Hungary, and co-presented at DrupalCon 2009 in Washington, DC.

Erich lives in New Hampshire with his wife, two sons, and two weimaraners.  When not writing code, Erich enjoys landscaping and woodworking.

Faceted search

Categories

Content type

Project types

Artwork Type

Artwork Tags

Recent comments

Activity Stream

May 4, 2010

  • Twitter ebeyrent tweeted "Nicely put! RT: @webkenny: New Blog Posted! "The Lifestyle of Drupal Support" - http://bit.ly/dh0PM8 via @acquia #drupal" 10:52am #
  • Twitter ebeyrent tweeted "@starshaped I'll never see another concert there. Terrible sound system, zero security, staffed by idiots" 9:50am #
  • Twitter ebeyrent tweeted "Two of my favorite bands are on tour, but I won't see them because they are playing at the #palladium in #worcester #worstvenueever" 9:45am #
  • Twitter ebeyrent tweeted "#nowplaying Voyage 34 by @porcupinetree" 9:32am #

April 30, 2010

  • Twitter ebeyrent tweeted "Late rehearsal last night, not a lot of sleep. Definitely dragging a bit on a sunny Friday" 6:28am #

April 29, 2010

  • Twitter ebeyrent tweeted "Just committed initial release of Search Lucene Biblio for #drupal #lucene http://drupal.org/project/luceneapi_biblio" 2:53pm #
  • Twitter ebeyrent tweeted "Interesting #drupal intranet SaaS - http://www.officemedium.com/" 10:46am #
  • Twitter ebeyrent tweeted "OH: "Welcome to Boston, home to some of the worst motorists on the eastern seaboard"" 8:09am #
  • Twitter ebeyrent tweeted "Missing user 0 in #drupal messed me up for the last 3 days, preventing anonymous users from adding products to #ubercart. #fail" 7:13am #

April 28, 2010

April 27, 2010