1 #!/usr/bin/env python2.5
3 from album_art_spec import getCoverArtFileName, getCoverArtThumbFileName, get_thumb_filename_for_path
4 from utils import UrllibWrapper
11 libxml_available = True
13 libxml_available = False
19 print "Please install python-imaging package"
23 LASTFM_APIKEY = "1e1d53528c86406757a6887addef0ace"
24 BASE_LASTFM = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo"
27 BASE_MSN = "http://www.bing.com/images/search?q="
28 MSN_MEDIUM = "+filterui:imagesize-medium"
29 MSN_SMALL = "+filterui:imagesize-medium"
30 MSN_SQUARE = "+filterui:aspect-square"
31 MSN_PHOTO = "+filterui:photo-graphics"
33 CACHE_LOCATION = os.path.join (os.getenv ("HOME"), ".cache", "mussorgsky")
35 # http://www.lastfm.es/api/show?service=290
40 class AADownloadThread (threading.Thread):
42 def __init__ (self, url, counter):
43 threading.Thread.__init__ (self, target=self.grab_image, args=(url,))
44 self.thumbnailer = LocalThumbnailer ()
45 self.counter = counter
46 self.image_path = None
47 self.thumb_path = None
48 self.urllib_wrapper = UrllibWrapper ()
50 def grab_image (self, image_url):
51 print "Working", self.counter
52 image = self.urllib_wrapper.get_url (image_url)
54 self.image_path = os.path.join (CACHE_LOCATION, "alternative-" + str(self.counter))
55 self.thumb_path = os.path.join (CACHE_LOCATION, "alternative-" + str(self.counter) + "thumb")
56 self.urllib_wrapper.save_content_into_file (image, self.image_path)
57 self.thumbnailer.create (self.image_path, self.thumb_path)
59 def get_result (self):
60 return self.image_path, self.thumb_path
64 class MussorgskyAlbumArt:
67 bus = dbus.SessionBus ()
69 if (not os.path.exists (CACHE_LOCATION)):
70 os.makedirs (CACHE_LOCATION)
72 self.thumbnailer = LocalThumbnailer ()
73 self.urllib_wrapper = UrllibWrapper ()
75 def get_album_art (self, artist, album, force=False):
77 Return a tuple (album_art, thumbnail_album_art)
79 filename = getCoverArtFileName (album)
80 thumbnail = getCoverArtThumbFileName (album)
82 album_art_available = False
83 if (os.path.exists (filename) and not force):
84 print "Album art already there " + filename
85 album_art_available = True
87 results_page = self.__msn_images (artist, album)
88 for online_resource in self.__get_url_from_msn_results_page (results_page):
89 print "Choosed:", online_resource
90 content = self.urllib_wrapper.get_url (online_resource)
92 print "Albumart: %s " % (filename)
93 self.urllib_wrapper.save_content_into_file (content, filename)
94 album_art_available = True
97 if (not album_art_available):
100 if (not os.path.exists (thumbnail) or force or album_art_available):
101 if (not self.__request_thumbnail (filename)):
102 print "Failed doing thumbnail. Probably album art is not an image!"
106 print "Thumbnail exists (and probably valid) " + thumbnail
108 return (filename, thumbnail)
111 def get_alternatives (self, artist, album, max_alternatives=4):
113 return a list of paths of possible album arts
116 results_page = self.__msn_images (artist, album)
118 for image_url in self.__get_url_from_msn_results_page (results_page):
120 # Some searches doesn't return anything at all!
123 if (counter >= max_alternatives):
126 t = AADownloadThread (image_url, counter)
136 yield t.get_result ()
139 def save_alternative (self, artist, album, img_path, thumb_path):
140 if not os.path.exists (img_path) or not os.path.exists (thumb_path):
141 print "**** CRITICAL **** image in path", path, "doesn't exist!"
144 filename = getCoverArtFileName (album)
145 thumbnail = getCoverArtThumbFileName (album)
147 os.rename (img_path, filename)
148 os.rename (thumb_path, thumbnail)
150 return (filename, thumbnail)
153 def __msn_images (self, artist, album):
155 good_artist = self.__clean_string_for_search (artist)
156 good_album = self.__clean_string_for_search (album)
158 if (good_album and good_artist):
159 full_try = BASE_MSN + good_album + "+" + good_artist + MSN_MEDIUM + MSN_SQUARE
160 print "Searching (album + artist): %s" % (full_try)
161 result = self.urllib_wrapper.get_url (full_try)
162 if (result and result.find ("no_results") == -1):
166 if (album.lower ().find ("greatest hit") != -1):
167 print "Ignoring '%s': too generic" % (album)
170 album_try = BASE_MSN + good_album + MSN_MEDIUM + MSN_SQUARE
171 print "Searching (album): %s" % (album_try)
172 result = self.urllib_wrapper.get_url (album_try)
173 if (result and result.find ("no_results") == -1):
177 artist_try = BASE_MSN + good_artist + "+CD+music" + MSN_SMALL + MSN_SQUARE + MSN_PHOTO
178 print "Searching (artist CD): %s" % (artist_try)
179 result = self.urllib_wrapper.get_url (artist_try)
180 if (result and result.find ("no_results") == -1):
186 def __get_url_from_msn_results_page (self, page):
191 current_option = None
194 # 500 is just a safe limit
195 for i in range (0, 500):
196 # Iterate until find a jpeg
197 start = page.find ("furl=", starting_at)
200 end = page.find ("\"", start + len ("furl="))
201 current_option = page [start + len ("furl="): end].replace ("amp;", "")
202 if (current_option.lower().endswith (".jpg") or
203 current_option.lower().endswith (".jpeg")):
208 def __clean_string_for_search (self, text):
209 if (not text or len (text) < 1):
212 bad_stuff = "_:?\\-~"
215 clean = clean.replace (c, " ")
217 clean.replace ("/", "%2F")
218 clean = clean.replace (" CD1", "").replace(" CD2", "")
219 return urllib.quote(clean)
221 def __request_thumbnail (self, filename):
222 thumbFile = get_thumb_filename_for_path (filename)
223 return self.thumbnailer.create (filename, thumbFile)
227 class LocalThumbnailer:
229 self.THUMBNAIL_SIZE = (124,124)
231 def create (self, fullCoverFileName, thumbFile):
232 if (os.path.exists (fullCoverFileName)):
234 image = Image.open (fullCoverFileName)
235 image = image.resize (self.THUMBNAIL_SIZE, Image.ANTIALIAS )
236 image.save (thumbFile, "JPEG")
237 print "Thumbnail: " + thumbFile
245 if __name__ == "__main__":
247 from optparse import OptionParser
249 parser = OptionParser()
250 parser.add_option ("-p", "--print", dest="print_paths",
251 action="store_true", default=True,
252 help="Print the destination paths")
253 parser.add_option ("-r", "--retrieve", dest="retrieve",
254 action="store_true", default=False,
255 help="Try to retrieve the online content")
256 parser.add_option ("-m", "--multiple", dest="multiple",
257 action="store_true", default=False,
258 help="Show more than one option")
259 parser.add_option ("-a", "--artist", dest="artist", type="string",
260 help="ARTIST to look for", metavar="ARTIST")
261 parser.add_option ("-b", "--album", dest="album", type="string",
262 help="ALBUM to look for", metavar="ALBUM")
264 (options, args) = parser.parse_args ()
266 if (not options.artist and not options.album):
270 if (options.multiple and options.retrieve):
271 print "Multiple and retrieve are incompatible"
275 if options.print_paths and not options.retrieve:
276 print "Album art:", getCoverArtFileName (options.album)
277 print "Thumbnail:", getCoverArtThumbFileName (options.album)
280 maa = MussorgskyAlbumArt ()
281 maa.get_album_art (options.artist, options.album)
285 maa = MussorgskyAlbumArt ()
286 for (img, thumb) in maa.get_alternatives (options.artist, options.album, 5):