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 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_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 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 activate (self, shell): self.shell = shell sp = shell.get_player () self.pec_id = sp.connect ('playing-song-changed', self.playing_entry_changed) self.pc_id = sp.connect ('playing-changed', self.playing_changed) self.art_widget = gtk.Image () self.art_widget.set_padding (0, 5) shell.add_widget (self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR) self.sa_id = self.art_widget.connect ('size-allocate', self.size_allocated) self.current_pixbuf = None self.art_db = CoverArtDatabase () self.resize_id = 0 self.resize_in_progress = False self.old_width = 0 self.fade_step = 0 self.fade_id = 0 self.current_entry = None entry = sp.get_playing_entry () self.playing_entry_changed (sp, entry) #dbus stuff self.bus = dbus.SessionBus() self.awn_obj = self.bus.get_object(BUS_NAME, OBJ_PATH)
def activate(self, shell): self.shell = shell sp = shell.get_player() self.pec_id = sp.connect("playing-song-changed", self.playing_entry_changed) self.pc_id = sp.connect("playing-changed", self.playing_changed) self.art_widget = gtk.Image() self.art_widget.set_padding(0, 5) shell.add_widget(self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR) self.sa_id = self.art_widget.connect("size-allocate", self.size_allocated) self.current_pixbuf = None self.art_db = CoverArtDatabase() self.resize_id = 0 self.resize_in_progress = False self.old_width = 0 self.fade_step = 0 self.fade_id = 0 self.current_entry = None entry = sp.get_playing_entry() self.playing_entry_changed(sp, entry) # dbus stuff self.bus = dbus.SessionBus() self.awn_obj = self.bus.get_object(BUS_NAME, OBJ_PATH)
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)
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)
class ArtDisplayPlugin(rb.Plugin): def __init__(self): rb.Plugin.__init__(self) def activate(self, shell): self.shell = shell sp = shell.get_player() self.pec_id = sp.connect("playing-song-changed", self.playing_entry_changed) self.pc_id = sp.connect("playing-changed", self.playing_changed) self.art_widget = gtk.Image() self.art_widget.set_padding(0, 5) shell.add_widget(self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR) self.sa_id = self.art_widget.connect("size-allocate", self.size_allocated) self.current_pixbuf = None self.art_db = CoverArtDatabase() self.resize_id = 0 self.resize_in_progress = False self.old_width = 0 self.fade_step = 0 self.fade_id = 0 self.current_entry = None entry = sp.get_playing_entry() self.playing_entry_changed(sp, entry) # dbus stuff self.bus = dbus.SessionBus() self.awn_obj = self.bus.get_object(BUS_NAME, OBJ_PATH) def deactivate(self, shell): self.shell = None sp = shell.get_player() sp.disconnect(self.pec_id) sp.disconnect(self.pc_id) shell.remove_widget(self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR) self.art_widget.disconnect(self.sa_id) self.art_widget = None self.current_pixbuf = None self.art_db = None self.action = None self.action_group = None if self.resize_id != 0: gobject.source_remove(self.resize_id) if self.fade_id != 0: gobject.source_remove(self.fade_id) 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 size_allocated(self, widget, allocation): if self.old_width == allocation.width: return if self.resize_id == 0: self.resize_id = gobject.idle_add(self.do_resize) self.resize_in_progress = True self.old_width = allocation.width def do_resize(self): if self.resize_in_progress: self.resize_in_progress = False self.update_displayed_art(True) ret = True else: self.update_displayed_art(False) self.resize_id = 0 ret = False return ret def set_entry(self, entry): if entry != self.current_entry: db = self.shell.get_property("db") # Intitates search in the database (which checks art cache, internet etc.) self.current_entry = entry self.art_db.get_pixbuf(db, entry, self.on_get_pixbuf_completed) def on_get_pixbuf_completed(self, entry, pixbuf): # Set the pixbuf for the entry returned from the art db if entry == self.current_entry: self.set_current_art(pixbuf) def fade_art(self, old_pixbuf, new_pixbuf): self.fade_step += 1.0 / FADE_STEPS if self.fade_step <= 0.999: # get pixbuf size ow = old_pixbuf.get_width() nw = new_pixbuf.get_width() oh = old_pixbuf.get_height() nh = new_pixbuf.get_height() # find scale, widget size and alpha ww = self.art_widget.parent.allocation.width wh = ww * (self.fade_step * (float(nh) / nw) + (1 - self.fade_step) * (float(oh) / ow)) sw = float(ww) / nw sh = float(wh) / nh alpha = int(self.fade_step * 255) self.current_pixbuf = old_pixbuf.scale_simple(int(ww), int(wh), gtk.gdk.INTERP_BILINEAR) new_pixbuf.composite( self.current_pixbuf, 0, 0, int(ww), int(wh), 0, 0, sw, sh, gtk.gdk.INTERP_BILINEAR, alpha ) self.art_widget.set_from_pixbuf(self.current_pixbuf) self.art_widget.show() return True else: self.current_pixbuf = new_pixbuf self.update_displayed_art(False) self.fade_id = 0 return False def set_current_art(self, pixbuf): current_pb = self.art_widget.get_pixbuf() if self.fade_id != 0: gobject.source_remove(self.fade_id) self.fade_id = 0 if current_pb is not None and pixbuf is not None: self.fade_step = 0.0 self.fade_art(current_pb, pixbuf) self.fade_id = gobject.timeout_add((FADE_TOTAL_TIME / FADE_STEPS), self.fade_art, current_pb, pixbuf) else: self.current_pixbuf = pixbuf self.update_displayed_art(False) def update_displayed_art(self, quick): if self.current_pixbuf is None: # don't use Image.clear(), not pygtk 2.6-compatible self.art_widget.set_from_pixbuf(None) self.art_widget.hide() try: self.awn_obj.UnsetTaskIconByName("rhythmbox") except: pass else: width = self.art_widget.parent.allocation.width height = self.current_pixbuf.get_height() * width / self.current_pixbuf.get_width() if quick: mode = gtk.gdk.INTERP_BILINEAR else: mode = gtk.gdk.INTERP_HYPER self.art_widget.set_from_pixbuf(self.current_pixbuf.scale_simple(width, height, mode)) self.art_widget.show() try: self.current_pixbuf.save(SAVE_PATH, "png") self.awn_obj.SetTaskIconByName("rhythmbox", "/tmp/rhythmbox.png") except: pass
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)
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)
class ArtDisplayPlugin (rb.Plugin): def __init__ (self): rb.Plugin.__init__ (self) def activate (self, shell): self.shell = shell sp = shell.get_player () self.pec_id = sp.connect ('playing-song-changed', self.playing_entry_changed) self.pc_id = sp.connect ('playing-changed', self.playing_changed) self.art_widget = gtk.Image () self.art_widget.set_padding (0, 5) shell.add_widget (self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR) self.sa_id = self.art_widget.connect ('size-allocate', self.size_allocated) self.current_pixbuf = None self.art_db = CoverArtDatabase () self.resize_id = 0 self.resize_in_progress = False self.old_width = 0 self.fade_step = 0 self.fade_id = 0 self.current_entry = None entry = sp.get_playing_entry () self.playing_entry_changed (sp, entry) #dbus stuff self.bus = dbus.SessionBus() self.awn_obj = self.bus.get_object(BUS_NAME, OBJ_PATH) def deactivate (self, shell): self.shell = None sp = shell.get_player () sp.disconnect (self.pec_id) sp.disconnect (self.pc_id) shell.remove_widget (self.art_widget, rb.SHELL_UI_LOCATION_SIDEBAR) self.art_widget.disconnect(self.sa_id) self.art_widget = None self.current_pixbuf = None self.art_db = None self.action = None self.action_group = None if self.resize_id != 0: gobject.source_remove (self.resize_id) if self.fade_id != 0: gobject.source_remove (self.fade_id) 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 size_allocated (self, widget, allocation): if self.old_width == allocation.width: return if self.resize_id == 0: self.resize_id = gobject.idle_add (self.do_resize) self.resize_in_progress = True self.old_width = allocation.width def do_resize(self): if self.resize_in_progress: self.resize_in_progress = False self.update_displayed_art (True) ret = True else: self.update_displayed_art (False) self.resize_id = 0 ret = False return ret def set_entry (self, entry): if entry != self.current_entry: db = self.shell.get_property ("db") # Intitates search in the database (which checks art cache, internet etc.) self.current_entry = entry self.art_db.get_pixbuf(db, entry, self.on_get_pixbuf_completed) def on_get_pixbuf_completed(self, entry, pixbuf): # Set the pixbuf for the entry returned from the art db if entry == self.current_entry: self.set_current_art (pixbuf) def fade_art (self, old_pixbuf, new_pixbuf): self.fade_step += 1.0 / FADE_STEPS if self.fade_step <= 0.999: # get pixbuf size ow = old_pixbuf.get_width () nw = new_pixbuf.get_width () oh = old_pixbuf.get_height () nh = new_pixbuf.get_height () # find scale, widget size and alpha ww = self.art_widget.parent.allocation.width wh = ww * (self.fade_step * (float(nh)/nw) + (1 - self.fade_step) * (float(oh)/ow)) sw = float(ww)/nw sh = float(wh)/nh alpha = int (self.fade_step * 255) self.current_pixbuf = old_pixbuf.scale_simple (int(ww), int(wh), gtk.gdk.INTERP_BILINEAR) new_pixbuf.composite (self.current_pixbuf, 0, 0, int(ww), int(wh), 0, 0, sw, sh, gtk.gdk.INTERP_BILINEAR, alpha) self.art_widget.set_from_pixbuf (self.current_pixbuf) self.art_widget.show () return True else: self.current_pixbuf = new_pixbuf self.update_displayed_art (False); self.fade_id = 0 return False def set_current_art (self, pixbuf): current_pb = self.art_widget.get_pixbuf () if self.fade_id != 0: gobject.source_remove (self.fade_id) self.fade_id = 0 if current_pb is not None and pixbuf is not None: self.fade_step = 0.0 self.fade_art (current_pb, pixbuf) self.fade_id = gobject.timeout_add ((FADE_TOTAL_TIME / FADE_STEPS), self.fade_art, current_pb, pixbuf) else: self.current_pixbuf = pixbuf; self.update_displayed_art (False); def update_displayed_art (self, quick): if self.current_pixbuf is None: # don't use Image.clear(), not pygtk 2.6-compatible self.art_widget.set_from_pixbuf (None) self.art_widget.hide () try: self.awn_obj.UnsetTaskIconByName ("rhythmbox") except: pass else: width = self.art_widget.parent.allocation.width height = self.current_pixbuf.get_height () * width / self.current_pixbuf.get_width () if quick: mode = gtk.gdk.INTERP_BILINEAR else: mode = gtk.gdk.INTERP_HYPER self.art_widget.set_from_pixbuf (self.current_pixbuf.scale_simple (width, height, mode)) self.art_widget.show () try: self.current_pixbuf.save (SAVE_PATH, "png") self.awn_obj.SetTaskIconByName("rhythmbox", "/tmp/rhythmbox.png") except: pass