[rhythmbox] artdisplay: add last.fm album art search
- From: Jonathan Matthew <jmatthew src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rhythmbox] artdisplay: add last.fm album art search
- Date: Sun, 21 Mar 2010 05:49:15 +0000 (UTC)
commit ca807b61c8efc249f7b9ebf103208e85904bb088
Author: Jonathan Matthew <jonathan d14n org>
Date: Sun Mar 21 15:45:54 2010 +1000
artdisplay: add last.fm album art search
This requires the user to have a last.fm account, and displays the
last.fm logo in the tooltip for album art images retrieved from last.fm.
I've at least made an honest effort to comply with the last.fm terms of
service.
plugins/artdisplay/Makefile.am | 2 +-
plugins/artdisplay/artdisplay/CoverArtDatabase.py | 3 +-
.../artdisplay/artdisplay/LastFMCoverArtSearch.py | 163 ++++++++++++++++++++
plugins/artdisplay/artdisplay/Makefile.am | 1 +
plugins/artdisplay/lastfm_red_small.png | Bin 0 -> 1929 bytes
5 files changed, 167 insertions(+), 2 deletions(-)
---
diff --git a/plugins/artdisplay/Makefile.am b/plugins/artdisplay/Makefile.am
index c7f0257..8ffb46c 100644
--- a/plugins/artdisplay/Makefile.am
+++ b/plugins/artdisplay/Makefile.am
@@ -10,7 +10,7 @@ plugin_in_files = artdisplay.rb-plugin.in
plugin_DATA = $(plugin_in_files:.rb-plugin.in=.rb-plugin)
artworkdir = $(plugindir)
-artwork_DATA = rhythmbox-missing-artwork.svg
+artwork_DATA = rhythmbox-missing-artwork.svg lastfm_red_small.png
EXTRA_DIST = $(plugin_in_files) $(artwork_DATA)
diff --git a/plugins/artdisplay/artdisplay/CoverArtDatabase.py b/plugins/artdisplay/artdisplay/CoverArtDatabase.py
index a56f8ae..14e6325 100644
--- a/plugins/artdisplay/artdisplay/CoverArtDatabase.py
+++ b/plugins/artdisplay/artdisplay/CoverArtDatabase.py
@@ -34,13 +34,14 @@ import gobject
from PodcastCoverArtSearch import PodcastCoverArtSearch
from MusicBrainzCoverArtSearch import MusicBrainzCoverArtSearch
+from LastFMCoverArtSearch import LastFMCoverArtSearch
from EmbeddedCoverArtSearch import EmbeddedCoverArtSearch
from LocalCoverArtSearch import LocalCoverArtSearch
from urllib import unquote
ART_SEARCHES_LOCAL = [LocalCoverArtSearch, EmbeddedCoverArtSearch]
-ART_SEARCHES_REMOTE = [PodcastCoverArtSearch, MusicBrainzCoverArtSearch]
+ART_SEARCHES_REMOTE = [PodcastCoverArtSearch, LastFMCoverArtSearch, MusicBrainzCoverArtSearch]
OLD_ART_FOLDER = '~/.gnome2/rhythmbox/covers'
ART_FOLDER = os.path.join(rb.user_cache_dir(), 'covers')
diff --git a/plugins/artdisplay/artdisplay/LastFMCoverArtSearch.py b/plugins/artdisplay/artdisplay/LastFMCoverArtSearch.py
new file mode 100644
index 0000000..9a2d65c
--- /dev/null
+++ b/plugins/artdisplay/artdisplay/LastFMCoverArtSearch.py
@@ -0,0 +1,163 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+#
+# Copyright (C) 2009 Jonathan Matthew <jonathan d14n org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# The Rhythmbox authors hereby grant permission for non-GPL compatible
+# GStreamer plugins to be used and distributed together with GStreamer
+# and Rhythmbox. This permission is above and beyond the permissions granted
+# by the GPL license by which Rhythmbox is covered. If you modify this code
+# you may extend this exception to your version of the code, but you are not
+# obligated to do so. If you do not wish to do so, delete this exception
+# statement from your version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import urllib
+import xml.dom.minidom as dom
+import re
+import gconf
+
+import rb
+import rhythmdb
+
+# this API key belongs to jonathan d14n org
+# and was generated specifically for this use
+API_KEY = 'ff56d530598d65c1a4088e57da7be2f9'
+API_URL = 'http://ws.audioscrobbler.com/2.0/'
+
+LASTFM_LOGO = "lastfm_red_small.png"
+LASTFM_TOOLTIP = (LASTFM_LOGO, _("Image provided by Last.fm"))
+
+DISC_NUMBER_REGEXS = (
+ "\(disc *[0-9]+\)",
+ "\(cd *[0-9]+\)",
+ "\[disc *[0-9]+\]",
+ "\[cd *[0-9]+\]",
+ " - disc *[0-9]+$",
+ " - cd *[0-9]+$",
+ " disc *[0-9]+$",
+ " cd *[0-9]+$"
+)
+
+USERNAME_GCONF_KEY = "/apps/rhythmbox/audioscrobbler/username"
+
+def user_has_account():
+ username = gconf.client_get_default().get_string(USERNAME_GCONF_KEY)
+ return (username is not None and username != "")
+
+class LastFMCoverArtSearch (object):
+ def __init__(self):
+ pass
+
+ def __search_request (self, artist, album, album_mbid):
+ # Remove variants of Disc/CD [1-9] from album title before search
+ orig_album = album
+ for exp in DISC_NUMBER_REGEXS:
+ p = re.compile (exp, re.IGNORECASE)
+ album = p.sub ('', album)
+
+ album.strip()
+
+ print "searching for (%s, %s, %s)" % (artist, album, album_mbid)
+ url = API_URL + "?method=album.getinfo&"
+ if artist != "":
+ url = url + "artist=%s&" % (urllib.quote_plus(artist))
+ if album != "":
+ url = url + "album=%s&" % (urllib.quote_plus(album))
+ if album_mbid != "":
+ url = url + "mbid=%s&" % (urllib.quote_plus(album_mbid))
+
+ url = url + "api_key=%s" % API_KEY
+ print "last.fm query url = %s" % url
+
+ loader = rb.Loader()
+ loader.get_url(url, self.__album_info_cb)
+
+ def __album_info_cb (self, data):
+ if data is None:
+ print "last.fm query returned nothing"
+ self.callback (self, self.entry, [], *self.callback_args)
+ return
+
+ parsed = dom.parseString(data)
+
+ # find image URLs
+ image_urls = []
+ for tag in parsed.getElementsByTagName('image'):
+ if tag.firstChild is None:
+ print "got useless image tag"
+ continue
+
+ url = tag.firstChild.data
+ url.strip()
+ if url != "":
+ print "found image url: %s" % url
+ image_urls.append(url)
+
+ # images tags appear in order of increasing size, and we want the largest. probably.
+ image_urls.reverse()
+ self.callback (self, self.entry, image_urls, *self.callback_args)
+
+
+
+ def search (self, db, entry, is_playing, callback, *args):
+ self.entry = entry
+ self.callback = callback
+ self.callback_args = args
+
+ if user_has_account() == False:
+ print "can't search: no last.fm account details"
+ callback (self, entry, None, *args)
+ return
+
+ artist = db.entry_get (entry, rhythmdb.PROP_ARTIST)
+ if artist == _("Unknown"):
+ artist = ""
+
+ album = db.entry_get (entry, rhythmdb.PROP_ALBUM)
+ if album == _("Unknown"):
+ album = ""
+
+ album_mbid = db.entry_get (entry, rhythmdb.PROP_MUSICBRAINZ_ALBUMID)
+ if (artist, album, album_mbid) == ("", "", ""):
+ print "can't search: no artist, album, or album ID"
+ callback (self, entry, None, *args)
+ return
+
+ self.searches = [
+ (artist, album, album_mbid),
+ ("Various Artists", album, album_mbid)
+ ]
+ self.searches.reverse()
+ self.search_next()
+
+ def search_next (self):
+ if len(self.searches) == 0:
+ return False
+
+ args = self.searches.pop()
+ self.__search_request(*args)
+ return True
+
+ def get_result_meta (self, search_results):
+ return LASTFM_TOOLTIP
+
+ def get_result_pixbuf (self, search_results):
+ return None
+
+ def get_best_match_urls (self, search_results):
+ return search_results
+
+
diff --git a/plugins/artdisplay/artdisplay/Makefile.am b/plugins/artdisplay/artdisplay/Makefile.am
index 821cc5e..9725665 100644
--- a/plugins/artdisplay/artdisplay/Makefile.am
+++ b/plugins/artdisplay/artdisplay/Makefile.am
@@ -4,6 +4,7 @@ plugindir = $(PLUGINDIR)/artdisplay
plugin_PYTHON = \
EmbeddedCoverArtSearch.py \
PodcastCoverArtSearch.py \
+ LastFMCoverArtSearch.py \
LocalCoverArtSearch.py \
CoverArtDatabase.py \
MusicBrainzCoverArtSearch.py \
diff --git a/plugins/artdisplay/lastfm_red_small.png b/plugins/artdisplay/lastfm_red_small.png
new file mode 100644
index 0000000..6e9a88d
Binary files /dev/null and b/plugins/artdisplay/lastfm_red_small.png differ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]