[rhythmbox] artsearch: new art search plugin using extdb



commit 8ca7e20967581beceeed6e10e9a9f93c793f924b
Author: Jonathan Matthew <jonathan d14n org>
Date:   Fri Dec 16 23:07:14 2011 +1000

    artsearch: new art search plugin using extdb
    
    This does the same searches as the artdisplay plugin, triggered
    by the 'request' signal from extdb, and storing results in extdb
    as they're found.

 configure.ac                          |    1 +
 plugins/Makefile.am                   |    1 +
 plugins/artsearch/Makefile.am         |   20 ++++
 plugins/artsearch/artsearch.plugin.in |   10 ++
 plugins/artsearch/artsearch.py        |  104 +++++++++++++++++++
 plugins/artsearch/lastfm.py           |  184 +++++++++++++++++++++++++++++++++
 plugins/artsearch/local.py            |  147 ++++++++++++++++++++++++++
 plugins/artsearch/musicbrainz.py      |  104 +++++++++++++++++++
 plugins/artsearch/oldcache.py         |   69 ++++++++++++
 po/POTFILES.in                        |    3 +
 10 files changed, 643 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 3b8f98c..2919646 100644
--- a/configure.ac
+++ b/configure.ac
@@ -863,6 +863,7 @@ plugins/sample-python/Makefile
 plugins/sample-vala/Makefile
 plugins/pythonconsole/Makefile
 plugins/artdisplay/Makefile
+plugins/artsearch/Makefile
 plugins/magnatune/Makefile
 plugins/generic-player/Makefile
 plugins/rb/Makefile
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 8d35079..2a3ed5c 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -14,6 +14,7 @@ SUBDIRS =						\
 if ENABLE_PYTHON
 SUBDIRS +=						\
 	artdisplay					\
+	artsearch					\
 	context						\
 	im-status					\
 	lyrics						\
