Esempio n. 1
0
class ArtDisplayPlugin (gobject.GObject, Peas.Activatable):
	__gtype_name__ = 'ArtDisplayPlugin'
	object = gobject.property(type=gobject.GObject)

	def __init__ (self):
		gobject.GObject.__init__ (self)

	def do_activate (self):
		shell = self.object
		sp = shell.props.shell_player
		self.player_cb_ids = (
			sp.connect ('playing-song-changed', self.playing_entry_changed),
			sp.connect ('playing-changed', self.playing_changed)
		)
		self.emitting_uri_notify = False
		db = shell.props.db
		self.db_cb_ids = (
			db.connect_after ('entry-extra-metadata-request::rb:coverArt', self.cover_art_request),
			db.connect_after ('entry-extra-metadata-notify::rb:coverArt', self.cover_art_notify),
			db.connect_after ('entry-extra-metadata-request::rb:coverArt-uri', self.cover_art_uri_request),
			db.connect_after ('entry-extra-metadata-notify::rb:coverArt-uri', self.cover_art_uri_notify),
			db.connect_after ('entry-extra-metadata-gather', self.cover_art_uri_gather),
		)
		self.art_widget = ArtDisplayWidget (rb.find_plugin_file (self, ART_MISSING_ICON + ".svg"))
		self.art_widget.connect ('pixbuf-dropped', self.on_set_pixbuf)
		self.art_widget.connect ('uri-dropped', self.on_set_uri)
		self.art_widget.connect ('get-max-size', self.get_max_art_size)
		self.art_widget.connect ('button-press-event', self.on_button_press)
		self.art_container = Gtk.VBox ()
		self.art_container.pack_start (self.art_widget, True, True, 6)
		shell.add_widget (self.art_container, RB.ShellUILocation.SIDEBAR, False, True)
		self.art_db = CoverArtDatabase ()
		self.current_entry, self.current_pixbuf = None, None
		self.playing_entry_changed (sp, sp.get_playing_entry ())

	def do_deactivate (self):

		shell = self.object
		sp = shell.props.shell_player
		for id in self.player_cb_ids:
			sp.disconnect (id)
		self.player_cb_ids = ()

		db = shell.props.db
		for id in self.db_cb_ids:
			db.disconnect (id)
		self.db_cb_ids = ()

		shell.remove_widget (self.art_container, RB.ShellUILocation.SIDEBAR)
		self.art_widget.disconnect_handlers ()
		self.art_widget = None
		self.art_container = None
		self.art_db = None

	def playing_changed (self, sp, playing):
		self.set_entry(sp.get_playing_entry ())

	def playing_entry_changed (self, sp, entry):
		self.set_entry(entry)

	def set_entry (self, entry):
		if rb.entry_equal(entry, self.current_entry):
			return

		self.art_widget.set (entry, None, None, None, None, True)
		self.art_container.show_all ()
		# Intitates search in the database (which checks art cache, internet etc.)
		self.current_entry = entry
		self.current_pixbuf = None

		shell = self.object
		db = shell.props.db
		self.art_db.get_pixbuf(db, entry, True, self.on_get_pixbuf_completed)

	def on_get_pixbuf_completed(self, entry, pixbuf, uri, tooltip_image, tooltip_text):
		# Set the pixbuf for the entry returned from the art db
		if rb.entry_equal(entry, self.current_entry):
			self.current_pixbuf = pixbuf

			if tooltip_image is None:
				pb = None
			elif tooltip_image.startswith("/"):
				pb = GdkPixbuf.Pixbuf.new_from_file(tooltip_image)
			else:
				f = rb.find_plugin_file (self, tooltip_image)
				pb = GdkPixbuf.Pixbuf.new_from_file(f)
			self.art_widget.set (entry, pixbuf, uri, pb, tooltip_text, False)

		if pixbuf:
			# This might be from a playing-changed signal,
			# in which case consumers won't be ready yet.
			def idle_emit_art():
				shell = self.object
				db = shell.props.db
				db.emit_entry_extra_metadata_notify (entry, "rb:coverArt", pixbuf)
				if uri:
					self.emitting_uri_notify = True
					db.emit_entry_extra_metadata_notify (entry, "rb:coverArt-uri", uri)
					self.emitting_uri_notify = False
				return False
			gobject.idle_add(idle_emit_art)

	def cover_art_request (self, db, entry):
		a = [None]
		def callback(entry, pixbuf, uri, tooltip_image, tooltip_text):
			a[0] = pixbuf
			self.on_get_pixbuf_completed(entry, pixbuf, uri, tooltip_image, tooltip_text)

		playing = rb.entry_equal(entry, self.current_entry)
		self.art_db.get_pixbuf(db, entry, playing, callback)

		# If callback was called synchronously we can return a pixmap
		return a[0]

	def cover_art_notify (self, db, entry, field, metadata):
		if entry != self.current_entry:
			return
		if not isinstance (metadata, GdkPixbuf.Pixbuf):
			return
		self.art_db.cancel_get_pixbuf (entry)
		if self.current_pixbuf == metadata:
			return

		# cache the pixbuf so we can provide a url
		uri = self.art_db.cache_pixbuf (db, entry, metadata)
		self.art_widget.set (entry, metadata, uri, None, None, False)
		self.emitting_uri_notify = True
		db.emit_entry_extra_metadata_notify (entry, "rb:coverArt-uri", uri)
		self.emitting_uri_notify = False

	def cover_art_uri_notify (self, db, entry, field, metadata):
		if entry != self.current_entry:
			return

		if self.emitting_uri_notify:
			return

		if not metadata:
			print "got no-cover-art notification"
			self.art_widget.set (entry, None, None, False)
			db.emit_entry_extra_metadata_notify (entry, "rb:coverArt", None)
			return

		uri = str (metadata)
		def loader_cb_busted (data):
			if data and len (data) >= 1000:
				pbl = GdkPixbuf.PixbufLoader ()
				try:
					if pbl.write (data, len(data)) and pbl.close ():
						pixbuf = pbl.get_pixbuf ()
						if pixbuf:
							self.art_db.cancel_get_pixbuf (entry)
							self.on_get_pixbuf_completed (entry, pixbuf, uri, None, None)
				except GError:
					pass

		def loader_cb (data):
			if data and len(data) >= 1000:
				l = GdkPixbuf.PixbufLoader()
				l.write(data)
				l.close()
				pixbuf = l.get_pixbuf()
			else:
				pixbuf = None

			self.art_db.cancel_get_pixbuf (entry)
			self.on_get_pixbuf_completed (entry, pixbuf, uri, None, None)

		print "got cover art URI notification: %s" % (uri)

		# TODO use GdkPixbuf.new_from_stream_async()
		l = rb.Loader()
		l.get_url (uri, loader_cb)


	def cover_art_uri_request (self, db, entry):
		if rb.entry_equal(entry, self.current_entry):
			return self.art_widget.current_uri

	def cover_art_uri_gather (self, db, entry, metadata):
		if rb.entry_equal(entry, self.current_entry) and self.art_widget.current_uri:
			metadata ['rb:coverArt-uri'] = self.art_widget.current_uri

	def on_set_pixbuf (self, widget, entry, pixbuf):
		shell = self.object
		db = shell.props.db
		self.art_db.set_pixbuf (db, entry, pixbuf, self.on_get_pixbuf_completed)

	def on_set_uri (self, widget, entry, uri):
		shell = self.object
		db = shell.props.db
		self.art_db.set_pixbuf_from_uri (db, entry, uri, self.on_get_pixbuf_completed)

	def get_max_art_size (self, widget):
		# limit the art image to a third of the window height to prevent it from
		# forcing the window to resize, obscuring everything else, and so on.
		shell = self.object
		(width, height) = shell.props.window.get_size()
		return height / 3

	def on_button_press (self, widget, event):
		# on double clicks, open the cover image (if there is one) in the default
		# image viewer

		doubleclick = Gdk.EventType._2BUTTON_PRESS
		if event.type != doubleclick or event.button != 1:
			return

		if self.art_widget.current_uri is None:
			return

		f = Gio.file_new_for_uri(self.art_widget.current_uri)
		shell = self.object
		Gtk.show_uri(shell.props.window.get_screen(), f.get_uri(), event.time)
