Location: PHPKode > projects > Open Media Collectors Database > site/amazonecs.class.php
<?php
/* 	
	Open Media Collectors Database
	Copyright (C) 2001,2006 by Jason Pell

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

	Here are some sample codes for each kind of item to test the new feature if
	you do not have a book, cd or dvd in the hand :
	CDs :
	5099748120229
	724384131425
	008811270728
	078221908023
	077778645528
	0077779959426
	
	DVDs:
	5050582415131
	8711875960369
	7321951345495
	7321951346898
	3459379402156
	
	Books:
	9782742747955
	9782729606107
*/
include_once("./functions/SitePlugin.class.inc");

function amazonItemFactory($itemType)
{
	switch($itemType)
	{		
		case "DVD":
			return new amazonDVD();
			break;
		case "Music":
			return new amazonMusic();
			break;
		case "Books":
			return new amazonBook();
			break;
		default;
			return new amazonItem();
	}
}

class amazonItem
{
	var $asin = "";
	var $detailPageURL = "";
	var $smallImageURL = "";
	var $mediumImageURL = "";
	var $largeImageURL = "";
	var $title = "";
	var $manufacturer = "";
	var $productGroup = "";
	var $releaseDate = "";	
	var $averageRating = "";
	// $inAncestprs is set to TRUE if we are in an ancestor tag
	// this is used to get only last level categories
	var $inAncestors = FALSE;
	// $browseNodeIsGenres is set to TRUE when BrowseNode name is equal to 'Genres'
	// We use it to keep only BrowseNodes which type is 'Genres'
	var $browseNodeIsGenres = FALSE;
	var $browseNodeName = "";
	var $genres = array();
	// To store editorial review and description
	// Todo need to improve reviews parsing
	var $reviews = array();
	
	function amazonItem()
	{
	}
	
	// $parentTag must be an empty string if there is no parent Tag
	// $continue must be TRUE if $data is a continuation of previous data from a current tag
	// or FALSE for the beginning of data for a new Tag
	function parsing($tag, $parentTag, $data, $continue)
	{
		switch($tag)
		{
			case "ASIN":
				if ($parentTag == 'Item')
					$this->asin .= $data;
				break;
			case "Title":
				if ($parentTag == 'ItemAttributes')
					$this->title .= $data;
				break;
			case "DetailPageURL":
				$this->detailPageURL .= $data;
				break;
			case "URL":
				// We only store the first set of images that is parsed,
				// this is done by testing if the extension jpg is already present in the field
				if ($parentTag == 'SmallImage' && substr($this->smallImageURL, -4) != ".jpg")
					$this->smallImageURL .= $data;
				elseif ($parentTag == 'MediumImage' && substr($this->mediumImageURL, -4) != ".jpg")
					$this->mediumImageURL .= $data;
				elseif ($parentTag == 'LargeImage' && substr($this->largeImageURL, -4) != ".jpg")
					$this->largeImageURL .= $data;
				break;
			case "Manufacturer":
				$this->manufacturer .= $data;
				break;
			case "ProductGroup":
				$this->productGroup .= $data;
				break;
			case "ReleaseDate":
				$this->releaseDate .= $data;
				break;
			case "AverageRating":
				$this->averageRating .= $data;
				break;
			case 'Name':
				if ($parentTag == 'BrowseNode' && !$this->inAncestors)
				{
					if ($continue)
						$this->genres[count($this->genres) - 1] .= $data;
					else
						$this->genres[count($this->genres)] = $data;
				}
				elseif ($parentTag == 'BrowseNode')
				{
					if ($continue)
						$this->browseNodeName .= $data;
					else
						$this->browseNodeName = $data;
					if ($this->browseNodeName == 'Genres')
						$this->browseNodeIsGenres = TRUE;
				}
				break;
			case 'Content':
				if ($parentTag == 'EditorialReview')
				{
					if ($continue)
						$this->reviews[count($this->reviews) - 1] .= $data;
					else
						$this->reviews[count($this->reviews)] = $data;
				}
				break;
		}		
	}

