Update the Amazon cover fetcher to use the new ECS 4.0 API
---
 .../artdisplay/artdisplay/AmazonCoverArtSearch.py  |   93 +++++++++-----------
 1 files changed, 42 insertions(+), 51 deletions(-)

diff --git a/plugins/artdisplay/artdisplay/AmazonCoverArtSearch.py b/plugins/artdisplay/artdisplay/AmazonCoverArtSearch.py
index 8fb3e78..489d302 100644
--- a/plugins/artdisplay/artdisplay/AmazonCoverArtSearch.py
+++ b/plugins/artdisplay/artdisplay/AmazonCoverArtSearch.py
@@ -35,11 +35,13 @@ class AmazonCoverArtSearch (object):
 		self.searching = False
 		self.cancel = False
 		self.loader = loader
+		# "JP is the only locale that correctly takes UTF8 input. All other locales use LATIN1."
+		# http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1295&categoryID=117
 		self._supportedLocales = {
-			"en_US" : ("us", "xml.amazon.com", "music"),
-			"en_GB" : ("uk", "xml-eu.amazon.com", "music"),
-			"de" : ("de", "xml-eu.amazon.com", "music"),
-			"ja" : ("jp", "xml.amazon.co.jp", "music-jp")
+			"en_US" : ("com", "latin1"),
+			"en_GB" : ("co.uk", "latin1"),
+			"de" : ("de", "latin1"),
+			"ja" : ("jp", "utf8")
 		}
 		self.db = None
 		self.entry = None
@@ -55,10 +57,7 @@ class AmazonCoverArtSearch (object):
 				if self._supportedLocales.has_key (lang):
 					lc_id = lang
 
-		lc_host = self._supportedLocales[lc_id][1]
-		lc_name = self._supportedLocales[lc_id][0]
-		lc_mode = self._supportedLocales[lc_id][2]
-		return ((lc_host, lc_name, lc_mode))
+		return self._supportedLocales[lc_id]
 
 	def search (self, db, entry, on_search_completed_callback, *args):
 		self.searching = True
@@ -127,15 +126,17 @@ class AmazonCoverArtSearch (object):
 		self.search_next ();
 
 	def __build_url (self, keyword):
-		(lc_host, lc_name, lc_mode) = self.__get_locale ()
-
-		url = "http://" + lc_host + "/onca/xml3?f=xml"
-		url += "&t=%s" % ASSOCIATE
-		url += "&dev-t=%s" % LICENSE_KEY
-		url += "&type=%s" % 'lite'
-		url += "&locale=%s" % lc_name
-		url += "&mode=%s" % lc_mode
-		url += "&%s=%s" % ('KeywordSearch', urllib.quote (keyword))
+		(tld, encoding) = self.__get_locale ()
+
+		url = "http://ecs.amazonaws." + tld + "/onca/xml" \
+		      "?Service=AWSECommerceService"              \
+		      "&AWSAccessKeyId=" + LICENSE_KEY +          \
+		      "&AssociateTag=" + ASSOCIATE +              \
+		      "&ResponseGroup=Images,ItemAttributes"      \
+		      "&Operation=ItemSearch"                     \
+		      "&SearchIndex=Music"                        \
+		      "&Keywords=" + urllib.quote (keyword.encode (encoding, 'replace'))
+		print "url: '%s'" % (url)
 
 		return url
 
@@ -161,26 +162,21 @@ class AmazonCoverArtSearch (object):
 
 	def __unmarshal (self, element):
 		rc = Bag ()
-		if isinstance (element, minidom.Element) and (element.tagName == 'Details'):
-			rc.URL = element.attributes["url"].value
 		childElements = [e for e in element.childNodes if isinstance (e, minidom.Element)]
 		if childElements:
 			for child in childElements:
 				key = child.tagName
 				if hasattr (rc, key):
-					if type (getattr (rc, key)) <> type ([]):
+					if not isinstance (getattr (rc, key), list):
 						setattr (rc, key, [getattr (rc, key)])