Esempio n. 2
0
class ArtDisplayPlugin (rb.Plugin):
	def __init__ (self):
		rb.Plugin.__init__ (self)

	def activate (self, shell):
		self.shell = shell
		sp = shell.get_player ()
		self.player_cb_ids = (
			sp.connect ('playing-song-changed', self.playing_entry_changed),
			sp.connect ('playing-changed', self.playing_changed)
		)
		db = shell.get_property ("db")
		self.db_cb_ids = (
			db.connect_after ('entry-extra-metadata-request::rb:coverArt', self.cover_art_request),
			db.connect_after ('entry-extra-metadata-notify::rb:coverArt', self.cover_art_notify),
			db.connect_after ('entry-extra-metadata-request::rb:coverArt-uri', self.cover_art_uri_request),
			db.connect_after ('entry-extra-metadata-notify::rb:coverArt-uri', self.cover_art_uri_notify),
			db.connect_after ('entry-extra-metadata-gather', self.cover_art_uri_gather),
		)
		self.art_widget = ArtDisplayWidget (self.find_file (ART_MISSING_ICON + ".svg"))
		self.art_widget.connect ('pixbuf-dropped', self.on_set_pixbuf)
		self.art_widget.connect ('uri-dropped', self.on_set_uri)
		self.art_widget.connect ('get-max-size', self.get_max_art_size)
		self.art_widget.connect ('button-press-event', self.on_button_press)
		self.art_container = gtk.VBox ()
		self.art_container.pack_start (self.art_widget, padding=6)
		shell.add_widget (self.art_container, rb.SHELL_UI_LOCATION_SIDEBAR)
		self.art_db = CoverArtDatabase ()
		self.current_entry, self.current_pixbuf = None, None
		self.playing_entry_changed (sp, sp.get_playing_entry ())

	def deactivate (self, shell):
		self.shell = None

		sp = shell.get_player ()
		for id in self.player_cb_ids:
			sp.disconnect (id)
		self.player_cb_ids = ()

		db = shell.get_property ("db")
		for id in self.db_cb_ids:
			db.disconnect (id)
		self.db_cb_ids = ()

		shell.remove_widget (self.art_container, rb.SHELL_UI_LOCATION_SIDEBAR)
		self.art_widget.disconnect_handlers ()
		self.art_widget = None
		self.art_db = None

	def playing_changed (self, sp, playing):
		self.set_entry(sp.get_playing_entry ())

	def playing_entry_changed (self, sp, entry):
		self.set_entry(entry)

	def set_entry (self, entry):
		if entry == self.current_entry:
			return
		db = self.shell.get_property ("db")

		self.art_widget.set (entry, None, None, None, None, True)
		self.art_container.show_all ()
		# Intitates search in the database (which checks art cache, internet etc.)
		self.current_entry = entry
		self.current_pixbuf = None
		self.art_db.get_pixbuf(db, entry, True, self.on_get_pixbuf_completed)

	def on_get_pixbuf_completed(self, entry, pixbuf, uri, tooltip_image, tooltip_text):
		# Set the pixbuf for the entry returned from the art db
		if entry == self.current_entry:
			self.current_pixbuf = pixbuf

			if tooltip_image is None:
				pb = None
			elif tooltip_image.startswith("/"):
				pb = gtk.gdk.pixbuf_new_from_file(tooltip_image)
			else:
				f = self.find_file(tooltip_image)
				pb = gtk.gdk.pixbuf_new_from_file(f)
			self.art_widget.set (entry, pixbuf, uri, pb, tooltip_text, False)

		if pixbuf:
			db = self.shell.get_property ("db")
			# This might be from a playing-changed signal,
			# in which case consumers won't be ready yet.
			def idle_emit_art():
				db.emit_entry_extra_metadata_notify (entry, "rb:coverArt", pixbuf)
				return False
			gobject.idle_add(idle_emit_art)

	def cover_art_request (self, db, entry):
		a = [None]
		def callback(entry, pixbuf, uri, tooltip_image, tooltip_text):
			a[0] = pixbuf
			self.on_get_pixbuf_completed(entry, pixbuf, uri, tooltip_image, tooltip_text)

		playing = (entry == self.current_entry)
		self.art_db.get_pixbuf(db, entry, playing, callback)

		# If callback was called synchronously we can return a pixmap
		return a[0]

	def cover_art_notify (self, db, entry, field, metadata):
		if entry != self.current_entry:
			return
		if not isinstance (metadata, gtk.gdk.Pixbuf):
			return
		self.art_db.cancel_get_pixbuf (entry)
		if self.current_pixbuf == metadata:
			return
		self.art_widget.set (entry, metadata, None, None, None, False)

	def cover_art_uri_notify (self, db, entry, field, metadata):
		if entry != self.current_entry:
			return

		if not metadata:
			print "got no-cover-art notification"
			self.art_widget.set (entry, None, None, False)
			db.emit_entry_extra_metadata_notify (entry, "rb:coverArt", None)
			return

		uri = str (metadata)
		def loader_cb (data):
			if data and len (data) >= 1000:
				pbl = gtk.gdk.PixbufLoader ()
				try:
					if pbl.write (data) and pbl.close ():
						pixbuf = pbl.get_pixbuf ()
						if pixbuf:
							self.art_db.cancel_get_pixbuf (entry)
							self.on_get_pixbuf_completed (entry, pixbuf, uri, None, None)
				except GError:
					pass

		print "got cover art URI notification: %s" % (uri)
		l = rb.Loader()
		l.get_url (uri, loader_cb)

	def cover_art_uri_request (self, db, entry):
		if entry == self.current_entry:
			return self.art_widget.current_uri

	def cover_art_uri_gather (self, db, entry, metadata):
		if entry == self.current_entry and self.art_widget.current_uri:
			metadata ['rb:coverArt-uri'] = self.art_widget.current_uri

	def on_set_pixbuf (self, widget, entry, pixbuf):
		db = self.shell.get_property ("db")
		self.art_db.set_pixbuf (db, entry, pixbuf, self.on_get_pixbuf_completed)

	def on_set_uri (self, widget, entry, uri):
		db = self.shell.get_property ("db")
		self.art_db.set_pixbuf_from_uri (db, entry, uri, self.on_get_pixbuf_completed)

	def get_max_art_size (self, widget):
		# limit the art image to a third of the window height to prevent it from
		# forcing the window to resize, obscuring everything else, and so on.
		(width, height) = self.shell.props.window.get_size()
		return height / 3

	def on_button_press (self, widget, event):
		# on double clicks, open the cover image (if there is one) in the default
		# image viewer
		if event.type != gtk.gdk._2BUTTON_PRESS or event.button != 1:
			return

		if self.art_widget.current_uri is None:
			return

		f = gio.File(self.art_widget.current_uri)
		gtk.show_uri(self.shell.props.window.get_screen(), f.get_uri(), event.time)