diff --git a/plugins/artsearch/Makefile.am b/plugins/artsearch/Makefile.am
new file mode 100644
index 0000000..f53410f
--- /dev/null
+++ b/plugins/artsearch/Makefile.am
@@ -0,0 +1,20 @@
+# art search plugin
+
+plugindir = $(PLUGINDIR)/artsearch
+plugindatadir = $(PLUGINDATADIR)/artsearch
+plugin_PYTHON = 			\
+	artsearch.py			\
+	lastfm.py			\
+	local.py			\
+	musicbrainz.py			\
+	oldcache.py
+
+plugin_in_files = artsearch.plugin.in
+%.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+plugin_DATA = $(plugin_in_files:.plugin.in=.plugin)
+
+EXTRA_DIST = $(plugin_in_files) $(artwork_DATA)
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
diff --git a/plugins/artsearch/artsearch.plugin.in b/plugins/artsearch/artsearch.plugin.in
new file mode 100644
index 0000000..1e695cd
--- /dev/null
+++ b/plugins/artsearch/artsearch.plugin.in
@@ -0,0 +1,10 @@
+[Plugin]
+Loader=python
+Module=artsearch
+IAge=2
+Depends=rb
+_Name=Cover art search
+_Description=Fetch album covers from the Internet
+Authors=Jonathan Matthew <jonathan d14n org>
+Copyright=Copyright  2011 Jonathan Matthew
+Website=http://www.rhythmbox.org/
diff --git a/plugins/artsearch/artsearch.py b/plugins/artsearch/artsearch.py
new file mode 100644
index 0000000..bb14573
--- /dev/null
+++ b/plugins/artsearch/artsearch.py
@@ -0,0 +1,104 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+#
+# Copyright (C) 2011 Jonathan Matthew
+#
+# 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.
+
+from gi.repository import GObject, Peas, RB, GdkPixbuf
+
+import gettext
+gettext.install('rhythmbox', RB.locale_dir())
+
+import oldcache
+from lastfm import LastFMSearch
+from local import LocalSearch
+from musicbrainz import MusicBrainzSearch
+
+class Search(object):
+	def __init__(self, store, key, last_time, searches):
+		self.store = store
+		self.key = key.copy()
+		self.last_time = last_time
+		self.searches = searches
+
+
+	def next_search(self):
+		if len(self.searches) == 0:
+			self.store.store(self.key, RB.ExtDBSourceType.NONE, None)
+			return False
+
+		search = self.searches.pop(0)
+		search.search(self.key, self.last_time, self.search_results, None)
+		return True
+
+
+	def search_results(self, storekey, data, source_type, args):
+
+		if data is None or storekey is None:
+			pass
+		elif isinstance(data, str) or isinstance(data, unicode):
+			print "got a uri: %s" % str(data)
+			self.store.store_uri(storekey, source_type, data)
+		elif isinstance(data, list) or isinstance(data, tuple):
+			print "got a uri list: %s" % str(data)
+			for u in data:
+				self.store.store_uri(storekey, source_type, u)
+		elif isinstance(data, GdkPixbuf.Pixbuf):
+			print "got a pixbuf"
+			self.store.store(storekey, source_type, data)
+		else:
+			# complain quietly
+			print "got mystery meat: %s" % data
+			pass
+
+		# keep going anyway
+		self.next_search()
+
+
+class ArtSearchPlugin (GObject.GObject, Peas.Activatable):
+	__gtype_name__ = 'ArtSearchPlugin'
+	object = GObject.property(type=GObject.GObject)
+
+	def __init__ (self):
+		GObject.GObject.__init__ (self)
+
+	def do_activate (self):
+		self.art_store = RB.ExtDB(name="album-art")
+		self.req_id = self.art_store.connect("request", self.album_art_requested)
+
+	def do_deactivate (self):
+		self.art_store.disconnect(self.req_id)
+		self.req_id = 0
+		self.art_store = None
+		self.object = None
+
+	def album_art_requested(self, store, key, last_time):
+		searches = []
+		if oldcache.USEFUL:
+			searches.append(oldcache.OldCacheSearch())
+		searches.append(LocalSearch())
+		searches.append(MusicBrainzSearch())
+		searches.append(LastFMSearch())
+
+		s = Search(store, key, last_time, searches)
+		return s.next_search()
diff --git a/plugins/artsearch/lastfm.py b/plugins/artsearch/lastfm.py
new file mode 100644
index 0000000..33db7bb
--- /dev/null
+++ b/plugins/artsearch/lastfm.py
@@ -0,0 +1,184 @@
+# -*- 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 ConfigParser
+import os
+import time
+
+import rb
+from gi.repository import RB
+
+import gettext
+gettext.install('rhythmbox', RB.locale_dir())
+
+# allow repeat searches once a week
+REPEAT_SEARCH_PERIOD = 86400 * 7
+
+# 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]+$"
+)
+
+def user_has_account():
+	session_file = os.path.join(RB.user_data_dir(), "audioscrobbler", "sessions")
+
+	if os.path.exists(session_file) == False:
+		return False
+
+	sessions = ConfigParser.RawConfigParser()
+	sessions.read(session_file)
+	try:
+		return (sessions.get('Last.fm', 'username') != "")
+	except:
+		return False
+
+class LastFMSearch (object):
+	def __init__(self):
+		pass
+
+	def search_url (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 != None:
+			url = url + "artist=%s&" % (urllib.quote_plus(artist))
+		if album != None:
+			url = url + "album=%s&" % (urllib.quote_plus(album))
+		if album_mbid != None:
+			url = url + "mbid=%s&" % (urllib.quote_plus(album_mbid))
+
+		url = url + "api_key=%s" % API_KEY
+		print "last.fm query url = %s" % url
+		return url
+
+
+	def album_info_cb (self, data):
+		if data is None:
+			print "last.fm query returned nothing"
+			self.search_next()
+			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)
+
+		if len(image_urls) > 0:
+			# images tags appear in order of increasing size, and we want the largest.  probably.
+			url = image_urls.pop()
+			self.callback(self.current_key, url, RB.ExtDBSourceType.SEARCH, self.callback_args)
+		else:
+			self.search_next()
+
+
+	def search_next (self):
+		if len(self.searches) == 0:
+			self.callback(None, None, RB.ExtDBSourceType.NONE, self.callback_args)
+			return
+
+		(artist, album, album_mbid, artist_field) = self.searches.pop(0)
+		self.current_key = RB.ExtDBKey.create("album", album)
+		if artist is not None:
+			self.current_key.add_field(artist_field, RB.ExtDBFieldType.OPTIONAL, artist)
+
+		url = self.search_url(artist, album, album_mbid)
+
+		l = rb.Loader()
+		l.get_url(url, self.album_info_cb)
+
+
+	def search(self, key, last_time, callback, args):
+		if last_time > (time.time() - REPEAT_SEARCH_PERIOD):
+			print "we already tried this one"
+			callback (None, None, RB.ExtDBSourceType.NONE, args)
+			return
+
+		if user_has_account() == False:
+			print "can't search: no last.fm account details"
+			callback (None, None, RB.ExtDBSourceType.NONE, args)
+			return
+
+		album = key.get_field("album")
+		albumartist = key.get_field("album-artist")
+		album_mbid = key.get_field("musicbrainz-albumid")
+		artist = key.get_field("artist")
+
+		if albumartist in ("", _("Unknown")):
+			albumartist = None
+		if artist in ("", _("Unknown")):
+			artist = None
+		if album in ("", _("Unknown")):
+			album = None
+
+		if album == None or (albumartist == None and artist == None):
+			print "can't search: no useful details"
+			callback (None, None, RB.ExtDBSourceType.NONE, args)
+			return
+
+		self.searches = []
+		if artist != albumartist and artist != None:
+			self.searches.append([artist, album, album_mbid, "artist"])
+		if albumartist != None:
+			self.searches.append([albumartist, album, album_mbid, "album-artist"])
+		self.searches.append(["Various Artists", album, album_mbid, "album-artist"])
+
+		self.callback = callback
+		self.callback_args = args
+		self.search_next()
diff --git a/plugins/artsearch/local.py b/plugins/artsearch/local.py
new file mode 100644
index 0000000..79be909
--- /dev/null
+++ b/plugins/artsearch/local.py
@@ -0,0 +1,147 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+#
+# Copyright (C) 2006 - Ed Catmur <ed catmur co uk>
+# 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 os
+
+from gi.repository import RB
+from gi.repository import GObject, GLib, Gio
+
+IMAGE_NAMES = ["cover", "album", "albumart", "front", ".folder", "folder"]
+ITEMS_PER_NOTIFICATION = 10
+
+IGNORED_SCHEMES = ('http', 'cdda', 'daap', 'mms')
+
+def file_root (f_name):
+	return os.path.splitext (f_name)[0].lower ()
+
+def shared_prefix_length (a, b):
+	l = 0
+	while a[l] == b[l]:
+		l = l+1
+	return l
+
+class LocalSearch:
+	def __init__ (self):
+		pass
+
+	def finished(self, results):
+		parent = self.file.get_parent()
+		ordered = []
+
+		# Compare lower case, without file extension
+		for name in [file_root (self.file.get_basename())] + IMAGE_NAMES:
+			for f_name in results:
+				if file_root (f_name) == name:
+					ordered.append(parent.resolve_relative_path(f_name).get_uri())
+
+		# look for file names containing the artist and album (case-insensitive)
+		# (mostly for jamendo downloads)
+		artist = self.artist.lower()
+		album = self.album.lower()
+		for f_name in results:
+			f_root = file_root (f_name).lower()
+			if f_root.find (artist) != -1 and f_root.find (album) != -1:
+				ordered.append(parent.resolve_relative_path(f_name).get_uri())
+
+		# if that didn't work, look for the longest shared prefix
+		# only accept matches longer than 2 to avoid weird false positives
+		match_len = 2
+		match = None
+		for f_name in results:
+			pl = shared_prefix_length(f_name, self.file.get_basename())
+			if pl > match_len:
+				match_len = pl
+				match = f_name
+
+		if match is not None:
+			ordered.append(parent.resolve_relative_path(match).get_uri())
+
+		key = RB.ExtDBKey.create("album", self.album)
+		key.add_field("album-artist", RB.ExtDBFieldType.OPTIONAL, self.artist)
+		self.callback(key, ordered, RB.ExtDBSourceType.USER, self.callback_args)
+
+	def _enum_dir_cb(self, fileenum, result, results):
+		try:
+			files = fileenum.next_files_finish(result)
+			if files is None or len(files) == 0:
+				print "okay, done; got %d files" % len(results)
+				self.finished(results)
+				return
+
+			for f in files:
+				ct = f.get_attribute_string("standard::content-type")
+				# assume readable unless told otherwise
+				readable = True
+				if f.has_attribute("access::can-read"):
+					readable = f.get_attribute_boolean("access::can-read")
+				if ct is not None and ct.startswith("image/") and readable:
+					results.append(f.get_name())
+
+			fileenum.next_files_async(ITEMS_PER_NOTIFICATION, GLib.PRIORITY_DEFAULT, None, self._enum_dir_cb, results)
+		except Exception, e:
+			print "okay, probably done: %s" % e
+			import sys
+			sys.excepthook(*sys.exc_info())
+			self.finished(results)
+
+
+	def _enum_children_cb(self, parent, result, data):
+		try:
+			enumfiles = parent.enumerate_children_finish(result)
+			enumfiles.next_files_async(ITEMS_PER_NOTIFICATION, GLib.PRIORITY_DEFAULT, None, self._enum_dir_cb, [])
+		except Exception, e:
+			print "okay, probably done: %s" % e
+			import sys
+			sys.excepthook(*sys.exc_info())
+			self.callback(None, None, RB.ExtDBSourceType.NONE, self.callback_args)
+
+
+	def search (self, key, last_time, callback, args):
+		# ignore last_time
+
+		location = key.get_field("location")
+		if location is None:
+			print "not searching, we don't have a location"
+			callback(None, None, RB.ExtDBSourceType.NONE, args)
+			return
+
+		self.file = Gio.file_new_for_uri(location)
+		if self.file.get_uri_scheme() in IGNORED_SCHEMES:
+			print 'not searching for local art for %s' % (self.file.get_uri())
+			callback(None, None, RB.ExtDBSourceType.NONE, args)
+			return
+
+		self.album = key.get_field("album")
+		self.artist = key.get_field("album-artist")
+		if self.artist in (None, ""):
+			self.artist = key.get_field("artist")
+		self.callback = callback
+		self.callback_args = args
+
+		print 'searching for local art for %s' % (self.file.get_uri())
+		parent = self.file.get_parent()
+		enumfiles = parent.enumerate_children_async("standard::content-type,access::can-read,standard::name", 0, 0, None, self._enum_children_cb, None)
diff --git a/plugins/artsearch/musicbrainz.py b/plugins/artsearch/musicbrainz.py
new file mode 100644
index 0000000..fdf90b7
--- /dev/null
+++ b/plugins/artsearch/musicbrainz.py
@@ -0,0 +1,104 @@
+# -*- 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 xml.dom.minidom as dom
+
+import rb
+from gi.repository import RB
+
+# musicbrainz URLs
+MUSICBRAINZ_RELEASE_URL = "http://musicbrainz.org/ws/2/release/%s?inc=artists";
+MUSICBRAINZ_RELEASE_PREFIX = "http://musicbrainz.org/release/";
+MUSICBRAINZ_RELEASE_SUFFIX = ".html"
+
+# musicbrainz IDs
+MUSICBRAINZ_VARIOUS_ARTISTS = "89ad4ac3-39f7-470e-963a-56509c546377"
+
+# Amazon URL bits
+AMAZON_IMAGE_URL = "http://images.amazon.com/images/P/%s.01.LZZZZZZZ.jpg";
+
+class MusicBrainzSearch(object):
+
+	def get_release_cb (self, data, args):
+		(key, callback, cbargs) = args
+		if data is None:
+			print "musicbrainz release request returned nothing"
+			callback(None, None, RB.ExtDBSourceType.NONE, *cbargs)
+			return
+
+		try:
+			parsed = dom.parseString(data)
+
+			storekey = RB.ExtDBKey.create('album', key.get_field('album'))
+
+			# check that there's an artist that isn't 'various artists'
+			artist_tags = parsed.getElementsByTagName('artist')
+			if len(artist_tags) > 0:
+				artist_id = artist_tags[0].attributes['id'].firstChild.data
+				if artist_id != MUSICBRAINZ_VARIOUS_ARTISTS:
+					# add the artist name (as album-artist) to the storage key
+					nametags = artist_tags[0].getElementsByTagName('name')
+					if len(nametags) > 0:
+						artistname = nametags[0].firstChild.data
+						print "got musicbrainz artist name %s" % artistname
+						storekey.add_field('album-artist', RB.ExtDBFieldType.OPTIONAL, artistname)
+
+
+			# look for an ASIN tag
+			asin_tags = parsed.getElementsByTagName('asin')
+			if len(asin_tags) > 0:
+				asin = asin_tags[0].firstChild.data
+
+				print "got ASIN %s" % asin
+				image_url = AMAZON_IMAGE_URL % asin
+
+				callback(storekey, image_url, RB.ExtDBSourceType.SEARCH, *cbargs)
+			else:
+				print "no ASIN for this release"
+				callback(None, None, RB.ExtDBSourceType.NONE, *cbargs)
+		except Exception, e:
+			print "exception parsing musicbrainz response: %s" % e
+			callback(None, None, RB.ExtDBSourceType.NONE, *cbargs)
+
+	def search(self, key, last_time, callback, *args):
+		key = key.copy()	# ugh
+		album_id = key.get_field("musicbrainz-albumid")
+		if album_id is None:
+			print "no musicbrainz release ID for this track"
+			callback(None, None, RB.ExtDBSourceType.NONE, *args)
+			return
+
+		if album_id.startswith(MUSICBRAINZ_RELEASE_PREFIX):
+			album_id = album_id[len(MUSICBRAINZ_RELEASE_PREFIX):]
+
+		if album_id.endswith(MUSICBRAINZ_RELEASE_SUFFIX):
+			album_id = album_id[:-len(MUSICBRAINZ_RELEASE_SUFFIX)]
+
+		print "stripped release ID: %s" % album_id
+
+		url = MUSICBRAINZ_RELEASE_URL % (album_id)
+		loader = rb.Loader()
+		loader.get_url(url, self.get_release_cb, (key, callback, args))
diff --git a/plugins/artsearch/oldcache.py b/plugins/artsearch/oldcache.py
new file mode 100644
index 0000000..eecf125
--- /dev/null
+++ b/plugins/artsearch/oldcache.py
@@ -0,0 +1,69 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+#
+# Copyright (C) 2011 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 os.path
+import urllib
+
+import rb
+from gi.repository import RB
+
+import gettext
+gettext.install('rhythmbox', RB.locale_dir())
+
+ART_FOLDER = os.path.expanduser(os.path.join(RB.user_cache_dir(), 'covers'))
+USEFUL = os.path.exists(ART_FOLDER)
+
+class OldCacheSearch(object):
+	def __init__(self):
+		pass
+
+	def filename (self, album, artist, extension):
+		artist = artist.replace('/', '-')
+		album = album.replace('/', '-')
+		return os.path.join(ART_FOLDER, '%s - %s.%s' % (artist, album, extension))
+
+	def search(self, key, last_time, callback, *args):
+		album = key.get_field("album")
+		artist = key.get_field("artist")
+		albumartist = key.get_field("album-artist")
+
+		print "looking for %s by (%s, %s)" % (album, artist, albumartist)
+		for field in ('album-artist', 'artist'):
+			artist = key.get_field(field)
+			if artist is None:
+				continue
+
+			for ext in ('jpg', 'png'):
+				path = self.filename(album, field, ext)
+				if os.path.exists(path):
+					print "found %s" % path
+					uri = "file://" + urllib.pathname2url(path)
+					storekey = RB.ExtDBKey.create('album', album)
+					storekey.add_field(field, RB.ExtDBFieldType.OPTIONAL, artist)
+					callback(storekey, uri, RB.ExtDBSourceType.SEARCH, *args)
+					return
+
+		callback(None, None, RB.ExtDBSourceType.NONE, *args)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 315c820..f26096a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -30,6 +30,9 @@ metadata/rb-metadata-gst.c
 [type: gettext/ini]plugins/artdisplay/artdisplay.plugin.in
 plugins/artdisplay/artdisplay.py
 plugins/artdisplay/LastFMCoverArtSearch.py
+[type: gettext/ini]plugins/artsearch/artsearch.plugin.in
+plugins/artsearch/artsearch.py
+plugins/artsearch/lastfm.py
 [type: gettext/glade]plugins/audiocd/album-info.ui
 [type: gettext/ini]plugins/audiocd/audiocd.plugin.in
 [type: gettext/glade]plugins/audiocd/multiple-album.ui



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]