def __init__(self, referer=None): self.referer = referer if not self.referer or self.referer.startswith("http://localhost"): self.referer = "http://github.com/sphildreth/roadie" self.logger = Logger() self.allMusicSearcher = AllMusicGuide(self.referer) self.spotifySearcher = Spotify(self.referer) self.mbSearcher = MusicBrainz(self.referer) self.lastFMSearcher = LastFM(self.referer) self.imageSearcher = ImageSearcher() self.iTunesSearcher = iTunes(self.referer) self.imageSearcher = ImageSearcher(self.referer)
class ArtistSearcher(object): """ Query Enabled Search Engines and Find Artist Information and aggregate results. """ allMusicSearcher = None spotifySearcher = None mbSearcher = None lastFMSearcher = None imageSearcher = None iTunesSearcher = None imageSearcher = None artistThumbnailSize = 160, 160 releaseThumbnailSize = 80, 80 imageMaximumSize = 500, 500 cache = dict() imageCache = dict() def __init__(self, referer=None): self.referer = referer if not self.referer or self.referer.startswith("http://localhost"): self.referer = "http://github.com/sphildreth/roadie" self.logger = Logger() self.allMusicSearcher = AllMusicGuide(self.referer) self.spotifySearcher = Spotify(self.referer) self.mbSearcher = MusicBrainz(self.referer) self.lastFMSearcher = LastFM(self.referer) self.imageSearcher = ImageSearcher() self.iTunesSearcher = iTunes(self.referer) self.imageSearcher = ImageSearcher(self.referer) def searchForArtist(self, name): """ Perform a search in all enabled search engines and return an aggregate Artist for the given Artist name :param name: String Name of the Artist to find :return: Artist Populated Artist or None if error or not found """ if not name: return None if name in self.cache: return self.cache[name] try: startTime = arrow.utcnow().datetime artist = Artist(name=name) artist.roadieId = str(uuid.uuid4()) if self.iTunesSearcher.IsActive: artist = artist.mergeWithArtist(self.iTunesSearcher.lookupArtist(name)) if self.mbSearcher.IsActive: artist = artist.mergeWithArtist(self.mbSearcher.lookupArtist(name)) if self.lastFMSearcher.IsActive: artist = artist.mergeWithArtist(self.lastFMSearcher.lookupArtist(name)) if self.spotifySearcher.IsActive: artist = artist.mergeWithArtist(self.spotifySearcher.lookupArtist(name)) if self.allMusicSearcher.IsActive: artist = artist.mergeWithArtist(self.allMusicSearcher.lookupArtist(name)) if artist: # Fetch images with only urls, remove any with neither URL or BLOB if artist.images: images = [] firstImageInImages = None for image in artist.images: if not image.image and image.url: image.image = self.imageSearcher.getImageBytesForUrl(image.url) if image.image: # Resize to maximum image size and convert to JPEG img = Image.open(io.BytesIO(image.image)).convert('RGB') img.resize(self.imageMaximumSize) b = io.BytesIO() img.save(b, "JPEG") image.image = b.getvalue() firstImageInImages = firstImageInImages or image.image image.signature = image.averageHash() images.append(image) if images: dedupedImages = [] imageSignatures = [] for image in images: if image.signature not in imageSignatures: imageSignatures.append(image.signature) dedupedImages.append(image) artist.images = dedupedImages if not artist.thumbnail and firstImageInImages: try: img = Image.open(io.BytesIO(firstImageInImages)).convert('RGB') img.thumbnail(self.artistThumbnailSize) b = io.BytesIO() img.save(b, "JPEG") artist.thumbnail = b.getvalue() except: pass # Add special search names to alternate names if not artist.alternateNames: artist.alternateNames = [] if artist.name not in artist.alternateNames: cleanedArtistName = createCleanedName(artist.name) if cleanedArtistName != artist.name.lower().strip() and \ cleanedArtistName not in artist.alternateNames: artist.alternateNames.append(cleanedArtistName) if not artist.bioContext: try: artist.bioContext = wikipedia.summary(artist.name) except: pass self.cache[name] = artist elapsedTime = arrow.utcnow().datetime - startTime printableName = name.encode('ascii', 'ignore').decode('utf-8') self.logger.debug("searchForArtist Elapsed Time [" + str(elapsedTime) + "] Name [" + printableName + "] Found [" + (artist.name if artist else "") + "] MusicBrainzId [" + str(artist.musicBrainzId) + "] " + " iTunesId [" + str(artist.iTunesId) + "] " + " amgId [" + str(artist.amgId) + "]" + " spotifyId [" + str(artist.spotifyId) + "]" .encode('ascii', 'ignore').decode('utf-8') + "]") return artist except: self.logger.exception("Error In searchForArtist") return None def _mergeReleaseLists(self, left, right): if left and not right: return left elif not left and right: return right elif not left and not right: return [] else: mergeReleaseListsStart = arrow.utcnow() mergedReleases = left # Merge the right to the result for rRelease in right: foundRightInMerged = False for mRelease in mergedReleases: if mRelease == rRelease: mRelease.mergeWithRelease(rRelease) foundRightInMerged = True break if not foundRightInMerged: mergedReleases.append(rRelease) mergedReleaseElapsed = arrow.utcnow() - mergeReleaseListsStart self.logger.debug("= MergeReleaseLists left size [" + str(len(left)) + "], right size [" + str( len(right)) + "] Elapsed Time [" + str(mergedReleaseElapsed) + "]") return mergedReleases def searchForArtistReleases(self, artist, artistReleaseImages, titleFilter=None): """ Using the given populated Artist find all releases, with an optional filter :param artist: Artist Artist to find releases for :param artistReleaseImages: list Collection if image signatures for Artist for deduping :param titleFilter: String Optional filter of release Title to only include in results :return: iterable Release Collection of releases found for artist """ if not artist: return None try: startTime = arrow.utcnow().datetime releases = [] if self.iTunesSearcher.IsActive: releases = self._mergeReleaseLists(releases, self.iTunesSearcher.searchForRelease(artist, titleFilter)) if self.mbSearcher.IsActive: releases = self._mergeReleaseLists(releases, self.mbSearcher.searchForRelease(artist, titleFilter)) if self.lastFMSearcher.IsActive and releases: mbIdList = [] if not titleFilter: mbIdList = [x.musicBrainzId for x in releases if x.musicBrainzId] else: for x in releases: if isEqual(x.title, titleFilter): mbIdList.append(x.musicBrainzId) break if mbIdList: releases = self._mergeReleaseLists(releases, self.lastFMSearcher.lookupReleasesForMusicBrainzIdList(artist, mbIdList)) if self.spotifySearcher.IsActive: releases = self._mergeReleaseLists(releases, self.spotifySearcher.searchForRelease(artist, titleFilter)) if releases: self.logger.debug( "searchForArtistReleases Found [" + str(len(releases)) + "] For title [" + str(titleFilter) + "]") for searchForArtistRelease in releases: if searchForArtistRelease.coverUrl: coverImage = ArtistImage(searchForArtistRelease.coverUrl) searchForArtistRelease.images.append(coverImage) # Fetch images with only urls, remove any with neither URL or BLOB if searchForArtistRelease.images: images = [] for image in searchForArtistRelease.images: if not image.image and image.url: image.image = self.getImageForUrl(image.url) if image.image: # Resize to maximum image size and convert to JPEG img = Image.open(io.BytesIO(image.image)).convert('RGB') img.resize(self.imageMaximumSize) b = io.BytesIO() img.save(b, "JPEG") image.image = b.getvalue() # Hash image for deduping image.signature = image.averageHash() if image.signature: images.append(image) if not images: searchForArtistRelease.images = [] else: dedupedImages = [] imageSignatures = artistReleaseImages or [] for image in images: if image.signature not in imageSignatures: imageSignatures.append(image.signature) dedupedImages.append(image) searchForArtistRelease.images = dedupedImages if not searchForArtistRelease.thumbnail: try: firstImageInImages = None for image in searchForArtistRelease.images: firstImageInImages = firstImageInImages or image.image if firstImageInImages: break img = Image.open(io.BytesIO(firstImageInImages)).convert('RGB') img.thumbnail(self.releaseThumbnailSize) b = io.BytesIO() img.save(b, "JPEG") searchForArtistRelease.thumbnail = b.getvalue() except: pass if titleFilter and releases: filteredReleases = [] cleanedTitleFilter = createCleanedName(titleFilter) for searchForArtistRelease in releases: if isEqual(searchForArtistRelease.title, titleFilter) or cleanedTitleFilter in searchForArtistRelease.alternateNames: filteredReleases.append(searchForArtistRelease) releases = filteredReleases elapsedTime = arrow.utcnow().datetime - startTime self.logger.debug("searchForArtistReleases ElapseTime [" + str(elapsedTime) + "]") return releases except: self.logger.exception("Error In searchForArtistReleases") pass return None def getImageForUrl(self, url): if url not in self.imageCache: self.imageCache[url] = self.imageSearcher.getImageBytesForUrl(url) self.logger.debug("= Downloading Image [" + str(url) + "]") return self.imageCache[url]