	function startTag($tagName)
	{
		switch ($tagName)
		{
			case 'Ancestors':
				$this->inAncestors = TRUE;
				break;
		}
	}
	
	function endTag($tagName)
	{
		switch ($tagName)
		{
			case 'Ancestors':
				// Remove last added genres if it is not effectively a genre browseNode
				if (!$this->browseNodeIsGenres && $this->inAncestors)
					array_pop($this->genres);
				$this->inAncestors = FALSE;
				$this->browseNodeIsGenres = FALSE;
				break;
		}
	}
	
	function setSitePluginAttributes(&$plugin)
	{
		$plugin->addItemAttribute('aecsasin', $this->asin);
		$plugin->addItemAttribute('title', $this->title);
		$plugin->addItemAttribute('imageurl', $this->largeImageURL);
		$plugin->addItemAttribute('year', substr($this->releaseDate, 0, 4));
		$plugin->addItemAttribute('genre', $this->genres);
		if (count($this->reviews) >= 1)
			$plugin->addItemAttribute('blurb', $this->reviews[0], TRUE);
	}
}

class amazonDVD extends amazonItem
{
	var $actors = array();
	var $directors = array();
	var $format = "";
	var $regionCode = "";
	var $aspectRatio = "";
	var $numberOfDiscs = "";
	var $audienceRating = "NR";
	var $runningTime = "";
	var $strudio = "";
	var $languages = array();
	var $theatricalReleaseDate = "";
	
	function parsing($tag, $parentTag, $data, $continue)
	{
		parent::parsing($tag, $parentTag, $data, $continue);

		switch($tag)
		{
			case "TheatricalReleaseDate":
				$this->theatricalReleaseDate .= $data;
				break;
			case 'Format':
				$this->format .= (($this->format != '' && !$continue)?', ':'') . $data;
				break;
			case 'Actor':
				if ($continue)
					$this->actors[count($this->actors) - 1] .= $data;
				else
					$this->actors[count($this->actors)] = $data;
				break;
			case 'Director':
				if ($continue)
					$this->directors[count($this->directors) - 1] .= $data;
				else
					$this->directors[count($this->directors)] = $data;
				break;
			case 'RegionCode':
				$this->regionCode .= $data;
				break;
			case 'AspectRatio':
				$this->aspectRatio .= $data;
				break;
			case 'NumberOfDiscs':
				$this->numberOfDiscs .= $data;
				break;
			case 'AudienceRating':
				$this->audienceRating .= $data;
				break;			
			case 'RunningTime':
				$this->runningTime .= $data;
				break;
			case 'Studio':
				$this->studio .= $data;
				break;
			case 'Name':
				if ($parentTag == 'Language')
				{
					$this->languages[count($this->languages) - 1]->name .= $data;
				}
				break;
			case 'Type':
				if ($parentTag == 'Language')
				{
					$this->languages[count($this->languages) - 1]->type .= $data;
				}
				break;
			case 'AudioFormat':
				if ($parentTag == 'Language')
				{
					$this->languages[count($this->languages) - 1]->audioFormat .= $data;
				}
				break;
		}
	}

	function startTag($tagName)
	{
		parent::startTag($tagName);
		switch ($tagName)
		{
			case 'Language':
				array_push($this->languages, new dvdLanguage());
				break;
		}
	}
	
