Index: CoverArtDatabase.py
===================================================================
--- CoverArtDatabase.py	(revision 5540)
+++ CoverArtDatabase.py	(working copy)
@@ -26,10 +26,11 @@
 from PodcastCoverArtSearch import PodcastCoverArtSearch
 from AmazonCoverArtSearch import AmazonCoverArtSearch
 from LocalCoverArtSearch import LocalCoverArtSearch
+from EmbeddedArtSearch import EmbeddedArtSearch
 
 from urllib import unquote
 
-ART_SEARCHES_LOCAL = [LocalCoverArtSearch]
+ART_SEARCHES_LOCAL = [LocalCoverArtSearch, EmbeddedArtSearch]
 ART_SEARCHES_REMOTE = [PodcastCoverArtSearch, AmazonCoverArtSearch]
 ART_FOLDER = '~/.gnome2/rhythmbox/covers'
 ART_CACHE_EXTENSION = 'jpg'
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 5540)
+++ Makefile.am	(working copy)
@@ -6,4 +6,5 @@
 	AmazonCoverArtSearch.py		\
 	LocalCoverArtSearch.py		\
 	CoverArtDatabase.py		\
+	EmbeddedArtSearch.py            \
 	__init__.py
Index: EmbeddedArtSearch.py
===================================================================
--- EmbeddedArtSearch.py	(revision 0)
+++ EmbeddedArtSearch.py	(revision 0)
@@ -0,0 +1,128 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*- 
+#
+# Copyright (C) 2007-2008 - John Millikin <jmillikin@gmail.com>
+#
+# 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.
+# 
+# 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
+import gnomevfs
+
+import codecs
+import sys
+if 'gst' not in sys.modules:
+	import pygst
+	pygst.require ("0.10")
+import gst
+
+def extended_comment_to_dict (comments):
+	def split (comment):
+		key, value = comment.split ('=', 1)
+		return key.upper (), value
+		
+	try:
+		return dict (split (comment) for comment in comments)
+	except Exception, e:
+		print 'Error decoding extended comment: %s' % e
+		return {}
+		
+def on_message (bus, message, pipeline, on_found_data):
+	t = message.type
+	
+	if t == gst.MESSAGE_STATE_CHANGED and message.src == pipeline:
+		# If the pipeline has transitioned from READ to PAUSED, there
+		# is no more metadata to be read.
+		change = message.structure
+		if (change['old-state'] == gst.STATE_READY and
+		    change['new-state'] == gst.STATE_PAUSED):
+		    	print 'Finished metadata with no cover art'
+			pipeline.set_state (gst.STATE_NULL)
+			on_found_data (None)
+			
+	if t == gst.MESSAGE_EOS:
+		print 'EOS before finding cover art'
+		pipeline.set_state (gst.STATE_NULL)
+		on_found_data (None)
+		
+	elif t == gst.MESSAGE_ERROR:
+		pipeline.set_state (gst.STATE_NULL)
+		err, debug = message.parse_error ()
+		print "Error: %s" % err, debug
+		on_found_data (None)
+		
+	elif t == gst.MESSAGE_TAG:
+		tags = message.structure
+		if tags.has_field ('image'):
+			print 'Got cover art in %r tag' % 'image'
+			pipeline.set_state (gst.STATE_NULL)
+			on_found_data (tags['image'])
+		elif tags.has_field ('extended-comment'):
+			extended = extended_comment_to_dict (tags['extended-comment'])
+			if 'COVERART' in extended:
+				try:
+					decoder = codecs.getdecoder ('base64')
+					art = decoder (extended['COVERART'])[0]
+					print 'Got cover art extended %r tag' % 'COVERART'
+					pipeline.set_state (gst.STATE_NULL)
+					on_found_data (art)
+				except Exception, e:
+					print 'Error reading base64 album art for %r: %s' % (filename, e)
+					
+def build_pipeline (uri, on_found_data):
+	pipeline = gst.Pipeline ("pipeline")
+	source = gst.element_factory_make ("gnomevfssrc", None)
+	decoder = gst.element_factory_make ("decodebin", None)
+	audiosink = gst.element_factory_make ("fakesink", None)
+	
+	source.set_property ('location', uri)
+	decoder.connect ('new-decoded-pad',
+	                 lambda dbin, pad, islast: pad.link (audiosink.get_pad ('sink')))
+	
+	pipeline.add (source)
+	pipeline.add (decoder)
+	pipeline.add (audiosink)
+	
+	gst.element_link_many (source, decoder)
+	bus = pipeline.get_bus ()
+	bus.add_signal_watch ()
+	bus.connect ('message', on_message, pipeline, on_found_data)
+	pipeline.set_state (gst.STATE_PAUSED)
+	status, state, pending = pipeline.get_state ()
+	
+	if state != gst.STATE_PAUSED:
+		print 'Error reading embedded art for %r' % (filename)
+		on_found_data (None)
+		
+class EmbeddedArtSearch (object):
+	def __init__ (self, loader):
+		self.loader = loader
+		
+	def search (self, db, entry, on_search_completed_cb, *args):
+		def on_found_data (image_data):
+			on_search_completed_cb (self, entry, [image_data], *args)
+			
+		try:
+			uri = gnomevfs.URI (entry.get_playback_uri())
+			print 'searching for embedded art for %r' % (uri)
+			build_pipeline (uri, on_found_data)
+		except Exception, e:
+			on_found_data (None)
+			
+	def search_next (self):
+		return False
+		
+	def get_best_match_urls (self, search_results):
+		for image_data in search_results:
+			yield image_data, None
+			