Esempio n. 3
0
class ArtDisplayPlugin(rb.Plugin):
    def __init__(self):
        rb.Plugin.__init__(self)

    def activate(self, shell):
        self.shell = shell
        sp = shell.get_player()
        self.player_cb_ids = (sp.connect('playing-song-changed',
                                         self.playing_entry_changed),
                              sp.connect('playing-changed',
                                         self.playing_changed))
        db = shell.get_property("db")
        self.db_cb_ids = (
            db.connect_after('entry-extra-metadata-request::rb:coverArt',
                             self.cover_art_request),
            db.connect_after('entry-extra-metadata-notify::rb:coverArt',
                             self.cover_art_notify),
            db.connect_after('entry-extra-metadata-request::rb:coverArt-uri',
                             self.cover_art_uri_request),
            db.connect_after('entry-extra-metadata-notify::rb:coverArt-uri',
                             self.cover_art_uri_notify),
            db.connect_after('entry-extra-metadata-gather',
                             self.cover_art_uri_gather),
        )
        self.art_widget = ArtDisplayWidget(
            self.find_file(ART_MISSING_ICON + ".svg"))
        self.art_widget.connect('pixbuf-dropped', self.on_set_pixbuf)
        self.art_widget.connect('uri-dropped', self.on_set_uri)
        self.art_widget.connect('get-max-size', self.get_max_art_size)
        self.art_widget.connect('button-press-event', self.on_button_press)
        self.art_container = gtk.VBox()
        self.art_container.pack_start(self.art_widget, padding=6)
        shell.add_widget(self.art_container, rb.SHELL_UI_LOCATION_SIDEBAR)
        self.art_db = CoverArtDatabase()
        self.current_entry, self.current_pixbuf = None, None
        self.playing_entry_changed(sp, sp.get_playing_entry())

    def deactivate(self, shell):
        self.shell = None

        sp = shell.get_player()
        for id in self.player_cb_ids:
            sp.disconnect(id)
        self.player_cb_ids = ()

        db = shell.get_property("db")
        for id in self.db_cb_ids:
            db.disconnect(id)
        self.db_cb_ids = ()

        shell.remove_widget(self.art_container, rb.SHELL_UI_LOCATION_SIDEBAR)
        self.art_widget.disconnect_handlers()
        self.art_widget = None
        self.art_db = None

    def playing_changed(self, sp, playing):
        self.set_entry(sp.get_playing_entry())

    def playing_entry_changed(self, sp, entry):
        self.set_entry(entry)

    def set_entry(self, entry):
        if entry == self.current_entry:
            return
        db = self.shell.get_property("db")

        self.art_widget.set(entry, None, None, None, None, True)
        self.art_container.show_all()
        # Intitates search in the database (which checks art cache, internet etc.)
        self.current_entry = entry
        self.current_pixbuf = None
        self.art_db.get_pixbuf(db, entry, True, self.on_get_pixbuf_completed)

    def on_get_pixbuf_completed(self, entry, pixbuf, uri, tooltip_image,
                                tooltip_text):
        # Set the pixbuf for the entry returned from the art db
        if entry == self.current_entry:
            self.current_pixbuf = pixbuf

            if tooltip_image is None:
                pb = None
            elif tooltip_image.startswith("/"):
                pb = gtk.gdk.pixbuf_new_from_file(tooltip_image)
            else:
                f = self.find_file(tooltip_image)
                pb = gtk.gdk.pixbuf_new_from_file(f)
            self.art_widget.set(entry, pixbuf, uri, pb, tooltip_text, False)

        if pixbuf:
            db = self.shell.get_property("db")

            # This might be from a playing-changed signal,
            # in which case consumers won't be ready yet.
            def idle_emit_art():
                db.emit_entry_extra_metadata_notify(entry, "rb:coverArt",
                                                    pixbuf)
                return False

            gobject.idle_add(idle_emit_art)

    def cover_art_request(self, db, entry):
        a = [None]

        def callback(entry, pixbuf, uri, tooltip_image, tooltip_text):
            a[0] = pixbuf
            self.on_get_pixbuf_completed(entry, pixbuf, uri, tooltip_image,
                                         tooltip_text)

        playing = (entry == self.current_entry)
        self.art_db.get_pixbuf(db, entry, playing, callback)

        # If callback was called synchronously we can return a pixmap
        return a[0]

    def cover_art_notify(self, db, entry, field, metadata):
        if entry != self.current_entry:
            return
        if not isinstance(metadata, gtk.gdk.Pixbuf):
            return
        self.art_db.cancel_get_pixbuf(entry)
        if self.current_pixbuf == metadata:
            return
        self.art_widget.set(entry, metadata, None, None, None, False)

    def cover_art_uri_notify(self, db, entry, field, metadata):
        if entry != self.current_entry:
            return

        if not metadata:
            print "got no-cover-art notification"
            self.art_widget.set(entry, None, None, False)
            db.emit_entry_extra_metadata_notify(entry, "rb:coverArt", None)
            return

        uri = str(metadata)

        def loader_cb(data):
            if data and len(data) >= 1000:
                pbl = gtk.gdk.PixbufLoader()
                try:
                    if pbl.write(data) and pbl.close():
                        pixbuf = pbl.get_pixbuf()
                        if pixbuf:
                            self.art_db.cancel_get_pixbuf(entry)
                            self.on_get_pixbuf_completed(
                                entry, pixbuf, uri, None, None)
                except GError:
                    pass

        print "got cover art URI notification: %s" % (uri)
        l = rb.Loader()
        l.get_url(uri, loader_cb)

    def cover_art_uri_request(self, db, entry):
        if entry == self.current_entry:
            return self.art_widget.current_uri

    def cover_art_uri_gather(self, db, entry, metadata):
        if entry == self.current_entry and self.art_widget.current_uri:
            metadata['rb:coverArt-uri'] = self.art_widget.current_uri

    def on_set_pixbuf(self, widget, entry, pixbuf):
        db = self.shell.get_property("db")
        self.art_db.set_pixbuf(db, entry, pixbuf, self.on_get_pixbuf_completed)

    def on_set_uri(self, widget, entry, uri):
        db = self.shell.get_property("db")
        self.art_db.set_pixbuf_from_uri(db, entry, uri,
                                        self.on_get_pixbuf_completed)

    def get_max_art_size(self, widget):
        # limit the art image to a third of the window height to prevent it from
        # forcing the window to resize, obscuring everything else, and so on.
        (width, height) = self.shell.props.window.get_size()
        return height / 3

    def on_button_press(self, widget, event):
        # on double clicks, open the cover image (if there is one) in the default
        # image viewer
        if event.type != gtk.gdk._2BUTTON_PRESS or event.button != 1:
            return

        if self.art_widget.current_uri is None:
            return

        f = gio.File(self.art_widget.current_uri)
        gtk.show_uri(self.shell.props.window.get_screen(), f.get_uri(),
                     event.time)