	function setSitePluginAttributes(&$plugin)
	{
		parent::setSitePluginAttributes($plugin);
		if (strpos($this->format, 'NTSC') !== FALSE)
			$plugin->addItemAttribute('vid_format', 'NTSC');		
		elseif (strpos($this->format, 'PAL') !== FALSE)
			$plugin->addItemAttribute('vid_format', 'PAL');		
		elseif (strpos($this->format, 'SECAM') !== FALSE)
			$plugin->addItemAttribute('vid_format', 'SECAM');		
		if (strpos($this->format, 'Anamorphic') !== FALSE)
			$plugin->addItemAttribute('anamorphic', 'Y');		
		$plugin->addItemAttribute('actors', $this->actors);		
		$plugin->addItemAttribute('director', $this->directors);		
		$plugin->addItemAttribute('dvd_region', $this->regionCode);		
		$plugin->addItemAttribute('ratio', $this->aspectRatio);		
		$plugin->addItemAttribute('no_discs', $this->numberOfDiscs);		
		$plugin->addItemAttribute('age_rating', $this->audienceRating);	
		$plugin->addItemAttribute('studio', $this->studio);		
		$plugin->addItemAttribute('dvd_rel_dt', $this->releaseDate);		
		$plugin->addItemAttribute('run_time', $this->runningTime);		
		$plugin->addItemAttribute('amazon_review', $this->averageRating);		
		$plugin->addItemAttribute('audio_lang', $this->_generateAudioLanguagesArray());
		$plugin->addItemAttribute('subtitles', $this->_generateSubtitlesLanguagesArray());
	}
	
	function _generateAudioLanguagesArray()
	{		
		$resultArray = array();
		foreach ($this->languages as $language)
		{
			if ($language->type != 'Subtitled')
			{
				array_push($resultArray, $language->name);
			}
		}
		
		return $resultArray;
	}
	
	function _generateSubtitlesLanguagesArray()
	{		
		$resultArray = array();
		foreach ($this->languages as $language)
		{
			if ($language->type == 'Subtitled')
			{
				array_push($resultArray, $language->name);
			}
		}

		return $resultArray;
	}
}

class amazonMusic extends amazonItem
{
	var $artists = array();
	var $numberOfDiscs = "";
	var $label = "";
	var $tracks = array();
	
	function parsing($tag, $parentTag, $data, $continue)
	{
		parent::parsing($tag, $parentTag, $data, $continue);

		switch($tag)
		{
			case 'Artist':
				if ($continue)
					$this->artists[count($this->artists) - 1] .= $data;
				else
					$this->artists[count($this->artists)] = $data;
				break;
			case 'NumberOfDiscs':
				$this->numberOfDiscs .= $data;
				break;
			case 'MusicLabel':
				$this->label .= $data;
				break;
			case 'Track':
				if ($continue)
					$this->tracks[count($this->tracks) - 1] .= $data;
				else
					$this->tracks[count($this->tracks)] = $data;
				break;				
		}
	}

	function setSitePluginAttributes(&$plugin)
	{
		parent::setSitePluginAttributes($plugin);
		$plugin->addItemAttribute('artist', $this->artists);		
		$plugin->addItemAttribute('no_discs', $this->numberOfDiscs);
		$plugin->addItemAttribute('musiclabel', $this->label);
		$plugin->addItemAttribute('cdtrack', $this->tracks);
	}
}

class amazonBook extends amazonItem
{
	var $authors = array();
	var $isbn = "";
	var $publisher = "";
	var $numberOfPages = "";
	var $publicationDate = "";
	var $edition = "";
			
	function parsing($tag, $parentTag, $data, $continue)
	{
		parent::parsing($tag, $parentTag, $data, $continue);

		switch($tag)
		{
			case 'Author':
				if ($continue)
					$this->authors[count($this->authors) - 1] .= $data;
				else
					$this->authors[count($this->authors)] = $data;
				break;
			case 'ISBN':
				if ($parentTag == "ItemAttributes")
					$this->isbn .= $data;
				break;				
			case 'Publisher':
				$this->publisher .= $data;
				break;				
			case 'PublicationDate':
				$this->publicationDate .= $data;
				break;				
			case 'Edition':
				$this->edition .= $data;
				break;				
			case 'NumberOfPages':
				$this->numberOfPages .= $data;
				break;				
		}
	}

