Move the qml/* files to the src folder. Now it is the main version
[mussorgsky] / src / aa_search.py
1 #!/usr/bin/env python2.5
2 import os
3 from utils import UrllibWrapper
4 import dbus, time
5 import string
6 import urllib
7
8
9 LASTFM_APIKEY = "1e1d53528c86406757a6887addef0ace"
10 BASE_LASTFM = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo"
11
12
13 BASE_MSN = "http://www.bing.com/images/search?q="
14 MSN_MEDIUM = "+filterui:imagesize-medium"
15 MSN_SMALL = "+filterui:imagesize-medium"
16 MSN_SQUARE = "+filterui:aspect-square"
17 MSN_PHOTO = "+filterui:photo-graphics"
18
19 CACHE_LOCATION = os.path.join (os.getenv ("HOME"), ".cache", "mussorgsky")
20 # LastFM:
21 # http://www.lastfm.es/api/show?service=290
22 #
23
24
25 import threading
26 class AADownloadThread (threading.Thread):
27
28     def __init__ (self, url, artist, album, counter):
29         threading.Thread.__init__ (self, target=self.grab_image, args=(url,))
30         self.counter = counter
31         self.artistName = artist.replace (" ", "_")
32         self.albumName = album.replace (" ", "_")
33         self.image_path = None
34         self.urllib_wrapper = UrllibWrapper ()
35
36     def grab_image (self, image_url):
37         print "Working", self.counter
38         image = self.urllib_wrapper.get_url (image_url)
39         if (image):
40             self.image_path = os.path.join (CACHE_LOCATION, self.artistName + self.albumName + str(self.counter))
41             self.urllib_wrapper.save_content_into_file (image, self.image_path)
42         
43     def get_result (self):
44         return self.image_path
45
46
47
48 class MussorgskyAlbumArt:
49
50     def __init__ (self):
51         bus = dbus.SessionBus ()
52
53         if (not os.path.exists (CACHE_LOCATION)):
54             os.makedirs (CACHE_LOCATION)
55             
56         self.urllib_wrapper = UrllibWrapper ()
57
58     def get_possible_url (self, artist, album, amount=4):
59             results_page = self.__msn_images (artist, album)
60             return self.__get_url_from_msn_results_page (results_page)
61         
62
63     def get_album_art (self, albumItem, force=False):
64         """
65         Save the first available result as the albumart for that item
66         """
67         filename = albumItem.get_aa().get_media_art_path ()
68         if (os.path.exists (filename) and not force):
69             print "Album art already there " + filename
70             return
71
72         results_page = self.__msn_images (albumItem.artist, albumItem.title)
73         for online_resource in self.__get_url_from_msn_results_page (results_page):
74             print "Trying:", online_resource
75             content = self.urllib_wrapper.get_url (online_resource)
76             if (content):
77                 print "Saved on: %s " % (filename)
78                 self.urllib_wrapper.save_content_into_file (content, filename)
79                 albumItem.album_art = filename
80                 break
81
82     def get_alternatives (self, artist, album, max_alternatives=4):
83         """
84         return a list of images in the local disk
85         """
86         results_page = self.__msn_images (artist, album)
87         return self.__process_results_page (results_page, artist, album, max_alternatives)
88
89     def get_alternatives_free_text (self, search_text, max_alternatives=4):
90         results_page = self.__msn_images_free_text (search_text)
91         return self.__process_results_page (results_page, max_alternatives)
92
93     def __process_results_page (self, results_page, artist, album, max_alternatives):
94         counter = 0
95         threads = []
96         for image_url in self.__get_url_from_msn_results_page (results_page):
97             if (not image_url):
98                 # Some searches doesn't return anything at all!
99                 break
100
101             if (counter >= max_alternatives):
102                 break
103             
104             t = AADownloadThread (image_url, artist, album, counter)
105             t.start ()
106             threads.append (t)
107             counter += 1
108
109         for t in threads:
110             t.join (5)
111             if (t.isAlive ()):
112                 yield None
113             else:
114                 yield t.get_result ()
115             
116
117     def save_alternative (self, artist, album, img_path, thumb_path):
118         """
119         This is done now in the controller
120         """
121         if not os.path.exists (img_path) or not os.path.exists (thumb_path):
122             print "**** CRITICAL **** image in path", path, "doesn't exist!"
123             return (None, None)
124         
125         filename = getCoverArtFileName (album)
126         thumbnail = getCoverArtThumbFileName (album)
127
128         os.rename (img_path, filename)
129         os.rename (thumb_path, thumbnail)
130
131         return (filename, thumbnail)
132
133     def reset_alternative (self, artist, album):
134
135         for filepath in [getCoverArtFileName (album),
136                          getCoverArtThumbFileName (album)]:
137             if os.path.exists (filepath):
138                 os.remove (filepath)
139
140     def __msn_images (self, artist, album):
141
142         good_artist = self.__clean_string_for_search (artist)
143         good_album = self.__clean_string_for_search (album)
144         
145         if (good_album and good_artist):
146             full_try = BASE_MSN + good_album + "+" + good_artist + MSN_MEDIUM + MSN_SQUARE
147             print "Searching (album + artist): %s" % (full_try)
148             result = self.urllib_wrapper.get_url (full_try)
149             if (result and result.find ("no_results") == -1):
150                 return result
151
152         if (album):
153             if (album.lower ().find ("greatest hit") != -1):
154                 print "Ignoring '%s': too generic" % (album)
155                 pass
156             else:
157                 album_try = BASE_MSN + good_album + MSN_MEDIUM + MSN_SQUARE
158                 print "Searching (album): %s" % (album_try)
159                 result = self.urllib_wrapper.get_url (album_try)
160                 if (result and result.find ("no_results") == -1):
161                     return result
162             
163         if (artist):
164             artist_try = BASE_MSN + good_artist + "+CD+music"  + MSN_SMALL + MSN_SQUARE + MSN_PHOTO
165             print "Searching (artist CD): %s" % (artist_try)
166             result = self.urllib_wrapper.get_url (artist_try)
167             if (result and result.find ("no_results") == -1):
168                 return result
169         
170         return None
171
172     def __msn_images_free_text (self, search_text):
173         full_try = BASE_MSN + self.__clean_string_for_search (search_text) + MSN_MEDIUM + MSN_SQUARE
174         result = self.urllib_wrapper.get_url (full_try)
175         return result
176     
177
178     def __get_url_from_msn_results_page (self, page):
179         if (not page):
180             return
181
182         current_option = None
183         starting_at = 0
184
185         # 500 is just a safe limit
186         for i in range (0, 500):
187             # Iterate until find a jpeg
188             start = page.find ("imgurl:"", starting_at)
189             if (start == -1):
190                 yield None
191             end = page.find ("&", start + len ("imgurl:""))
192             current_option = page [start + len ("imgurl:""): end].replace ("amp;", "")
193             if (current_option.lower().endswith (".jpg") or
194                 current_option.lower().endswith (".jpeg")):
195                 yield current_option
196             starting_at = end
197         
198
199     def __clean_string_for_search (self, text):
200         if (not text or len (text) < 1):
201             return None
202             
203         bad_stuff = "_:?\\-~"
204         clean = text
205         for c in bad_stuff:
206             clean = clean.replace (c, " ")
207
208         clean.replace ("/", "%2F")
209         clean = clean.replace (" CD1", "").replace(" CD2", "")
210         return urllib.quote(clean)
211
212
213 if __name__ == "__main__":
214     import sys
215     from optparse import OptionParser
216
217     parser = OptionParser()
218     parser.add_option ("-p", "--print", dest="print_paths",
219                        action="store_true", default=True,
220                        help="Print the destination paths")
221     parser.add_option ("-r", "--retrieve", dest="retrieve",
222                        action="store_true", default=False,
223                        help="Try to retrieve the online content")
224     parser.add_option ("-m", "--multiple", dest="multiple",
225                        action="store_true", default=False,
226                        help="Show more than one option")
227     parser.add_option ("-a", "--artist", dest="artist", type="string",
228                        help="ARTIST to look for", metavar="ARTIST")
229     parser.add_option ("-b", "--album", dest="album", type="string",
230                        help="ALBUM to look for", metavar="ALBUM")
231
232     (options, args) = parser.parse_args ()
233     print options
234     if (not options.artist and not options.album):
235         parser.print_help ()
236         sys.exit (-1)
237
238     if (options.multiple and options.retrieve):
239         print "Multiple and retrieve are incompatible"
240         parser.print_help ()
241         sys.exit (-1)
242         
243     if options.print_paths and not options.retrieve:
244         print "Album art:", getCoverArtFileName (options.album)
245         print "Thumbnail:", getCoverArtThumbFileName (options.album)
246
247     if options.retrieve:
248         maa = MussorgskyAlbumArt ()
249         maa.get_album_art (options.artist, options.album)
250
251     if options.multiple:
252         start = time.time ()
253         maa = MussorgskyAlbumArt ()
254         for (img, thumb) in  maa.get_alternatives (options.artist, options.album, 5):
255             print img
256             print thumb
257         end = time.time ()
258         print end - start