Esempio n. 4
0
class ArtDisplayPlugin(gobject.GObject, Peas.Activatable):
    __gtype_name__ = 'ArtDisplayPlugin'
    object = gobject.property(type=gobject.GObject)

    def __init__(self):
        gobject.GObject.__init__(self)

    def do_activate(self):
        shell = self.object
        sp = shell.props.shell_player
        self.player_cb_ids = (sp.connect('playing-song-changed',
                                         self.playing_entry_changed),
                              sp.connect('playing-changed',
                                         self.playing_changed))
        self.emitting_uri_notify = False
        db = shell.props.db
        self.db_cb_ids = (
            db.connect_after('entry-extra-metadata-request::rb:coverArt',
                             self.cover_art_request),
            db.connect_after('entry-extra-metadata-notify::rb:coverArt',
                             self.cover_art_notify),
            db.connect_after('entry-extra-metadata-request::rb:coverArt-uri',
                             self.cover_art_uri_request),
            db.connect_after('entry-extra-metadata-notify::rb:coverArt-uri',
                             self.cover_art_uri_notify),
            db.connect_after('entry-extra-metadata-gather',
                             self.cover_art_uri_gather),
        )
        self.art_widget = ArtDisplayWidget(
            rb.find_plugin_file(self, ART_MISSING_ICON + ".svg"))
        self.art_widget.connect('pixbuf-dropped', self.on_set_pixbuf)
        self.art_widget.connect('uri-dropped', self.on_set_uri)
        self.art_widget.connect('get-max-size', self.get_max_art_size)
        self.art_widget.connect('button-press-event', self.on_button_press)
        self.art_container = Gtk.VBox()
        self.art_container.pack_start(self.art_widget, True, True, 6)
        shell.add_widget(self.art_container, RB.ShellUILocation.SIDEBAR, False,
                         True)
        self.art_db = CoverArtDatabase()
        self.current_entry, self.current_pixbuf = None, None
        self.playing_entry_changed(sp, sp.get_playing_entry())

    def do_deactivate(self):

        shell = self.object
        sp = shell.props.shell_player
        for id in self.player_cb_ids:
            sp.disconnect(id)
        self.player_cb_ids = ()

        db = shell.props.db
        for id in self.db_cb_ids:
            db.disconnect(id)
        self.db_cb_ids = ()

        shell.remove_widget(self.art_container, RB.ShellUILocation.SIDEBAR)
        self.art_widget.disconnect_handlers()
        self.art_widget = None
        self.art_container = None
        self.art_db = None

    def playing_changed(self, sp, playing):
        self.set_entry(sp.get_playing_entry())

    def playing_entry_changed(self, sp, entry):
        self.set_entry(entry)

    def set_entry(self, entry):
        if rb.entry_equal(entry, self.current_entry):
            return

        self.art_widget.set(entry, None, None, None, None, True)
        self.art_container.show_all()
        # Intitates search in the database (which checks art cache, internet etc.)
        self.current_entry = entry
        self.current_pixbuf = None

        shell = self.object
        db = shell.props.db
        self.art_db.get_pixbuf(db, entry, True, self.on_get_pixbuf_completed)

    def on_get_pixbuf_completed(self, entry, pixbuf, uri, tooltip_image,
                                tooltip_text):
        # Set the pixbuf for the entry returned from the art db
        if rb.entry_equal(entry, self.current_entry):
            self.current_pixbuf = pixbuf

            if tooltip_image is None:
                pb = None
            elif tooltip_image.startswith("/"):
                pb = GdkPixbuf.Pixbuf.new_from_file(tooltip_image)
            else:
                f = rb.find_plugin_file(self, tooltip_image)
                pb = GdkPixbuf.Pixbuf.new_from_file(f)
            self.art_widget.set(entry, pixbuf, uri, pb, tooltip_text, False)

        if pixbuf:
            # This might be from a playing-changed signal,
            # in which case consumers won't be ready yet.
            def idle_emit_art():
                shell = self.object
                db = shell.props.db
                db.emit_entry_extra_metadata_notify(entry, "rb:coverArt",
                                                    pixbuf)
                if uri:
                    self.emitting_uri_notify = True
                    db.emit_entry_extra_metadata_notify(
                        entry, "rb:coverArt-uri", uri)
                    self.emitting_uri_notify = False
                return False

            gobject.idle_add(idle_emit_art)

    def cover_art_request(self, db, entry):
        a = [None]

        def callback(entry, pixbuf, uri, tooltip_image, tooltip_text):
            a[0] = pixbuf
            self.on_get_pixbuf_completed(entry, pixbuf, uri, tooltip_image,
                                         tooltip_text)

        playing = rb.entry_equal(entry, self.current_entry)
        self.art_db.get_pixbuf(db, entry, playing, callback)

        # If callback was called synchronously we can return a pixmap
        return a[0]

    def cover_art_notify(self, db, entry, field, metadata):
        if entry != self.current_entry:
            return
        if not isinstance(metadata, GdkPixbuf.Pixbuf):
            return
        self.art_db.cancel_get_pixbuf(entry)
        if self.current_pixbuf == metadata:
            return

        # cache the pixbuf so we can provide a url
        uri = self.art_db.cache_pixbuf(db, entry, metadata)
        self.art_widget.set(entry, metadata, uri, None, None, False)
        self.emitting_uri_notify = True
        db.emit_entry_extra_metadata_notify(entry, "rb:coverArt-uri", uri)
        self.emitting_uri_notify = False

    def cover_art_uri_notify(self, db, entry, field, metadata):
        if entry != self.current_entry:
            return

        if self.emitting_uri_notify:
            return

        if not metadata:
            print "got no-cover-art notification"
            self.art_widget.set(entry, None, None, False)
            db.emit_entry_extra_metadata_notify(entry, "rb:coverArt", None)
            return

        uri = str(metadata)

        def loader_cb_busted(data):
            if data and len(data) >= 1000:
                pbl = GdkPixbuf.PixbufLoader()
                try:
                    if pbl.write(data, len(data)) and pbl.close():
                        pixbuf = pbl.get_pixbuf()
                        if pixbuf:
                            self.art_db.cancel_get_pixbuf(entry)
                            self.on_get_pixbuf_completed(
                                entry, pixbuf, uri, None, None)
                except GError:
                    pass

        def loader_cb(data):
            if data and len(data) >= 1000:
                l = GdkPixbuf.PixbufLoader()
                l.write(data)
                l.close()
                pixbuf = l.get_pixbuf()
            else:
                pixbuf = None

            self.art_db.cancel_get_pixbuf(entry)
            self.on_get_pixbuf_completed(entry, pixbuf, uri, None, None)

        print "got cover art URI notification: %s" % (uri)

        # TODO use GdkPixbuf.new_from_stream_async()
        l = rb.Loader()
        l.get_url(uri, loader_cb)

    def cover_art_uri_request(self, db, entry):
        if rb.entry_equal(entry, self.current_entry):
            return self.art_widget.current_uri

    def cover_art_uri_gather(self, db, entry, metadata):
        if rb.entry_equal(entry,
                          self.current_entry) and self.art_widget.current_uri:
            metadata['rb:coverArt-uri'] = self.art_widget.current_uri

    def on_set_pixbuf(self, widget, entry, pixbuf):
        shell = self.object
        db = shell.props.db
        self.art_db.set_pixbuf(db, entry, pixbuf, self.on_get_pixbuf_completed)

    def on_set_uri(self, widget, entry, uri):
        shell = self.object
        db = shell.props.db
        self.art_db.set_pixbuf_from_uri(db, entry, uri,
                                        self.on_get_pixbuf_completed)

    def get_max_art_size(self, widget):
        # limit the art image to a third of the window height to prevent it from
        # forcing the window to resize, obscuring everything else, and so on.
        shell = self.object
        (width, height) = shell.props.window.get_size()
        return height / 3

    def on_button_press(self, widget, event):
        # on double clicks, open the cover image (if there is one) in the default
        # image viewer

        doubleclick = Gdk.EventType._2BUTTON_PRESS
        if event.type != doubleclick or event.button != 1:
            return

        if self.art_widget.current_uri is None:
            return

        f = Gio.file_new_for_uri(self.art_widget.current_uri)
        shell = self.object
        Gtk.show_uri(shell.props.window.get_screen(), f.get_uri(), event.time)