	function setSitePluginAttributes(&$plugin)
	{
		parent::setSitePluginAttributes($plugin);
		$plugin->addItemAttribute('author', $this->authors);		
		$plugin->addItemAttribute('isbn', $this->isbn);
		$plugin->addItemAttribute('publisher', $this->publisher);		
		$plugin->addItemAttribute('edition', $this->edition);		
		$plugin->addItemAttribute('nb_pages', $this->numberOfPages);	
		$plugin->addItemAttribute('pub_date', substr($this->publicationDate, 0, 4));
	}
}

class dvdLanguage 
{
	var $name = "";
	var $audioFormat = "";
	var $type = "";
}


class amazonecs extends SitePlugin
{
	var $itemType = "";
	// Members below are used for XML parsing
	var $currentTags = array();
    var $itemsArray = array();
	var $itemCount = 0;
	var $inItem = FALSE;
	var $amazonErrorCode = "";
	var $amazonError = FALSE;
	var $continue = FALSE;

	var $siteDomain = NULL;
	var $siteAttributeType = NULL;

	function amazonecs($site_type)
	{
		parent::SitePlugin($site_type);
		
		$this->siteAttributeType = strtolower(fetch_site_attribute_type($site_type));
		$this->siteDomain = $this->getConfigValue('amazon_site_domain');
	}

	function queryListing($page_no, $items_per_page, $offset, $s_item_type, $search_vars_r)
	{
		if(strlen($search_vars_r[$this->siteAttributeType])>0)
		{
			$context_search_vars[$this->siteAttributeType] = $search_vars_r[$this->siteAttributeType];
			
			// if site domain is not defined, then its assumed that a domain input field will be provided.
			if(empty($siteDomain))
			{
				$context_search_vars['aecsdomain'] = $search_vars_r['aecsdomain'];
			}
			
			$this->addListingRow(NULL, NULL, NULL, $context_search_vars);
			return TRUE;
		}
		else
		{
			$siteDomain = ifempty($this->siteDomain, $search_vars_r['aecsdomain']);
			
			$index_type = ifempty($this->getConfigValue('item_type_to_index_map', $s_item_type), strtolower($s_item_type));

			// Can not continue if no amazon access key has been set
			if ($this->getConfigValue('amazon_access_key', 0) == '')
				return FALSE;
				
			$queryUrl = "http://webservices.amazon.".$siteDomain."/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=".$this->getConfigValue('amazon_access_key', 0)."&Operation=ItemSearch&SearchIndex=".$index_type."&ResponseGroup=Small,Images&ItemPage=".$page_no."&Keywords=".rawurlencode($search_vars_r['title']);
			
			$pageBuffer = $this->fetchURI($queryUrl);
			
			// no sense going any further here.
			if(strlen($pageBuffer)==0)
				return FALSE;

			$this->amazonParseXML($pageBuffer);
			if (!$this->amazonError)
			{
				foreach ($this->itemsArray as $item)
				{
					$context_search_vars[$this->siteAttributeType] = $item->asin;
			
					// if site domain is not defined, then its assumed that a domain input field will be provided.
					if(empty($this->siteDomain))
					{
						$context_search_vars['aecsdomain'] = $search_vars_r['aecsdomain'];
					}
			
					$this->addListingRow($item->title, $item->smallImageURL, NULL, $context_search_vars);
				}
					
				return TRUE;
			}
			else
			{
				return FALSE;
			}
		}
	}
	
