/* You may freely copy, distribute, modify and use this class as long as the original author attribution remains intact. See message below. Copyright (C) 2005 Christian Pesch. All Rights Reserved. */ package slash.metamusic.coverdb; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.input.SAXBuilder; import slash.metamusic.distance.Levenshtein; import slash.metamusic.util.DiscIndexHelper; import slash.metamusic.util.URLLoader; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.List; import java.util.logging.Logger; /** * A client that calls amazon for music information. * * @author Christian Pesch * @version $Id: AmazonMusicClient.java 925 2006-12-29 14:37:25Z cpesch $ */ public class AmazonMusicClient { /** * Logging output */ protected static final Logger log = Logger.getLogger(AmazonMusicClient.class.getName()); private static final String WEBSERVICE_URL = "http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&SubscriptionId=0RD4MWP106NB420YMMR2&Operation=ItemSearch&SearchIndex=Music&Keywords="; private static final Namespace ns = Namespace.getNamespace("http://webservices.amazon.com/AWSECommerceService/2005-10-05"); private static long lastWebserviceCall = 0; private static String encode(String request) throws UnsupportedEncodingException { return URLEncoder.encode(request, "UTF-8"); } private static URL asURL(String url) throws MalformedURLException { return new URL(url); } protected Document fetchDocument(String url) throws IOException { SAXBuilder builder = new SAXBuilder(); URL queryUrl = asURL(url); BufferedReader in = new BufferedReader(new InputStreamReader(queryUrl.openStream())); try { return builder.build(in); } catch (JDOMException e) { throw new IOException("Cannot build up document for " + url + ": " + e.getMessage()); } finally { in.close(); } } public SearchResult search(String artist, String album) throws IOException { if (artist == null || album == null) return null; artist = artist.trim(); album = DiscIndexHelper.removeDiscIndexPostfix(album); album = album.trim(); Document doc1, doc2; synchronized (this) { // ensure we're doing not more than 1 call per second while (true) { long wait = lastWebserviceCall + 1000 - System.currentTimeMillis(); if (wait <= 0) break; try { Thread.sleep(wait); } catch (InterruptedException e) { // doesn't matter } } try { doc1 = fetchDocument(WEBSERVICE_URL + encode(artist + " " + album) + "&ResponseGroup=Images"); doc2 = fetchDocument(WEBSERVICE_URL + encode(artist + " " + album)); } finally { lastWebserviceCall = System.currentTimeMillis(); } } List<Element> list1 = doc1.getRootElement().getChild("Items", ns).getChildren("Item", ns); Element items2 = doc2.getRootElement().getChild("Items", ns); List<Element> list2 = items2.getChildren("Item", ns); log.fine("Amazon music search for '" + artist + "' and '" + album + "' has " + items2.getChild("TotalResults", ns).getText() + " results"); for (Element e1 : list1) { for (Element e2 : list2) { if (e1.getChild("MediumImage", ns) != null && e1.getChild("ASIN", ns).getText().equals(e2.getChild("ASIN", ns).getText())) { SearchResult result = new SearchResult(); Element attributes = e2.getChild("ItemAttributes", ns); if (attributes.getChild("Artist", ns) != null) { result.artist = attributes.getChild("Artist", ns).getText(); } else if (attributes.getChild("Creator", ns) != null) { result.artist = attributes.getChild("Creator", ns).getText(); } else if (attributes.getChild("Author", ns) != null) { result.artist = attributes.getChild("Author", ns).getText(); } result.album = attributes.getChild("Title", ns).getText(); if (attributes.getChild("Manufacturer", ns) != null) { result.publisher = attributes.getChild("Manufacturer", ns).getText(); } if (e1.getChild("SmallImage", ns) != null) { result.smallImageUrl = asURL(e1.getChild("SmallImage", ns).getChild("URL", ns).getText()); } result.mediumImageUrl = asURL(e1.getChild("MediumImage", ns).getChild("URL", ns).getText()); if (e1.getChild("LargeImage", ns) != null) { result.largeImageUrl = asURL(e1.getChild("LargeImage", ns).getChild("URL", ns).getText()); } if (result.artist != null && Levenshtein.distance(artist, result.artist) < 3 && result.album != null && Levenshtein.distance(album, result.album) < 3) { log.info("Amazon music search for '" + artist + "' and '" + album + "' result is " + result); return result; } else log.fine("Skipping result for '" + artist + "' and '" + album + "': " + result); } } } return null; } public byte[] downloadCover(String artist, String album) { try { SearchResult result = search(artist, album); if (result != null) return result.getLargestAvailableImage(); } catch (IOException e) { log.severe("Cannot search cover for '" + artist + "' and '" + album + "': " + e.getMessage()); } return null; } public String searchPublisher(String artist, String album) { try { SearchResult result = search(artist, album); if (result != null) return result.publisher; } catch (IOException e) { log.severe("Cannot search publisher for '" + artist + "' and '" + album + ": " + e.getMessage()); } return null; } public static class SearchResult { public String artist; public String album; public String publisher; public URL smallImageUrl; public URL mediumImageUrl; public URL largeImageUrl; private byte[] getBytes(URL url) { try { if (url != null) return URLLoader.getContents(url.openStream()); } catch (IOException e) { log.severe("Cannot fetch " + url + ": " + e.getMessage()); } return null; } public byte[] getSmallImage() { return getBytes(smallImageUrl); } public byte[] getMediumImage() { return getBytes(mediumImageUrl); } public byte[] getLargeImage() { return getBytes(largeImageUrl); } public byte[] getLargestAvailableImage() { byte[] result = getLargeImage(); if (result == null) result = getMediumImage(); if (result == null) result = getSmallImage(); return result; } public String toString() { return artist + " - " + album + " - " + publisher + "\n" + smallImageUrl + "\n" + mediumImageUrl + "\n" + largeImageUrl; } } public static void main(String[] args) throws Exception { if (args.length != 2) { System.out.println("slash.metamusic.coverdb.AmazonMusicClient <artist> <album>"); System.exit(1); } AmazonMusicClient client = new AmazonMusicClient(); SearchResult result = client.search(args[0], args[1]); if (result != null) { byte[] largestAvailableImage = result.getLargestAvailableImage(); if (largestAvailableImage != null) System.out.println("Largest available image has " + largestAvailableImage.length + " bytes"); } System.exit(0); } }