From b36650aa439157a6e8876d23765b7f7687b675ff Mon Sep 17 00:00:00 2001 From: wicher Date: Wed, 18 Apr 2012 14:37:26 +0200 Subject: [PATCH] Optionally fetch album / artist art using Last.fm's API. --- theory/controllers/main.py | 1 + theory/model/albumart.py | 77 ++++++++++++++++++++++++++++++++---- theory/model/form.py | 1 + theory/model/tconfig.py | 3 ++ theory/templates/config.html | 12 +++++- 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/theory/controllers/main.py b/theory/controllers/main.py index f547c7e..c3b9ca9 100644 --- a/theory/controllers/main.py +++ b/theory/controllers/main.py @@ -161,6 +161,7 @@ def config(self, use_htmlfill=True): 'password':g.tc.password,'webpassword':g.tc.webpassword, 'awskey':g.tc.awskey,'timeout':g.tc.timeout, 'aws_secret':g.tc.aws_secret, + 'lastfmkey':g.tc.lastfmkey, 'default_search':g.tc.default_search, 'outputs': configured_outputs}) diff --git a/theory/model/albumart.py b/theory/model/albumart.py index b05d040..0c979ed 100644 --- a/theory/model/albumart.py +++ b/theory/model/albumart.py @@ -33,7 +33,7 @@ class NoArtOnDisk(Exception): pass class AlbumArt: - amazonurl = None + weburl = None imgurl = None logger = None @@ -46,7 +46,7 @@ def album_fetch(self,artist,album): """ attempt to load an album's cover art from disk. if it doesn't exist, make a request using Amazon's - Web Services + Web Services and/or the Last.fm API. """ self.artist = artist @@ -63,7 +63,10 @@ def album_fetch(self,artist,album): try: self.check_disk() except NoArtOnDisk: - self.amazon_fetch() + try: + self.amazon_fetch() + except NoArtError: + self.lastfm_fetch() def artist_art(self,artist): """ return all of the album covers for a particular artist """ @@ -154,11 +157,71 @@ def amazon_fetch(self): imgnodes = doc.getElementsByTagName('LargeImage') if len(imgnodes) > 0: node = imgnodes[0] - self.amazonurl = node.firstChild.firstChild.nodeValue - self.log('Found album art: %s' % self.amazonurl) + self.weburl = node.firstChild.firstChild.nodeValue + self.log('Found album art: %s' % self.weburl) break - if not self.amazonurl: + if not self.weburl: + raise NoArtError + + self.save_to_disk() + + def lastfm_fetch(self): + """ + attempts to fetch album cover art from last.fm and + calls save_to_disk() to save the largest image permanently + to avoid subsequent lookups. + no album? get a picture of the artist. + get a key for the last.fm API here: http://www.last.fm/api/account + example URL: http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=b25b959554ed76058ac220b7b2e0a026&artist=Cher&album=Believe + """ + + album = self.album if self.album != 'undefined' else '' + artist = self.artist if self.artist != 'undefined' else '' + + if g.tc.lastfmkey == '' or not artist: + raise NoArtError + + type = 'album' if (album and artist) else 'artist' + + artist_safe = urllib2.quote(artist) + album_safe = urllib2.quote(album) + + query_string = {'api_key': g.tc.lastfmkey, 'artist': artist_safe} + + if type == 'album': + query_string.update({'method': 'album.getinfo','album': album_safe}) + + elif type == 'artist': + query_string.update({'method': 'artist.getinfo'}) + + query_string_sorted = '&'.join(['='.join(kv) for kv in sorted(query_string.items())]) + + url = {'verb': 'GET', + 'protocol': 'http://', + 'host': 'ws.audioscrobbler.com', + 'request_uri': '/2.0', + 'query_string': query_string_sorted.replace(':','%3A')} + + real_url = url['protocol'] + url['host'] + url['request_uri'] + '?' + url['query_string'] + + try: + self.log('Fetching last.fm %s image: %s' % (type, real_url)) + urlfile = urllib2.urlopen(real_url) + except urllib2.URLError: + # there are probably other exceptions that need to be caught here.. + self.log('Error fetching last.fm XML') + raise NoArtError + + doc = xml.dom.minidom.parse(urlfile) + urlfile.close() + images = doc.getElementsByTagName('image') + node = filter(lambda x: x.getAttribute('size') == 'extralarge' and x.parentNode.parentNode.nodeName == 'lfm', images)[0] + if node.hasChildNodes(): + self.weburl = node.firstChild.nodeValue + self.log('Found %s art: %s' % (type, self.weburl)) + + if not self.weburl: raise NoArtError self.save_to_disk() @@ -183,7 +246,7 @@ def check_disk(self): def save_to_disk(self): """ save the fetched cover image to disk permanently """ try: - urlfile = urllib2.urlopen(self.amazonurl) + urlfile = urllib2.urlopen(self.weburl) except urllib2.URLError: raise NoArtError diff --git a/theory/model/form.py b/theory/model/form.py index 91eebd9..c5f0309 100644 --- a/theory/model/form.py +++ b/theory/model/form.py @@ -25,6 +25,7 @@ class ConfigForm(formencode.Schema): default_search = formencode.validators.String(not_empty=True) awskey = formencode.validators.String(strip=True,not_empty=False,if_missing=None) aws_secret = formencode.validators.String(strip=True,not_empty=False,if_missing=None) + lastfmkey = formencode.validators.String(strip=True,not_empty=False,if_missing=None) outputs = formencode.ForEach(OutputSchema(), if_missing=[]) class StreamNameInUse(formencode.validators.FancyValidator): diff --git a/theory/model/tconfig.py b/theory/model/tconfig.py index 58618a4..7d190f3 100644 --- a/theory/model/tconfig.py +++ b/theory/model/tconfig.py @@ -36,6 +36,7 @@ def __init__(self): self.webpassword = '' self.timeout = False self.awskey = None + self.lastfmkey = None self.aws_secret = None self.streams = [] self.default_search = 'Any' @@ -49,6 +50,7 @@ def __init__(self): self.password = conf.get('mpd','password') self.awskey = conf.get('services','awskey') self.aws_secret = conf.get('services','aws_secret') + self.lastfmkey = conf.get('services','lastfmkey') self.webpassword = conf.get('main','webpassword') self.timeout = conf.getboolean('main','timeout') self.default_search = conf.get('main','default_search') @@ -75,6 +77,7 @@ def commit_config(self): conf.add_section("services") conf.set('services','awskey',self.awskey) conf.set('services','aws_secret',self.aws_secret) + conf.set('services','lastfmkey',self.lastfmkey) conf.add_section('main') conf.set('main','webpassword',self.webpassword) conf.set('main','timeout',self.timeout) diff --git a/theory/templates/config.html b/theory/templates/config.html index 7a0b4b3..d6ff239 100644 --- a/theory/templates/config.html +++ b/theory/templates/config.html @@ -95,6 +95,14 @@ ${h.html.tags.text('aws_secret', None, size=45)}
+ + + Last.fm API Key: + + ${h.html.tags.text('lastfmkey', None, size=32)}
+ (for automatic downloading of album / artist art) + + Default search type: @@ -122,4 +130,6 @@ -
sign up for an Amazon Developer Key at https://aws-portal.amazon.com/gp/aws/developer/registration/index.html
+
sign up for an Amazon Developer Key at https://aws-portal.amazon.com/gp/aws/developer/registration/index.html
+sign up for a Last.fm Developer Key at http://www.last.fm/api/account +