	function queryItem($search_attributes_r, $s_item_type)
	{
		$index_type = ifempty($this->getConfigValue('item_type_to_index_map', $s_item_type), strtolower($s_item_type));
		$this->itemType = $index_type;

		// Can not continue if no amazon access key has been set
		if ($this->getConfigValue('amazon_access_key', 0) == '')
			return FALSE;

		$siteDomain = ifempty($this->siteDomain, $search_attributes_r['aecsdomain']);

		$queryUrl = "http://webservices.amazon.".$siteDomain."/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=".$this->getConfigValue('amazon_access_key', 0)."&Operation=ItemLookup&ResponseGroup=Large&ItemId=".$search_attributes_r[$this->siteAttributeType];
		
		// if search term is a 12 or 13 digits number then we assume it is an EAN number
		if (preg_match('/[0-9]{12,13}/', $search_attributes_r[$this->siteAttributeType]))
		{
			$queryUrl .= "&IdType=EAN";
		}

		$pageBuffer = $this->fetchURI($queryUrl);

		// no sense going any further here.
   		if(strlen($pageBuffer)==0)
   			return FALSE;
		
		$this->amazonParseXML($pageBuffer);
		if (!$amazonError && count($this->itemsArray) > 0)
		{
			$item = $this->itemsArray[0];
			
			$item->setSitePluginAttributes($this);
			
			if(empty($this->siteDomain))
			{
				$this->addItemAttribute('aecsdomain', $search_attributes_r['aecsdomain']);
			}
			
			return TRUE;
		}
		elseif (sizeof($this->itemsArray) <= 0)
		{
			$this->setError(get_opendb_lang_var('no_records_found'), $search_attributes_r[$this->siteAttributeType]);
		}
		else
		{
			return FALSE;
		}
	}	


	//******************************************
	// Xml parsing functions
	//******************************************
	// Parses xml answer from Amazon web service
	// $xmlcontent is a string containing xml answer
	function amazonParseXML($xmlContent)
	{
		// Reinitialize parsing values
		$this->itemsArray = array();
		$this->inItem = FALSE;
		$this->currentTags = array();
		$this->itemCount = 0;
		$this->amazonErrorCode = "";
		$this->amazonError = FALSE;
		$this->continue = FALSE;		
		
		$xmlParser = xml_parser_create();
		
		xml_set_object($xmlParser,$this);
		xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, false); 
		xml_set_element_handler($xmlParser, "amazonStartTag", "amazonEndTag"); 
		xml_set_character_data_handler($xmlParser, "amazonCharacterData");
		
		xml_parse($xmlParser, $xmlContent, true);
		
		xml_parser_free($xmlParser);
	}

	// Sets the current XML Tag, and pushes itself onto the Tag hierarchy
	function amazonStartTag($parser, $name, $attrs)
	{
		array_push($this->currentTags, $name);
		
		if($name == "Item")
		{
			$this->itemCount += 1;
			array_push($this->itemsArray, amazonItemFactory($this->itemType));
			$this->inItem = TRUE;
		}		
		elseif ($this->inItem)
		{			
			$this->itemsArray[$this->itemCount - 1]->startTag($name);
		}			
	} 

	function amazonCharacterData($parser, $data)
	{
		$currentCount = count($this->currentTags);
		$currentTag = $this->currentTags[$currentCount-1];
		$parentTag = "";
		if ($currentCount > 1)
			$parentTag = $this->currentTags[$currentCount - 2];

		$data = mb_convert_encoding($data, "ISO-8859-1", "UTF-8");		
			
		if ($this->inItem)
		{					
			$this->itemsArray[$this->itemCount - 1]->parsing($currentTag, $parentTag, $data, $this->continue);
		}
		else
		{
			switch($currentTag)
			{
				case "TotalResults":
					$this->setTotalCount(intval($data));
					break;
				case "Code":
					// Todo need to manage Amazon Errors
					if ($parentTag == 'Error' &&
						$data != "AWS.ECommerceService.NoExactMatches")
						$this->amazonError = TRUE;
					break;				
			}
		}

		$this->continue = TRUE;
	} 

   // If the XML Tag has ended, it is popped off the hierarchy
   function amazonEndTag($parser, $name)
   {
		$currentCount = count($this->currentTags);
		if($this->currentTags[$currentCount-1] == $name)
		{
			array_pop($this->currentTags);
		}

		if($name == "Item")
		{
			$this->inItem = FALSE;
		}
		elseif ($this->inItem)
		{					
			$this->itemsArray[$this->itemCount - 1]->endTag($name);
		}
				
		$this->continue = FALSE;
   }
}
?>
Return current item: Open Media Collectors Database