1 #!/usr/bin/env python2.5
4 from album_art_spec import getCoverArtFileName, getCoverArtThumbFileName, get_thumb_filename_for_path
10 libxml_available = True
12 libxml_available = False
22 LASTFM_APIKEY = "1e1d53528c86406757a6887addef0ace"
23 BASE_LASTFM = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo"
26 BASE_MSN = "http://www.bing.com/images/search?q="
27 MSN_MEDIUM = "+filterui:imagesize-medium"
28 MSN_SMALL = "+filterui:imagesize-medium"
29 MSN_SQUARE = "+filterui:aspect-square"
30 MSN_PHOTO = "+filterui:photo-graphics"
33 # http://www.lastfm.es/api/show?service=290
35 class MussorgskyAlbumArt:
38 bus = dbus.SessionBus ()
41 self.thumbnailer = LocalThumbnailer ()
44 self.thumbnailer = bus.get_object ('org.freedesktop.thumbnailer',
45 '/org/freedesktop/thumbnailer/Generic')
46 except dbus.exceptions.DBusException:
47 print "No thumbnailer available"
48 self.thumbnailer = None
50 def get_album_art (self, artist, album):
52 Return a tuple (album_art, thumbnail_album_art)
54 filename = getCoverArtFileName (album)
55 thumbnail = getCoverArtThumbFileName (album)
57 if (os.path.exists (filename)):
58 print "Album art already there " + filename
60 online_resource = self.__msn_images (artist, album)
62 print "Choosed:", online_resource
63 content = self.__get_url (online_resource)
65 print "Albumart: %s " % (filename)
66 self.__save_content_into_file (content, filename)
72 if (os.path.exists (thumbnail)):
73 print "Thumbnail exists " + thumbnail
75 if (not self.__request_thumbnail (filename)):
76 print "Failed doing thumbnail. Probably album art is not an image!"
80 return (filename, thumbnail)
82 def __last_fm (self, artist, album):
84 if (not libxml_available):
87 if (not album or len (album) < 1):
90 URL = BASE_LASTFM + "&api_key=" + LASTFM_APIKEY
91 if (artist and len(artist) > 1):
92 URL += "&artist=" + urllib.quote(artist)
94 URL += "&album=" + urllib.quote(album)
96 print "Retrieving: %s" % (URL)
97 result = self.__get_url (URL)
100 doc = libxml2.parseDoc (result)
101 image_nodes = doc.xpathEval ("//image[@size='large']")
102 if len (image_nodes) < 1:
105 return image_nodes[0].content
107 def __msn_images (self, artist, album):
109 good_artist = self.__clean_string_for_search (artist)
110 good_album = self.__clean_string_for_search (album)
112 if (good_album and good_artist):
113 full_try = BASE_MSN + good_album + "+" + good_artist + MSN_MEDIUM + MSN_SQUARE
114 print "Searching (album + artist): %s" % (full_try)
115 result = self.__get_url (full_try)
117 return self.__get_first_url_from_msn_results_page (result)
120 if (album.lower ().find ("greatest hit") != -1):
121 print "Ignoring '%s': too generic" % (album)
124 album_try = BASE_MSN + good_album + MSN_MEDIUM + MSN_SQUARE
125 print "Searching (album): %s" % (album_try)
126 result = self.__get_url (album_try)
128 return self.__get_first_url_from_msn_results_page (result)
131 artist_try = BASE_MSN + good_artist + "+CD+music" + MSN_SMALL + MSN_SQUARE + MSN_PHOTO
132 print "Searching (artist CD): %s" % (artist_try)
133 result = self.__get_url (artist_try)
135 return self.__get_first_url_from_msn_results_page (result)
140 def __get_first_url_from_msn_results_page (self, page):
142 current_option = None
146 while (security_limit > 0):
147 # Iterate until find a jpeg
148 start = page.find ("furl=", starting_at)
151 end = page.find ("\"", start + len ("furl="))
152 current_option = page [start + len ("furl="): end].replace ("amp;", "")
153 if (current_option.lower().endswith (".jpg") or
154 current_option.lower().endswith (".jpeg")):
155 return current_option
162 def __clean_string_for_search (self, text):
163 if (not text or len (text) < 1):
166 bad_stuff = "_:?\\-~"
169 clean = clean.replace (c, " ")
171 clean.replace ("/", "%2F")
172 clean = clean.replace (" CD1", "").replace(" CD2", "")
173 return urllib.quote(clean)
175 def __save_content_into_file (self, content, filename):
176 output = open (filename, 'w')
177 output.write (content)
180 def __get_url (self, url):
181 request = urllib2.Request (url)
182 request.add_header ('User-Agent', 'Mussorgsky/0.1 Test')
183 opener = urllib2.build_opener ()
185 return opener.open (request).read ()
189 def __request_thumbnail (self, filename):
190 if (not self.thumbnailer):
191 print "No thumbnailer available"
193 uri = "file://" + filename
194 handle = time.time ()
195 return self.thumbnailer.Queue ([uri], ["image/jpeg"], dbus.UInt32 (handle))
199 class LocalThumbnailer:
201 self.THUMBNAIL_SIZE = (124,124)
203 def Queue (self, uris, mimes, handle):
204 for i in range (0, len(uris)):
206 fullCoverFileName = uri[7:]
207 if (os.path.exists (fullCoverFileName)):
208 thumbFile = get_thumb_filename_for_path (fullCoverFileName)
210 image = Image.open (fullCoverFileName)
211 image = image.resize (self.THUMBNAIL_SIZE, Image.ANTIALIAS )
212 image.save( thumbFile, "JPEG" )
213 print "Thumbnail: " + thumbFile
221 if __name__ == "__main__":
223 from optparse import OptionParser
225 parser = OptionParser()
226 parser.add_option ("-p", "--print", dest="print_paths",
227 action="store_true", default=True,
228 help="Print the destination paths")
229 parser.add_option ("-r", "--retrieve", dest="retrieve",
230 action="store_true", default=False,
231 help="Try to retrieve the online content")
232 parser.add_option ("-a", "--artist", dest="artist", type="string",
233 help="ARTIST to look for", metavar="ARTIST")
234 parser.add_option ("-b", "--album", dest="album", type="string",
235 help="ALBUM to look for", metavar="ALBUM")
237 (options, args) = parser.parse_args ()
239 if (not options.artist and not options.album):
243 if options.print_paths and not options.retrieve:
244 print "Album art:", getCoverArtFileName (options.album)
245 print "Thumbnail:", getCoverArtThumbFileName (options.album)
249 maa = MussorgskyAlbumArt ()
250 maa.get_album_art (options.artist, options.album)