-					setattr (rc, key, getattr (rc, key) + [self.__unmarshal (child)])
-				elif isinstance(child, minidom.Element) and (child.tagName == 'Details'):
-					setattr (rc,key,[self.__unmarshal(child)])
+					getattr (rc, key).append (self.__unmarshal (child))
+				# get_best_match_urls() wants a list, even if there is only one item/artist
+				elif (child.tagName == "Item") or (child.tagName == "Artist"):
+					setattr (rc, key, [self.__unmarshal(child)])
 				else:
 					setattr (rc, key, self.__unmarshal(child))
 		else:
 			rc = "".join ([e.data for e in element.childNodes if isinstance (e, minidom.Text)])
-			if element.tagName == 'SalesRank':
-				rc = rc.replace ('.', '')
-				rc = rc.replace (',', '')
-				rc = int (rc)
 		return rc
 
 	def on_search_response (self, result_data):
@@ -194,14 +190,14 @@ class AmazonCoverArtSearch (object):
 			self.search_next()
 			return
 		
-		data = self.__unmarshal (xmldoc).ProductInfo
+		data = self.__unmarshal (xmldoc).ItemSearchResponse.Items
 
-		if hasattr(data, 'ErrorMsg'):
+		if hasattr (data.Request, "Errors"):
 			# Search was unsuccessful, try next keyword
 			self.search_next ()
 		else:
 			# We got some search results
-			self.on_search_results (data.Details)
+			self.on_search_results (data.Item)
 
 	def on_search_results (self, results):
 		self.on_search_completed (results)
@@ -224,10 +220,8 @@ class AmazonCoverArtSearch (object):
 		return s
 
 	def __valid_match (self, item):
-		if item.ImageUrlLarge == "" and item.ImageUrlMedium == "":
-			print "%s doesn't have image URLs; ignoring" % (item.URL)
-			return False
-		return True
+		return (hasattr (item, "LargeImage") or hasattr (item, "MediumImage")) \
+		       and hasattr (item, "ItemAttributes")
 
 	def get_best_match_urls (self, search_results):
 		# Default to "no match", our results must match our criteria
@@ -238,11 +232,11 @@ class AmazonCoverArtSearch (object):
 			if self.search_album != _("Unknown"):
 				album_check = self.__tidy_up_string (self.search_album)
 				for item in search_results:
+					if not hasattr (item.ItemAttributes, "Title"):
+						continue
 
-					# Check for album name in ProductName
-					product_name = self.__tidy_up_string (item.ProductName)
-
-					if product_name == album_check:
+					album = self.__tidy_up_string (item.ItemAttributes.Title)
+					if album == album_check:
 						# Found exact album, can not get better than that
 						best_match = item
 						break
@@ -250,8 +244,9 @@ class AmazonCoverArtSearch (object):
 					# Check the results for both an album name that contains the name
 					# we're searching for, and an album name that's a substring of the
 					# name we're searching for
-					elif (best_match is None) and (product_name.find (album_check) != -1 
-                                                                  or album_check.find (product_name) != -1):
+					elif (best_match is None) and \
+					     (album.find (album_check) != -1 or
+					      album_check.find (album) != -1):
 						best_match = item
 
 			# If we still have no definite hit, use first result where artist matches
@@ -261,13 +256,10 @@ class AmazonCoverArtSearch (object):
 					# Check if artist appears in the Artists list
 					hit = False
 					for item in search_results:
+						if not hasattr (item.ItemAttributes, "Artist"):
+							continue
 
-						if type (item.Artists.Artist) <> type ([]):
-							artists = [item.Artists.Artist]
-						else:
-							artists = item.Artists.Artist
-
-						for artist in artists:
+						for artist in item.ItemAttributes.Artist:
 							artist = self.__tidy_up_string (artist)
 							if artist.find (artist_check) != -1:
 								best_match = item
@@ -276,10 +268,9 @@ class AmazonCoverArtSearch (object):
 						if hit:
 							break
 
-			if best_match:
-				return filter(lambda x: x != "", [item.ImageUrlLarge, item.ImageUrlMedium])
-			else:
-				return []
+			return [getattr (best_match, size).URL for size in ("LargeImage", "MediumImage")
+			        if hasattr (best_match, size)]
 
-		except TypeError:
+		except TypeError, e:
+			print "TypeError: %s" % (e)
 			return []
