summaryrefslogtreecommitdiff
path: root/nephilim/plugins/AlbumCover.py
diff options
context:
space:
mode:
authorAnton Khirnov <wyskas@gmail.com>2009-08-20 13:48:29 +0200
committerAnton Khirnov <wyskas@gmail.com>2009-08-20 13:55:30 +0200
commit368cf0ee36cce1c40b9299adf056d17ebca32e89 (patch)
tree525d0d0ae9acd50aa1b7f511f74f46c915ca4c39 /nephilim/plugins/AlbumCover.py
parent16be49f2deb42293f85b90b149516c643cc4f395 (diff)
AlbumCover: rewrite to use same design as Lyrics
some features got removed in the process, should be put back soon.
Diffstat (limited to 'nephilim/plugins/AlbumCover.py')
-rw-r--r--nephilim/plugins/AlbumCover.py544
1 files changed, 287 insertions, 257 deletions
diff --git a/nephilim/plugins/AlbumCover.py b/nephilim/plugins/AlbumCover.py
index 8321944..e5345d1 100644
--- a/nephilim/plugins/AlbumCover.py
+++ b/nephilim/plugins/AlbumCover.py
@@ -1,5 +1,4 @@
#
-# Copyright (C) 2008 jerous <jerous@gmail.com>
# Copyright (C) 2009 Anton Khirnov <wyskas@gmail.com>
#
# Nephilim is free software: you can redistribute it and/or modify
@@ -16,19 +15,16 @@
# along with Nephilim. If not, see <http://www.gnu.org/licenses/>.
#
-from PyQt4 import QtGui, QtCore
+from PyQt4 import QtGui, QtCore, QtNetwork
from PyQt4.QtCore import QVariant
+
import os
from ..plugin import Plugin
-from ..misc import APPNAME, expand_tags, generate_metadata_path
+from .. import misc
-# FETCH MODES
-AC_NO_FETCH = 0
-AC_FETCH_LOCAL_DIR = 1
-AC_FETCH_AMAZON = 2
-class wgAlbumCover(QtGui.QLabel):
+class AlbumCoverWidget(QtGui.QLabel):
"cover - QPixmap or None"
cover = None
"is there a (non-default) cover loaded?"
@@ -38,8 +34,6 @@ class wgAlbumCover(QtGui.QLabel):
"logger"
logger = None
- _cover_dirname = None # Directory and full filepath where cover
- _cover_filepath = None # for current song should be stored.
_menu = None # popup menu
def __init__(self, plugin):
@@ -51,41 +45,19 @@ class wgAlbumCover(QtGui.QLabel):
# popup menu
self._menu = QtGui.QMenu("album")
refresh = self._menu.addAction('&Refresh cover.')
- refresh.setObjectName('refresh')
- select_file_action = self._menu.addAction('&Select cover file...')
- select_file_action.setObjectName('select_file_action')
- fetch_amazon_action = self._menu.addAction('Fetch from &Amazon.')
- fetch_amazon_action.setObjectName('fetch_amazon_action')
view_action = self._menu.addAction('&View in a separate window.')
save_action = self._menu.addAction('Save cover &as...')
- self.connect(refresh, QtCore.SIGNAL('triggered()'), self.refresh)
- self.connect(select_file_action, QtCore.SIGNAL('triggered()'), self._fetch_local_manual)
- self.connect(fetch_amazon_action, QtCore.SIGNAL('triggered()'), self.fetch_amazon)
- self.connect(view_action, QtCore.SIGNAL('triggered()'), self._view_cover)
- self.connect(save_action, QtCore.SIGNAL('triggered()'), self._save_cover)
-
- # MPD events
- self.connect(self.plugin.mpclient, QtCore.SIGNAL('song_changed'), self.refresh)
- self.connect(self.plugin.mpclient, QtCore.SIGNAL('disconnected'), self.refresh)
- self.connect(self.plugin.mpclient, QtCore.SIGNAL('state_changed'),self.refresh)
-
- self.connect(self, QtCore.SIGNAL('new_cover_fetched'), self.set_cover)
+ self.connect(refresh, QtCore.SIGNAL('triggered()'), self.plugin.refresh)
+ self.connect(view_action, QtCore.SIGNAL('triggered()'), self.__view_cover)
+ self.connect(save_action, QtCore.SIGNAL('triggered()'), self.__save_cover)
def contextMenuEvent(self, event):
event.accept()
self._menu.popup(event.globalPos())
- def refresh(self):
- self._fetch_cover(self._fetch_auto)
-
- def fetch_amazon(self):
- self._fetch_cover(self._fetch_amazon_manual)
-
- def set_cover(self, song, cover, write = False):
- """Set cover for current song, attempt to write it to a file
- if write is True and it's globally allowed."""
-
+ def set_cover(self, song, cover):
+ """Set cover for current song."""
self.logger.info('Setting cover')
if not cover or cover.isNull():
self.cover = None
@@ -97,135 +69,13 @@ class wgAlbumCover(QtGui.QLabel):
if song != self.plugin.mpclient.current_song():
return
- self.cover = QtGui.QPixmap.fromImage(cover)
+ self.cover = cover
self.cover_loaded = True
self.setPixmap(self.cover.scaled(self.size(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
self.plugin.emit(QtCore.SIGNAL('cover_changed'), self.cover)
self.logger.info('Cover set.')
- if (write and self.plugin.settings.value(self.plugin.name + '/store').toBool()
- and self._cover_filepath):
- if self.cover.save(self._cover_filepath, 'png'):
- self.logger.info('Cover saved.')
- else:
- self.logger.error('Error saving cover.')
-
- class FetchThread(QtCore.QThread):
- def __init__(self, parent, fetch_func, song):
- QtCore.QThread.__init__(self)
- self.setParent(parent)
- self.fetch_func = fetch_func
- self.song = song
-
- def run(self):
- cover, write = self.fetch_func(self.song)
- self.parent().emit(QtCore.SIGNAL('new_cover_fetched'), self.song, cover, write)
-
- def _fetch_cover(self, fetch_func):
- song = self.plugin.mpclient.current_song()
- if not song:
- return self.emit(QtCore.SIGNAL('new_cover_fetched'), None, None)
-
- thread = self.FetchThread(self, fetch_func, song)
- thread.start()
-
- def _fetch_auto(self, song):
- """Autofetch cover for currently playing song."""
- self.logger.info("autorefreshing cover")
-
- # generate filenames
- (self._cover_dirname, self._cover_filepath) = generate_metadata_path(song, self.plugin.settings.value(self.plugin.name + '/coverdir').toString(),
- self.plugin.settings.value(self.plugin.name + '/covername').toString())
-
- write = False
- if not QtCore.QFile.exists(self._cover_filepath):
- for i in (0, 1):
- src = self.plugin.settings.value(self.plugin.name + '/method%i'%i).toInt()[0]
- if src == AC_FETCH_LOCAL_DIR and self._cover_dirname:
- cover = self._fetch_local(song)
- elif src == AC_FETCH_AMAZON:
- cover = self._fetch_amazon(song)
- else:
- cover = QtGui.QImage()
-
- if cover and not cover.isNull():
- write = True
- break
- else:
- cover = QtGui.QImage(self._cover_filepath)
-
- return cover, write
-
- def _fetch_local_manual(self):
- song = self.plugin.mpclient.current_song()
- if not song:
- return self.emit(QtCore.SIGNAL('new_cover_fetched'), None, None)
-
- file = QtGui.QFileDialog.getOpenFileName(self,
- 'Select album cover for %s - %s'%(song.artist(), song.album()),
- self._cover_dirname, '')
- cover = QtGui.QImage(file)
- if cover.isNull():
- return None, False
- self.emit(QtCore.SIGNAL('new_cover_fetched'), song, cover, True)
-
- def _fetch_local(self, song):
- self.logger.info('Trying to guess local cover name.')
- # guess cover name
- covers = ['cover', 'album', 'front']
-
- exts = []
- for ext in QtGui.QImageReader().supportedImageFormats():
- exts.append('*.%s'%str(ext))
-
- filter = []
- for cover in covers:
- for ext in exts:
- filter.append('*.%s%s'%(cover,ext))
-
- dir = QtCore.QDir(self._cover_dirname)
- if not dir:
- self.logger.error('Error opening directory' + self._cover_dirname)
- return None
-
- dir.setNameFilters(filter)
- files = dir.entryList()
- if files:
- cover = QtGui.QImage(dir.filePath(files[0]))
- if not cover.isNull():
- self.logger.info('Found a cover.')
- return cover
-
- # if this failed, try any supported image
- dir.setNameFilters(exts)
- files = dir.entryList()
- if files:
- return QtGui.QImage(dir.filePath(files[0]))
- self.logger.info('No matching cover found')
- return None
-
- def _fetch_amazon_manual(self, song):
- cover = self._fetch_amazon(song)
- if not cover:
- return None, False
- return cover, True
-
- def _fetch_amazon(self, song):
- if not song.artist() or not song.album():
- return None
- # get the url from amazon WS
- coverURL = AmazonAlbumImage(song.artist(), song.album()).fetch()
- self.logger.info('Fetching cover from Amazon')
- if not coverURL:
- self.logger.info('Cover not found on Amazon')
- return None
-
- img = urllib.urlopen(coverURL)
- cover = QtGui.QImage()
- cover.loadFromData(img.read())
- return cover
-
- def _view_cover(self):
+ def __view_cover(self):
if not self.cover_loaded:
return
win = QtGui.QLabel(self, QtCore.Qt.Window)
@@ -233,60 +83,66 @@ class wgAlbumCover(QtGui.QLabel):
win.setPixmap(self.cover)
win.show()
- def _save_cover(self):
+ def __save_cover(self):
if not self.cover_loaded:
return
-
cover = self.cover
- file = QtGui.QFileDialog.getSaveFileName(None, '', os.path.expanduser('~'))
+ file = QtGui.QFileDialog.getSaveFileName(None, '', QtCore.QDir.homePath())
if file:
- if not cover.save(file):
- self.logger.error('Saving cover failed.')
+ self.plugin.save_cover_file(cover, file)
class AlbumCover(Plugin):
+ # public, read-only
o = None
- DEFAULTS = {'coverdir' : '$musicdir/$songdir', 'covername' : '.cover_nephilim_$artist_$album',
- 'method0' : 1, 'method1' : 1, 'store' : True}
-
- def _load(self):
- self.o = wgAlbumCover(self)
- def _unload(self):
- self.o = None
- def info(self):
- return "Display the album cover of the currently playing album."
-
- def refresh(self):
- self.o.refresh() if self.o else self.logger.warning('Attemped to refresh when not loaded.')
-
- def cover(self):
- if not self.o:
- return None
- return self.o.cover if self.o.cover_loaded else None
- def _get_dock_widget(self):
- return self._create_dock(self.o)
+ # private
+ DEFAULTS = {'coverdir' : '$musicdir/$songdir', 'covername' : '.cover_nephilim_$artist_$album',
+ 'fetchers': QtCore.QStringList(['local', 'Last.fm']), 'store' : True}
+ "implemented fetchers"
+ available_fetchers = None
+ "enabled fetchers, those with higher priority first"
+ __fetchers = None
+ "number of returned results from last refresh() call"
+ __results = None
+ "index/priority of current cover"
+ __index = None
+ "metadata paths"
+ __cover_dir = None
+ __cover_path = None
+
+ #### private ####
+ def __init__(self, parent, mpclient, name):
+ Plugin.__init__(self, parent, mpclient, name)
+
+ self.__fetchers = []
+ self.available_fetchers = [self.FetcherLocal, self.FetcherLastfm]
+
+ def __new_cover_fetched(self, song, cover):
+ self.logger.info('Got new cover.')
+ self.__results += 1
+
+ i = self.__fetchers.index(self.sender())
+ if cover and i < self.__index:
+ if self.settings.value(self.name + '/store').toBool():
+ self.save_cover_file(cover)
+ self.__index = i
+ return self.o.set_cover(song, cover)
+ elif self.__results >= len(self.__fetchers) and not self.o.cover_loaded:
+ self.o.set_cover(song, None)
class SettingsWidgetAlbumCover(Plugin.SettingsWidget):
- methods = []
- coverdir = None
- covername = None
- store = None
+ coverdir = None
+ covername = None
+ store = None
+ fetcherlist = None
def __init__(self, plugin):
Plugin.SettingsWidget.__init__(self, plugin)
self.settings.beginGroup(self.plugin.name)
- # fetching methods comboboxes
- self.methods = [QtGui.QComboBox(), QtGui.QComboBox()]
- for i,method in enumerate(self.methods):
- method.addItem('No method.')
- method.addItem('Local dir')
- method.addItem('Amazon')
- method.setCurrentIndex(self.settings.value('method' + str(i)).toInt()[0])
-
# store covers groupbox
self.store = QtGui.QGroupBox('Store covers.')
- self.store.setToolTip('Should %s store its own copy of covers?'%APPNAME)
+ self.store.setToolTip('Should %s store its own copy of covers?'%misc.APPNAME)
self.store.setCheckable(True)
self.store.setChecked(self.settings.value('store').toBool())
self.store.setLayout(QtGui.QGridLayout())
@@ -297,88 +153,262 @@ class AlbumCover(Plugin):
'$musicdir will be expanded to path to MPD music library (as set by user)\n'
'$songdir will be expanded to path to the song (relative to $musicdir\n'
'other tags same as in covername'
- %APPNAME)
+ %misc.APPNAME)
self.covername = QtGui.QLineEdit(self.settings.value('covername').toString())
self.covername.setToolTip('Filename for %s cover files.\n'
'All tags supported by MPD will be expanded to their\n'
'values for current song, e.g. $title, $track, $artist,\n'
- '$album, $genre etc.'%APPNAME)
+ '$album, $genre etc.'%misc.APPNAME)
self.store.layout().addWidget(QtGui.QLabel('Cover directory'), 0, 0)
self.store.layout().addWidget(self.coverdir, 0, 1)
self.store.layout().addWidget(QtGui.QLabel('Cover filename'), 1, 0)
self.store.layout().addWidget(self.covername, 1, 1)
+ # sites list
+ fetchers = self.settings.value('fetchers').toStringList()
+ self.fetcherlist = QtGui.QListWidget(self)
+ self.fetcherlist.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
+ for site in fetchers:
+ it = QtGui.QListWidgetItem(site)
+ it.setCheckState(QtCore.Qt.Checked)
+ self.fetcherlist.addItem(it)
+ for site in self.plugin.available_fetchers:
+ if not site.name in fetchers:
+ it = QtGui.QListWidgetItem(site.name)
+ it.setCheckState(QtCore.Qt.Unchecked)
+ self.fetcherlist.addItem(it)
+
self.setLayout(QtGui.QVBoxLayout())
- self._add_widget(self.methods[0], 'Method 0', 'Method to try first.')
- self._add_widget(self.methods[1], 'Method 1', 'Method to try if the first one fails.')
self.layout().addWidget(self.store)
+ self._add_widget(self.fetcherlist, label = 'Fetchers', tooltip = 'A list of sources used for fetching covers.\n'
+ 'Use drag and drop to change their priority.')
self.settings.endGroup()
def save_settings(self):
self.settings.beginGroup(self.plugin.name)
- self.settings.setValue('method0', QVariant(self.methods[0].currentIndex()))
- self.settings.setValue('method1', QVariant(self.methods[1].currentIndex()))
self.settings.setValue('coverdir', QVariant(self.coverdir.text()))
self.settings.setValue('covername', QVariant(self.covername.text()))
self.settings.setValue('store', QVariant(self.store.isChecked()))
+
+ fetchers = QtCore.QStringList()
+ for i in range(self.fetcherlist.count()):
+ it = self.fetcherlist.item(i)
+ if it.checkState() == QtCore.Qt.Checked:
+ fetchers.append(it.text())
+ self.settings.setValue('fetchers', QVariant(fetchers))
self.settings.endGroup()
- self.plugin.o.refresh()
+ self.plugin.refresh()
+
+ class Fetcher(QtCore.QObject):
+ """A basic class for cover fetchers. Provides a fetch(song) function,
+ emits a finished(song, cover) signal when done; cover is either a QPixmap
+ or None if not found."""
+ #public, read-only
+ logger = None
+ name = ''
+
+ #private
+ nam = None # NetworkAccessManager
+ srep = None # search results NetworkReply
+ crep = None # cover page NetworkReply
+ song = None # current song
+
+ #### private ####
+ def __init__(self, plugin):
+ QtCore.QObject.__init__(self, plugin)
+
+ self.nam = QtNetwork.QNetworkAccessManager()
+ self.logger = plugin.logger
+
+ def fetch2(self, song, url):
+ """A private convenience function to initiate fetch process."""
+ # abort any existing connections
+ if self.srep:
+ self.srep.abort()
+ self.srep = None
+ if self.crep:
+ self.crep.abort()
+ self.crep = None
+ self.song = song
+
+ self.logger.info('Searching %s: %s.'%(self. name, url))
+ self.srep = self.nam.get(QtNetwork.QNetworkRequest(url))
+
+ def finish(self, cover = None):
+ """A private convenience function to clean up and emit finished().
+ Feel free to reimplement/not use it."""
+ self.srep = None
+ self.crep = None
+ self.emit(QtCore.SIGNAL('finished'), self.song, cover)
+ self.song = None
+
+ #### public ####
+ def fetch(self, song):
+ """Reimplement this in subclasses."""
+ pass
+
+ class FetcherLastfm(Fetcher):
+ name = 'Last.fm'
+
+ def fetch(self, song):
+ url = QtCore.QUrl('http://ws.audioscrobbler.com/2.0/')
+ url.setQueryItems([('api_key', 'c325945c67b3e8327e01e3afb7cdcf35'),
+ ('method', 'album.getInfo'),
+ ('artist', song.artist()),
+ ('album', song.album())])
+ self.fetch2(song, url)
+ self.connect(self.srep, QtCore.SIGNAL('finished()'), self.__handle_search_res)
+
+ def __handle_search_res(self):
+ url = None
+ xml = QtCore.QXmlStreamReader(self.srep)
+
+ while not xml.atEnd():
+ token = xml.readNext()
+ if token == QtCore.QXmlStreamReader.StartElement:
+ if xml.name() == 'image' and xml.attributes().value('size') == 'extralarge':
+ url = QtCore.QUrl() # the url is already percent-encoded
+ url.setEncodedUrl(xml.readElementText().toLatin1())
+ if xml.hasError():
+ self.logger.error('Error parsing seach results: %s'%xml.errorString())
+
+ if not url:
+ self.logger.info('Didn\'t find the URL in %s search results.'%self.name)
+ return self.finish()
+ self.logger.info('Found %s song URL: %s.'%(self.name, url))
+
+ self.crep = self.nam.get(QtNetwork.QNetworkRequest(url))
+ self.connect(self.crep, QtCore.SIGNAL('finished()'), self.__handle_cover)
+
+ def __handle_cover(self):
+ data = self.crep.readAll()
+ pixmap = QtGui.QPixmap()
+ if pixmap.loadFromData(data):
+ self.finish(pixmap)
+ self.finish()
+
+ class FetcherLocal(QtCore.QObject):
+ """This fetcher tries to find cover files in the same directory as
+ current song."""
+ name = 'local'
+ logger = None
+ settings = None
- def get_settings_widget(self):
- return self.SettingsWidgetAlbumCover(self)
+ def __init__(self, plugin):
+ QtCore.QObject.__init__(self, plugin)
+ self.logger = plugin.logger
+ self.settings = QtCore.QSettings()
+
+ def fetch(self, song):
+ self.logger.info('Trying to guess local cover name.')
+ # guess cover name
+ covers = ['cover', 'album', 'front']
+
+ exts = []
+ for ext in QtGui.QImageReader().supportedImageFormats():
+ exts.append('*.%s'%str(ext))
+
+ filter = []
+ for cover in covers:
+ for ext in exts:
+ filter.append('*.%s%s'%(cover,ext))
+
+ dir = QtCore.QDir('%s/%s'%(self.settings.value('MPD/music_dir').toString(),
+ os.path.dirname(song.filepath())))
+ if not dir:
+ self.logger.error('Error opening directory' + self.__cover_dir)
+ return self.emit(QtCore.SIGNAL('finished'), song, None)
+
+ dir.setNameFilters(filter)
+ files = dir.entryList()
+ if files:
+ cover = QtGui.QPixmap(dir.filePath(files[0]))
+ if not cover.isNull():
+ self.logger.info('Found a cover: %s'%dir.filePath(files[0]))
+ return self.emit(QtCore.SIGNAL('finished'), song, cover)
+
+ # if this failed, try any supported image
+ dir.setNameFilters(exts)
+ files = dir.entryList()
+ if files:
+ cover = QtGui.QPixmap(dir.filePath(files[0]))
+ if not cover.isNull():
+ self.logger.info('Found a cover: %s'%dir.filePath(files[0]))
+ return self.emit(QtCore.SIGNAL('finished'), song, cover)
+ self.logger.info('No matching cover found')
+ self.emit(QtCore.SIGNAL('finished'), song, None)
+
+ #### public ####
+ def _load(self):
+ self.o = AlbumCoverWidget(self)
+ self.connect(self.mpclient, QtCore.SIGNAL('song_changed'), self.refresh)
+ self.connect(self.mpclient, QtCore.SIGNAL('disconnected'), self.refresh)
+ self.refresh_fetchers()
+ def _unload(self):
+ self.o = None
+ self.disconnect(self.mpclient, QtCore.SIGNAL('song_changed'), self.refresh)
+ self.disconnect(self.mpclient, QtCore.SIGNAL('disconnected'), self.refresh)
+ self.disconnect(self.mpclient, QtCore.SIGNAL('state_changed'),self.refresh)
+ def info(self):
+ return "Display the album cover of the currently playing album."
+
+ def refresh(self):
+ self.logger.info('Autorefreshing cover.')
+ self.__results = 0
+ self.__index = len(self.__fetchers)
+ self.o.cover_loaded = False
+ song = self.mpclient.current_song()
+ if not song:
+ self.__cover_dir = ''
+ self.__cover_path = ''
+ return self.o.set_cover(None, None)
+ (self.__cover_dir, self.__cover_path) = misc.generate_metadata_path(song,
+ self.settings.value(self.name + '/coverdir').toString(),
+ self.settings.value(self.name + '/covername').toString())
+ try:
+ self.logger.info('Trying to read cover from file %s.'%self.__cover_path)
+ cover = QtGui.QPixmap(self.__cover_path)
+ if not cover.isNull():
+ return self.o.set_cover(song, cover)
+ except IOError, e:
+ self.logger.info('Error reading cover file: %s.'%e)
+
+ for fetcher in self.__fetchers:
+ fetcher.fetch(song)
+
+ def refresh_fetchers(self):
+ """Refresh the list of available fetchers."""
+ self.__fetchers = []
+ # append fetchers in order they are stored in settings
+ for name in self.settings.value('%s/fetchers'%self.name).toStringList():
+ for site in self.available_fetchers:
+ if site.name == name:
+ self.__fetchers.append(site(self))
+ self.connect(self.__fetchers[-1], QtCore.SIGNAL('finished'), self.__new_cover_fetched)
+
+ def save_cover_file(self, cover, path = None):
+ """Save cover to a file specified in path.
+ If path is None, then a default value is used."""
+ self.logger.info('Saving cover...')
+ try:
+ if not path:
+ path = self.__cover_path
+ cover.save(path, 'png')
+ self.logger.info('Cover successfully saved.')
+ except IOError, e:
+ self.logger.error('Error writing cover: %s', e)
+
+ def cover(self):
+ if not self.o:
+ return None
+ return self.o.cover if self.o.cover_loaded else None
-# This is the amazon cover fetcher using their webservice api
-# Thank you, http://www.semicomplete.com/scripts/albumcover.py
-import re
-import urllib
+ def _get_dock_widget(self):
+ return self._create_dock(self.o)
-AMAZON_AWS_ID = "0K4RZZKHSB5N2XYJWF02"
+ def get_settings_widget(self):
+ return self.SettingsWidgetAlbumCover(self)
-class AmazonAlbumImage(object):
- awsurl = 'http://ecs.amazonaws.com/onca/xml'
- def __init__(self, artist, album):
- self.artist = artist
- self.album = album
- def fetch(self):
- url = self._GetResultURL(self._SearchAmazon())
- if not url:
- return None
- img_re = re.compile(r'''registerImage\("original_image", "([^"]+)"''')
- try:
- prod_data = urllib.urlopen(url).read()
- except:
- self.logger.warning('timeout opening %s'%(url))
- return None
- m = img_re.search(prod_data)
- if not m:
- return None
- img_url = m.group(1)
- return img_url
-
- def _SearchAmazon(self):
- data = {
- 'Service' : 'AWSECommerceService',
- 'Version' : '2005-03-23',
- 'Operation' : 'ItemSearch',
- 'ContentType' : 'text/xml',
- 'SubscriptionId': AMAZON_AWS_ID,
- 'SearchIndex' : 'Music',
- 'ResponseGroup' : 'Small',
- }
-
- data['Artist'] = self.artist.encode('utf-8')
- data['Keywords'] = self.album.encode('utf-8')
-
- fd = urllib.urlopen('%s?%s' % (self.awsurl, urllib.urlencode(data)))
- return fd.read()
-
-
- def _GetResultURL(self, xmldata):
- if not xmldata:
- return None
- url_re = re.compile(r'<DetailPageURL>([^<]+)</DetailPageURL>')
- m = url_re.search(xmldata)
- return m and m.group(1)