1 #!/usr/bin/env python2.5
3 from aa_spec import getCoverArtFileName, getCoverArtThumbFileName, get_thumb_filename_for_path
4 from utils import UrllibWrapper
11 libxml_available = True
13 libxml_available = False
20 print "Please install python-imaging package"
24 LASTFM_APIKEY = "1e1d53528c86406757a6887addef0ace"
25 BASE_LASTFM = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo"
28 BASE_MSN = "http://www.bing.com/images/search?q="
29 MSN_MEDIUM = "+filterui:imagesize-medium"
30 MSN_SMALL = "+filterui:imagesize-medium"
31 MSN_SQUARE = "+filterui:aspect-square"
32 MSN_PHOTO = "+filterui:photo-graphics"
34 CACHE_LOCATION = os.path.join (os.getenv ("HOME"), ".cache", "mussorgsky")
36 # http://www.lastfm.es/api/show?service=290
41 class AADownloadThread (threading.Thread):
43 def __init__ (self, url, artist, album, counter):
44 threading.Thread.__init__ (self, target=self.grab_image, args=(url,))
45 self.thumbnailer = LocalThumbnailer ()
46 self.counter = counter
47 self.artistName = artist.replace (" ", "_")
48 self.albumName = album.replace (" ", "_")
49 self.image_path = None
50 self.thumb_path = None
51 self.urllib_wrapper = UrllibWrapper ()
53 def grab_image (self, image_url):
54 print "Working", self.counter
55 image = self.urllib_wrapper.get_url (image_url)
57 self.image_path = os.path.join (CACHE_LOCATION, self.artistName + self.albumName + str(self.counter))
58 self.thumb_path = os.path.join (CACHE_LOCATION, self.artistName + self.albumName + str(self.counter) + "thumb")
59 self.urllib_wrapper.save_content_into_file (image, self.image_path)
60 self.thumbnailer.create (self.image_path, self.thumb_path)
62 def get_result (self):
63 return self.image_path, self.thumb_path
67 class MussorgskyAlbumArt:
70 bus = dbus.SessionBus ()
72 if (not os.path.exists (CACHE_LOCATION)):
73 os.makedirs (CACHE_LOCATION)
75 self.thumbnailer = LocalThumbnailer ()
76 self.urllib_wrapper = UrllibWrapper ()
78 def get_possible_url (self, artist, album, amount=4):
79 results_page = self.__msn_images (artist, album)
80 return self.__get_url_from_msn_results_page (results_page)
83 def get_album_art (self, artist, album, force=False):
85 Return a tuple (album_art, thumbnail_album_art)
87 filename = getCoverArtFileName (album)
88 thumbnail = getCoverArtThumbFileName (album)
90 album_art_available = False
91 if (os.path.exists (filename) and not force):
92 print "Album art already there " + filename
93 album_art_available = True
95 results_page = self.__msn_images (artist, album)
96 for online_resource in self.__get_url_from_msn_results_page (results_page):
97 print "Choosed:", online_resource
98 content = self.urllib_wrapper.get_url (online_resource)
100 print "Albumart: %s " % (filename)
101 self.urllib_wrapper.save_content_into_file (content, filename)
102 album_art_available = True
105 if (not album_art_available):
108 if (not os.path.exists (thumbnail) or force or album_art_available):
109 if (not self.__request_thumbnail (filename)):
110 print "Failed doing thumbnail. Probably album art is not an image!"
114 print "Thumbnail exists (and probably valid) " + thumbnail
116 return (filename, thumbnail)
119 def get_alternatives (self, artist, album, max_alternatives=4):
121 return a list of paths of possible album arts
123 results_page = self.__msn_images (artist, album)
124 return self.__process_results_page (results_page, artist, album, max_alternatives)
126 def get_alternatives_free_text (self, search_text, max_alternatives=4):
127 results_page = self.__msn_images_free_text (search_text)
128 return self.__process_results_page (results_page, max_alternatives)
130 def __process_results_page (self, results_page, artist, album, max_alternatives):
133 for image_url in self.__get_url_from_msn_results_page (results_page):
135 # Some searches doesn't return anything at all!
138 if (counter >= max_alternatives):
141 t = AADownloadThread (image_url, artist, album, counter)
151 yield t.get_result ()
154 def save_alternative (self, artist, album, img_path, thumb_path):
156 This is done now in the controller
158 if not os.path.exists (img_path) or not os.path.exists (thumb_path):
159 print "**** CRITICAL **** image in path", path, "doesn't exist!"
162 filename = getCoverArtFileName (album)
163 thumbnail = getCoverArtThumbFileName (album)
165 os.rename (img_path, filename)
166 os.rename (thumb_path, thumbnail)
168 return (filename, thumbnail)
170 def reset_alternative (self, artist, album):
172 for filepath in [getCoverArtFileName (album),
173 getCoverArtThumbFileName (album)]:
174 if os.path.exists (filepath):
177 def __msn_images (self, artist, album):
179 good_artist = self.__clean_string_for_search (artist)
180 good_album = self.__clean_string_for_search (album)
182 if (good_album and good_artist):
183 full_try = BASE_MSN + good_album + "+" + good_artist + MSN_MEDIUM + MSN_SQUARE
184 print "Searching (album + artist): %s" % (full_try)
185 result = self.urllib_wrapper.get_url (full_try)
186 if (result and result.find ("no_results") == -1):
190 if (album.lower ().find ("greatest hit") != -1):
191 print "Ignoring '%s': too generic" % (album)
194 album_try = BASE_MSN + good_album + MSN_MEDIUM + MSN_SQUARE
195 print "Searching (album): %s" % (album_try)
196 result = self.urllib_wrapper.get_url (album_try)
197 if (result and result.find ("no_results") == -1):
201 artist_try = BASE_MSN + good_artist + "+CD+music" + MSN_SMALL + MSN_SQUARE + MSN_PHOTO
202 print "Searching (artist CD): %s" % (artist_try)
203 result = self.urllib_wrapper.get_url (artist_try)
204 if (result and result.find ("no_results") == -1):
209 def __msn_images_free_text (self, search_text):
210 full_try = BASE_MSN + self.__clean_string_for_search (search_text) + MSN_MEDIUM + MSN_SQUARE
211 result = self.urllib_wrapper.get_url (full_try)
215 def __get_url_from_msn_results_page (self, page):
219 current_option = None
222 # 500 is just a safe limit
223 for i in range (0, 500):
224 # Iterate until find a jpeg
225 start = page.find ("imgurl:"", starting_at)
228 end = page.find ("&", start + len ("imgurl:""))
229 current_option = page [start + len ("imgurl:""): end].replace ("amp;", "")
230 if (current_option.lower().endswith (".jpg") or
231 current_option.lower().endswith (".jpeg")):
236 def __clean_string_for_search (self, text):
237 if (not text or len (text) < 1):
240 bad_stuff = "_:?\\-~"
243 clean = clean.replace (c, " ")
245 clean.replace ("/", "%2F")
246 clean = clean.replace (" CD1", "").replace(" CD2", "")
247 return urllib.quote(clean)
249 def __request_thumbnail (self, filename):
250 thumbFile = get_thumb_filename_for_path (filename)
251 return self.thumbnailer.create (filename, thumbFile)
255 class LocalThumbnailer:
257 self.THUMBNAIL_SIZE = (124,124)
259 def create (self, fullCoverFileName, thumbFile):
260 if (os.path.exists (fullCoverFileName)):
262 image = Image.open (fullCoverFileName)
263 image = image.resize (self.THUMBNAIL_SIZE, Image.ANTIALIAS )
264 image.save (thumbFile, "JPEG")
265 print "Thumbnail: " + thumbFile
273 if __name__ == "__main__":
275 from optparse import OptionParser
277 parser = OptionParser()
278 parser.add_option ("-p", "--print", dest="print_paths",
279 action="store_true", default=True,
280 help="Print the destination paths")
281 parser.add_option ("-r", "--retrieve", dest="retrieve",
282 action="store_true", default=False,
283 help="Try to retrieve the online content")
284 parser.add_option ("-m", "--multiple", dest="multiple",
285 action="store_true", default=False,
286 help="Show more than one option")
287 parser.add_option ("-a", "--artist", dest="artist", type="string",
288 help="ARTIST to look for", metavar="ARTIST")
289 parser.add_option ("-b", "--album", dest="album", type="string",
290 help="ALBUM to look for", metavar="ALBUM")
292 (options, args) = parser.parse_args ()
294 if (not options.artist and not options.album):
298 if (options.multiple and options.retrieve):
299 print "Multiple and retrieve are incompatible"
303 if options.print_paths and not options.retrieve:
304 print "Album art:", getCoverArtFileName (options.album)
305 print "Thumbnail:", getCoverArtThumbFileName (options.album)
308 maa = MussorgskyAlbumArt ()
309 maa.get_album_art (options.artist, options.album)
313 maa = MussorgskyAlbumArt ()
314 for (img, thumb) in maa.get_alternatives (options.artist, options.album, 5):