def fetch(self): """ Collects covers for all outstanding items """ self.emit('fetch-started', len(self.outstanding)) # Speed up the following loop get_cover = COVER_MANAGER.get_cover save = COVER_MANAGER.save for i, album in enumerate(self.outstanding[:]): if self.stopper.is_set(): # Allow for "fetch-completed" signal to be emitted break cover_data = get_cover(self.album_tracks[album][0], save_cover=True) cover_pixbuf = pixbuf_from_data(cover_data) if cover_data else None self.emit('fetch-progress', i + 1) if not cover_pixbuf: continue self.outstanding.remove(album) self.emit('cover-fetched', album, cover_pixbuf) if i % 50 == 0: logger.debug('Saving cover database') save() logger.debug('Saving cover database') save() self.emit('fetch-completed', len(self.outstanding))
def set_cover(self, *args, **kwargs): if self.exaile is None: return elif not hasattr(self.exaile, 'player'): log.debug("Player not loaded, ignoring set_cover call") return try: os.remove(self.temp_icon_path) except (TypeError, OSError): pass if player.PLAYER.current is None: log.debug("Player stopped, removing AWN cover") self.unset_cover() elif not self.cover_display: self.unset_cover() else: image_data = covers.MANAGER.get_cover(player.PLAYER.current, set_only=True, use_default=True) pixbuf = pixbuf_from_data(image_data) descriptor, self.temp_icon_path = tempfile.mkstemp() pixbuf.save(self.temp_icon_path, 'png') self.awn.SetTaskIconByXid(self.xid(), self.temp_icon_path)
def on_cover_chosen(self, cover_chooser, track, cover_data): """ Updates the cover of the current album after user selection """ path = cover_chooser.path if path: album = self.model[path][0] pixbuf = pixbuf_from_data(cover_data) self.emit('cover-fetched', album, pixbuf) try: self.outstanding.remove(album) except ValueError: pass else: outstanding = len(self.outstanding) if outstanding > 0: progress_text = self.outstanding_text.format( outstanding=outstanding) else: progress_text = self.completed_text self.progress_bar.set_text(progress_text)
def fetch_cover(self): """ Searches for covers for the current track """ db_strings = COVER_MANAGER.find_covers(self.track) if db_strings: for db_string in db_strings: if self.stopper.is_set(): return coverdata = COVER_MANAGER.get_cover_data(db_string) # Pre-render everything for faster display later pixbuf = pixbuf_from_data(coverdata) if pixbuf: self.covers_model.append( [ (db_string, coverdata), pixbuf, pixbuf.scale_simple(50, 50, GdkPixbuf.InterpType.BILINEAR), ] ) self.emit('covers-fetched', db_strings)
def set_cover_from_track(self, track): """ Updates the cover image and triggers cross-fading """ cover_data = covers.MANAGER.get_cover(track, set_only=True) if cover_data is None: self.hide() return if not self.props.visible: self.show() size = settings.get_option('plugin/desktopcover/size', 200) upscale = settings.get_option('plugin/desktopcover/override_size', False) pixbuf = self.image.get_pixbuf() next_pixbuf = pixbuf_from_data(cover_data, size=(size, size), upscale=upscale) fading = settings.get_option('plugin/desktopcover/fading', False) if fading and pixbuf is not None and self._cross_fade_id is None: # Prescale to allow for proper crossfading width, height = next_pixbuf.get_width(), next_pixbuf.get_height() pixbuf = pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR) self.image.set_from_pixbuf(pixbuf) duration = settings.get_option('plugin/desktopcover/fading_duration', 50) self._cross_fade_id = GLib.timeout_add( int(duration), self.cross_fade, pixbuf, next_pixbuf, duration ) else: self.image.set_from_pixbuf(next_pixbuf)
def set_cover(self, *args, **kwargs): if self.exaile is None: return elif not hasattr(self.exaile, 'player'): log.debug("Player not loaded, ignoring set_cover call") return try: os.remove(self.temp_icon_path) except (TypeError, OSError): pass if player.PLAYER.current is None: log.debug("Player stopped, removing AWN cover") self.unset_cover() elif not self.cover_display: self.unset_cover() else: image_data = covers.MANAGER.get_cover( player.PLAYER.current, set_only=True, use_default=True ) pixbuf = pixbuf_from_data(image_data) descriptor, self.temp_icon_path = tempfile.mkstemp() pixbuf.save(self.temp_icon_path, 'png') self.awn.SetTaskIconByXid(self.xid(), self.temp_icon_path)
def set_cover_from_track(self, track): """ Updates the cover image and triggers cross-fading """ cover_data = covers.MANAGER.get_cover(track, set_only=True) if cover_data is None: self.hide() return if not self.props.visible: self.show() size = settings.get_option('plugin/desktopcover/size', 200) upscale = settings.get_option('plugin/desktopcover/override_size', False) pixbuf = self.image.get_pixbuf() next_pixbuf = pixbuf_from_data(cover_data, size=(size, size), upscale=upscale) fading = settings.get_option('plugin/desktopcover/fading', False) if fading and pixbuf is not None and self._cross_fade_id is None: # Prescale to allow for proper crossfading width, height = next_pixbuf.get_width(), next_pixbuf.get_height() pixbuf = pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR) self.image.set_from_pixbuf(pixbuf) duration = settings.get_option('plugin/desktopcover/fading_duration', 50) self._cross_fade_id = GLib.timeout_add( int(duration), self.cross_fade, pixbuf, next_pixbuf, duration ) else: self.image.set_from_pixbuf(next_pixbuf)
def prefetch(self, collection): """ Collects all albums and sets the list of outstanding items """ albums = set() for track in collection: if self.stopper.is_set(): return try: artist = track.get_tag_raw('artist')[0] album = track.get_tag_raw('album')[0] except TypeError: continue if not album or not artist: continue album = (artist, album) try: self.album_tracks[album].append(track) except KeyError: self.album_tracks[album] = [track] albums.add(album) albums = sorted(albums) outstanding = [] # Speed up the following loop get_cover = COVER_MANAGER.get_cover default_cover_pixbuf = self.default_cover_pixbuf cover_size = self.cover_size self.emit('prefetch-started') for i, album in enumerate(albums): if self.stopper.is_set(): return cover_data = get_cover(self.album_tracks[album][0], set_only=True) cover_pixbuf = pixbuf_from_data(cover_data) if cover_data else None try: thumbnail_pixbuf = cover_pixbuf.scale_simple( *cover_size, interp_type=GdkPixbuf.InterpType.BILINEAR) except AttributeError: # cover_pixbuf is None thumbnail_pixbuf = default_cover_pixbuf outstanding.append(album) label = '{0} - {1}'.format(*album) iter = self.model.append((album, thumbnail_pixbuf, label)) self.model_path_cache[album] = self.model.get_path(iter) self.emit('prefetch-progress', i + 1) self.outstanding = outstanding self.emit('prefetch-completed', len(self.outstanding))
def do_drag_data_get(self, context, selection, info, time): """ Fills the selection with the current cover """ if self.filename is None: self.filename = tempfile.mkstemp(prefix='exaile_cover_')[1] pixbuf = pixbuf_from_data(self.cover_data) save_pixbuf(pixbuf, self.filename, 'png') selection.set_uris([Gio.File.new_for_path(self.filename).get_uri()])
def __init__(self, parent, collection): """ Initializes the window """ GObject.GObject.__init__(self) # List of identifiers of albums without covers self.outstanding = [] # Map of album identifiers and their tracks self.album_tracks = {} self.outstanding_text = _('{outstanding} covers left to fetch') self.completed_text = _('All covers fetched') self.cover_size = (90, 90) self.default_cover_pixbuf = pixbuf_from_data( COVER_MANAGER.get_default_cover(), self.cover_size ) builder = Gtk.Builder() builder.add_from_file(xdg.get_data_path('ui', 'covermanager.ui')) builder.connect_signals(self) self.window = builder.get_object('window') self.window.set_transient_for(parent) self.message = dialogs.MessageBar( parent=builder.get_object('content_area'), buttons=Gtk.ButtonsType.CLOSE ) self.previews_box = builder.get_object('previews_box') self.model = builder.get_object('covers_model') # Map of album identifiers and model paths self.model_path_cache = {} self.menu = CoverMenu(self) self.menu.attach_to_widget(self.previews_box, lambda menu, widget: True) self.progress_bar = builder.get_object('progressbar') self.progress_bar.set_text(_('Collecting albums and covers...')) self.progress_bar.pulse_timeout = GLib.timeout_add( 100, self.on_progress_pulse_timeout ) self.close_button = builder.get_object('close_button') self.stop_button = builder.get_object('stop_button') self.stop_button.set_sensitive(False) self.fetch_button = builder.get_object('fetch_button') self.window.show_all() self.stopper = threading.Event() thread = threading.Thread( target=self.prefetch, name='CoverPrefetch', args=(collection,) ) thread.daemon = True thread.start()
def __get_icon(self, track, media_icon): # TODO: icons are too small, even with settings.resize_covers=False icon_name = None if media_icon and self.settings.use_media_icons: icon_name = media_icon elif self.settings.show_covers: cover_data = covers.MANAGER.get_cover(track, set_only=True, use_default=True) size = DEFAULT_ICON_SIZE if self.settings.resize_covers else None new_icon = pixbuf_from_data(cover_data, size) self.notification.set_image_from_pixbuf(new_icon) return icon_name
def set_blank(self): """ Sets the default cover to display """ self.drag_dest_unset() pixbuf = pixbuf_from_data(COVER_MANAGER.get_default_cover()) self.image.set_from_pixbuf(pixbuf) self.set_drag_source_enabled(False) self.cover_data = None self.emit('cover-found', None)
def __get_icon(self, track, media_icon): # TODO: icons are too small, even with settings.resize_covers=False icon_name = None if media_icon and self.settings.use_media_icons: icon_name = media_icon elif self.settings.show_covers: cover_data = covers.MANAGER.get_cover( track, set_only=True, use_default=True ) size = DEFAULT_ICON_SIZE if self.settings.resize_covers else None new_icon = pixbuf_from_data(cover_data, size) self.notification.set_image_from_pixbuf(new_icon) return icon_name
def show_cover(self): """ Shows the current cover """ if not self.cover_data: return pixbuf = pixbuf_from_data(self.cover_data) if pixbuf: savedir = Gio.File.new_for_uri(self.__track.get_loc_for_io()).get_parent() if savedir: savedir = savedir.get_path() window = CoverWindow(self.get_toplevel(), pixbuf, self.__track.get_tag_display('album'), savedir) window.show_all()
def on_cover_chosen(self, object, track, cover_data): """ Called when a cover is selected from the coverchooser """ if self.__track != track: return width = settings.get_option('gui/cover_width', 100) pixbuf = pixbuf_from_data(cover_data, (width, width)) self.image.set_from_pixbuf(pixbuf) self.set_drag_source_enabled(True) self.cover_data = cover_data self.emit('cover-found', pixbuf)
def get_drag_cover_icon(self, tracks): """ Get drag cover icon (stacked covers pixbuf) Asynchronous load covers for tracks taking at most 0.333 seconds :param tracks: iterable for tracks (xl.trax.Track) :return: GdkPixbuf.Pixbuf or None if none found """ cover_width = settings.get_option('gui/cover_width', 100) as_pixbuf = lambda data: pixbuf_from_data( data=data, size=(cover_width, cover_width)) get_cover_for_tracks = covers.MANAGER.get_cover_for_tracks db_string_list = [] cover_for_tracks = lambda tracks: get_cover_for_tracks( tracks, db_string_list) filtered_covers = filter(None, map(cover_for_tracks, tracks)) # Remove None cover tracks async_loader = common.AsyncLoader(map(as_pixbuf, filtered_covers)) async_loader.end(0.333) return self.__create_drag_cover_icon(async_loader.result, cover_width)
def show_cover(self): """ Shows the currently selected cover """ paths = self.previews_box.get_selected_items() if paths: path = paths[0] album = self.model[path][0] track = self.album_tracks[album][0] # Arbitrary track in album cover_data = COVER_MANAGER.get_cover(track, set_only=True) cover_pixbuf = pixbuf_from_data(cover_data) if cover_data else None # Do not bother showing the dialog if there is no cover if cover_pixbuf: savedir = Gio.File.new_for_uri(track.get_loc_for_io()).get_parent() if savedir: savedir = savedir.get_path() cover_window = CoverWindow(self.window, cover_pixbuf, album[1], savedir) cover_window.show_all()
def do_drag_data_received(self, context, x, y, selection, info, time): """ Sets the cover based on the dragged data """ if self.__track is not None: uri = selection.get_uris()[0] db_string = 'localfile:%s' % uri try: stream = Gio.File.new_for_uri(uri).read() except GLib.Error: return self.cover_data = stream.read() width = settings.get_option('gui/cover_width', 100) pixbuf = pixbuf_from_data(self.cover_data, (width, width)) if pixbuf is not None: self.image.set_from_pixbuf(pixbuf) COVER_MANAGER.set_cover(self.__track, db_string, self.cover_data)
def get_drag_cover_icon(self, tracks): """ Get drag cover icon (stacked covers pixbuf) Asynchronous load covers for tracks taking at most 0.333 seconds :param tracks: iterable for tracks (xl.trax.Track) :return: GdkPixbuf.Pixbuf or None if none found """ cover_width = settings.get_option('gui/cover_width', 100) as_pixbuf = lambda data: pixbuf_from_data( data=data, size=(cover_width, cover_width) ) get_cover_for_tracks = covers.MANAGER.get_cover_for_tracks db_string_list = [] cover_for_tracks = lambda tracks: get_cover_for_tracks(tracks, db_string_list) filtered_covers = ifilter( None, imap(cover_for_tracks, tracks) ) # Remove None cover tracks async_loader = common.AsyncLoader(imap(as_pixbuf, filtered_covers)) async_loader.end(0.333) return self.__create_drag_cover_icon(async_loader.result, cover_width)
def __fetch_metadata(self, *_args): # "gtk-menu-images" is deprecated and is being ignored on most # platforms. On GNOME/Wayland it can be enabled by editing # ~/.config/gtk-3.0/settings.ini and adding `gtk-menu-images=1`. use_covers = Gtk.Settings.get_default().props.gtk_menu_images try: item = trax.Track(self.__path) if not self.__title: self.__title = item.get_tag_display('title') if use_covers: image = covers.MANAGER.get_cover(item, set_only=True) if image: try: self.__cover_pixbuf = pixbuf_from_data(image, size=(16, 16)) except GLib.GError: LOGGER.warning('Could not load cover') else: self.__cover_pixbuf = None except Exception: LOGGER.exception("Cannot open %s", self.__path) return
def __fetch_metadata(self, *_args): # "gtk-menu-images" is deprecated and is being ignored on most # platforms. On GNOME/Wayland it can be enabled by editing # ~/.config/gtk-3.0/settings.ini and adding `gtk-menu-images=1`. use_covers = Gtk.Settings.get_default().props.gtk_menu_images try: item = trax.Track(self.__path) if not self.__title: self.__title = item.get_tag_display('title') if use_covers: image = covers.MANAGER.get_cover(item, set_only=True) if image: try: self.__cover_pixbuf = pixbuf_from_data(image, size=(16, 16)) except GLib.GError: LOGGER.warning('Could not load cover') else: self.__cover_pixbuf = None except Exception: LOGGER.exception("Cannot open %s", self.__path) return