[gnome-music/wip/mschraal/artrework: 13/15] albumartcache: Rework art retrieval



commit 9372aca2d2e7bfdf40121381f3b3eecb8a52537d
Author: Marinus Schraal <mschraal gnome org>
Date:   Fri Jan 5 13:54:49 2018 +0100

    albumartcache: Rework art retrieval
    
    The basic idea is to pass the image to the art class with the media
    info, so it can update the contents at will.
    
    This works fine for GtkImage, but treeviews require an alternate
    approach with a signal because it only accepts GdkPixbuf and
    cellrenderers do not update by themselves.
    
    cleans art logic from views/widgets
    
    FIXME: does trigger too many open files crashes on large sets

 gnomemusic/albumartcache.py             | 61 +++++++++++++++++++++++++++++++++
 gnomemusic/player.py                    | 16 +++------
 gnomemusic/views/albumsview.py          | 14 ++------
 gnomemusic/views/baseview.py            | 24 +++++++------
 gnomemusic/views/searchview.py          | 36 ++++++++-----------
 gnomemusic/widgets/albumwidget.py       | 22 ++----------
 gnomemusic/widgets/artistalbumwidget.py | 19 ++--------
 7 files changed, 99 insertions(+), 93 deletions(-)
---
diff --git a/gnomemusic/albumartcache.py b/gnomemusic/albumartcache.py
index f9954ca..2758b20 100644
--- a/gnomemusic/albumartcache.py
+++ b/gnomemusic/albumartcache.py
@@ -218,6 +218,67 @@ class DefaultIcon(GObject.GObject):
 
         return self._cache[(icon_type, art_size)]
 
+class Art(GObject.GObject):
+
+    __gsignals__ = {
+        'finished': (GObject.SignalFlags.RUN_FIRST, None, ())
+    }
+
+    @log
+    def __init__(self, image, size, media):
+        super().__init__()
+
+        self._iter = None
+
+        # FIXME: A pixbuf instead of an image means this art is
+        # requested by a treeview.
+        if isinstance(image, GdkPixbuf.Pixbuf):
+            self._pixbuf = image
+            self._image = Gtk.Image()
+        else:
+            self._image = image
+
+        self._size = size
+        self._media = media
+
+        self._image.set_property("width-request", size.width)
+        self._image.set_property("height-request", size.height)
+
+        scale = self._image.get_scale_factor()
+
+        self._surface = DefaultIcon(scale).get(DefaultIcon.Type.loading, size)
+
+        self._image.set_from_surface(self._surface)
+
+        cache = AlbumArtCache(scale)
+
+        iter_ = None
+        cache.lookup(self._media, self._size, self._callback, iter_)
+
+    @GObject.Property
+    @log
+    def pixbuf(self):
+        return Gdk.pixbuf_get_from_surface(
+            self._surface, 0, 0, self._surface.get_width(),
+            self._surface.get_height())
+
+    @GObject.Property(type=Gtk.TreeIter)
+    @log
+    def iter(self):
+        return self._iter
+
+    @iter.setter
+    @log
+    def iter(self, iter_):
+        self._iter = iter_
+
+    @log
+    def _callback(self, surface, __):
+        self._surface = surface
+        self._image.set_from_surface(self._surface)
+
+        self.emit('finished')
+
 
 class AlbumArtCache(GObject.GObject):
     """Album art retrieval class
diff --git a/gnomemusic/player.py b/gnomemusic/player.py
index d8798d2..c755850 100644
--- a/gnomemusic/player.py
+++ b/gnomemusic/player.py
@@ -46,7 +46,7 @@ from gi.repository import Gtk, Gdk, GLib, Gio, GObject, Gst, GstAudio, GstPbutil
 from gettext import gettext as _, ngettext
 
 from gnomemusic import log
-from gnomemusic.albumartcache import AlbumArtCache, DefaultIcon, ArtSize
+from gnomemusic.albumartcache import Art, ArtSize
 from gnomemusic.grilo import grilo
 from gnomemusic.playlists import Playlists
 from gnomemusic.scrobbler import LastFmScrobbler
@@ -109,11 +109,6 @@ class Player(GObject.GObject):
         self.playlistField = None
         self.currentTrack = None
         self.currentTrackUri = None
-        scale = parent_window.get_scale_factor()
-        self.cache = AlbumArtCache(scale)
-        self._loading_icon_surface = DefaultIcon(scale).get(
-            DefaultIcon.Type.loading,
-            ArtSize.XSMALL)
         self._missingPluginMessages = []
 
         Gst.init(None)
@@ -584,8 +579,7 @@ class Player(GObject.GObject):
         artist = utils.get_artist_name(media)
         self.artistLabel.set_label(artist)
 
-        self.coverImg.set_from_surface(self._loading_icon_surface)
-        self.cache.lookup(media, ArtSize.XSMALL, self._on_cache_lookup, None)
+        Art(self._image, ArtSize.XSMALL, media)
 
         title = utils.get_media_title(media)
         self.titleLabel.set_label(title)
@@ -639,7 +633,7 @@ class Player(GObject.GObject):
 
     @log
     def _on_cache_lookup(self, surface, data=None):
-        self.coverImg.set_from_surface(surface)
+        # FIXME: Need this for mpris
         self.emit('thumbnail-updated')
 
     @log
@@ -780,9 +774,7 @@ class Player(GObject.GObject):
         self.songTotalTimeLabel = self._ui.get_object('duration')
         self.titleLabel = self._ui.get_object('title')
         self.artistLabel = self._ui.get_object('artist')
-        self.coverImg = self._ui.get_object('cover')
-        self.coverImg.set_property("width-request", ArtSize.XSMALL.width)
-        self.coverImg.set_property("height-request", ArtSize.XSMALL.height)
+        self._image = self._ui.get_object('cover')
 
         self.duration = self._ui.get_object('duration')
         self.repeatBtnImage = self._ui.get_object('playlistRepeat')
diff --git a/gnomemusic/views/albumsview.py b/gnomemusic/views/albumsview.py
index bdd83a7..b20b316 100644
--- a/gnomemusic/views/albumsview.py
+++ b/gnomemusic/views/albumsview.py
@@ -26,7 +26,7 @@ from gettext import gettext as _
 from gi.repository import GLib, GObject, Gtk, Gdk
 
 from gnomemusic import log
-from gnomemusic.albumartcache import ArtSize
+from gnomemusic.albumartcache import Art, ArtSize
 from gnomemusic.grilo import grilo
 from gnomemusic.toolbar import ToolbarState
 from gnomemusic.views.baseview import BaseView
@@ -166,13 +166,6 @@ class AlbumsView(BaseView):
         child.title.set_label(title)
         child.subtitle.set_label(artist)
 
-        child.image.set_from_surface(self._loading_icon_surface)
-        # In the case of off-sized icons (eg. provided in the soundfile)
-        # keep the size request equal to all other icons to get proper
-        # alignment with GtkFlowBox.
-        child.image.set_property("width-request", ArtSize.MEDIUM.width)
-        child.image.set_property("height-request", ArtSize.MEDIUM.height)
-
         child.events.add_events(Gdk.EventMask.TOUCH_MASK)
 
         child.events.connect('button-release-event',
@@ -189,7 +182,7 @@ class AlbumsView(BaseView):
         child.add(builder.get_object('main_box'))
         child.show()
 
-        self._cache.lookup(item, ArtSize.MEDIUM, self._on_lookup_ready, child)
+        Art(child.image, ArtSize.MEDIUM, item)
 
         return child
 
@@ -200,9 +193,6 @@ class AlbumsView(BaseView):
             if self.selection_mode:
                 child.check.set_active(True)
 
-    def _on_lookup_ready(self, icon, child):
-        child.image.set_from_surface(icon)
-
     @log
     def _on_child_toggled(self, check, pspec, child):
         if (check.get_active()
diff --git a/gnomemusic/views/baseview.py b/gnomemusic/views/baseview.py
index 7f5489c..1a6583b 100644
--- a/gnomemusic/views/baseview.py
+++ b/gnomemusic/views/baseview.py
@@ -26,7 +26,7 @@ from gettext import gettext as _, ngettext
 from gi.repository import Gd, Gdk, GdkPixbuf, GObject, Gtk
 
 from gnomemusic import log
-from gnomemusic.albumartcache import AlbumArtCache, DefaultIcon, ArtSize
+from gnomemusic.albumartcache import Art, ArtSize
 from gnomemusic.grilo import grilo
 from gnomemusic.widgets.starhandlerwidget import StarHandlerWidget
 import gnomemusic.utils as utils
@@ -107,11 +107,6 @@ class BaseView(Gtk.Stack):
         self.show_all()
         self._view.hide()
 
-        scale = self.get_scale_factor()
-        self._cache = AlbumArtCache(scale)
-        self._loading_icon_surface = DefaultIcon(scale).get(
-            DefaultIcon.Type.loading, ArtSize.MEDIUM)
-
         self._init = False
         grilo.connect('ready', self._on_grilo_ready)
         self._header_bar.connect('selection-mode-changed',
@@ -213,8 +208,13 @@ class BaseView(Gtk.Stack):
     def populate(self):
         pass
 
+    @log
+    def _retrieval_finished(self, klass):
+        self.model[klass.iter][4] = klass.pixbuf
+
     @log
     def _add_item(self, source, param, item, remaining=0, data=None):
+        print("add item")
         if not item:
             if remaining == 0:
                 self._view.set_model(self.model)
@@ -226,22 +226,24 @@ class BaseView(Gtk.Stack):
         title = utils.get_media_title(item)
 
         itr = self.model.append(None)
-        loading_icon = Gdk.pixbuf_get_from_surface(
-            self._loadin_icon_surface, 0, 0,
-            self._loading_icon_surface.get_width(),
-            self._loading_icon_surface.get_height())
+
+        pixbuf = GdkPixbuf.Pixbuf()
+        art = Art(pixbuf, ArtSize.MEDIUM, item)
 
         self.model[itr][0, 1, 2, 3, 4, 5, 7, 9] = [
             str(item.get_id()),
             '',
             title,
             artist,
-            loading_icon,
+            art,
             item,
             0,
             False
         ]
 
+        art.iter = itr
+        art.connect('finished', self._retrieval_finished)
+
     @log
     def _on_lookup_ready(self, surface, itr):
         if surface:
diff --git a/gnomemusic/views/searchview.py b/gnomemusic/views/searchview.py
index 3d53baf..1a687e7 100644
--- a/gnomemusic/views/searchview.py
+++ b/gnomemusic/views/searchview.py
@@ -23,9 +23,9 @@
 # delete this exception statement from your version.
 
 from gettext import gettext as _
-from gi.repository import Gd, Gdk, GdkPixbuf, GObject, Grl, Gtk, Pango
+from gi.repository import Gd, GdkPixbuf, GObject, Grl, Gtk, Pango
 
-from gnomemusic.albumartcache import DefaultIcon, ArtSize
+from gnomemusic.albumartcache import Art, ArtSize
 from gnomemusic.grilo import grilo
 from gnomemusic import log
 from gnomemusic.player import DiscoveryStatus
@@ -54,13 +54,6 @@ class SearchView(BaseView):
     def __init__(self, window, player):
         super().__init__('search', None, window, Gd.MainViewType.LIST)
 
-        scale = self.get_scale_factor()
-        loading_icon_surface = DefaultIcon(scale).get(
-            DefaultIcon.Type.loading, ArtSize.SMALL)
-        self._loading_icon = Gdk.pixbuf_get_from_surface(
-            loading_icon_surface, 0, 0, loading_icon_surface.get_width(),
-            loading_icon_surface.get_height())
-
         self._add_list_renderers()
         self.player = player
         self._head_iters = [None, None, None, None]
@@ -196,6 +189,10 @@ class SearchView(BaseView):
         self._albums[key].songs.append(item)
         self._add_item(source, None, item, 0, [self.model, 'song'])
 
+    @log
+    def _retrieval_finished(self, klass):
+        self.model[klass.iter][4] = klass.pixbuf
+
     @log
     def _add_item(self, source, param, item, remaining=0, data=None):
         if data is None:
@@ -246,13 +243,13 @@ class SearchView(BaseView):
         # scaled by GdkPixbuf, so it results in a * scale factor sized
         # icon for the search view.
         _iter = None
+        image = GdkPixbuf.Pixbuf()
+        art = Art(image, ArtSize.SMALL, item)
         if category == 'album':
             _iter = self.model.insert_with_values(
                 self._head_iters[group], -1, [0, 2, 3, 4, 5, 9, 11, 13],
-                [str(item.get_id()), title, artist, self._loading_icon, item,
-                 2, category, composer])
-            self._cache.lookup(
-                item, ArtSize.SMALL, self._on_lookup_ready, _iter)
+                [str(item.get_id()), title, artist, art.pixbuf, item, 2,
+                 category, composer])
         elif category == 'song':
             # FIXME: source specific hack
             if source.get_id() != 'grl-tracker-source':
@@ -261,24 +258,21 @@ class SearchView(BaseView):
                 fav = item.get_favourite()
             _iter = self.model.insert_with_values(
                 self._head_iters[group], -1, [0, 2, 3, 4, 5, 9, 11, 13],
-                [str(item.get_id()), title, artist, self._loading_icon, item,
-                 fav, category, composer])
-            self._cache.lookup(
-                item, ArtSize.SMALL, self._on_lookup_ready, _iter)
+                [str(item.get_id()), title, artist, art.pixbuf, item, fav,
+                 category, composer])
         else:
             if not artist.casefold() in self._artists:
                 _iter = self.model.insert_with_values(
                     self._head_iters[group], -1, [0, 2, 4, 5, 9, 11, 13],
-                    [str(item.get_id()), artist, self._loading_icon, item, 2,
+                    [str(item.get_id()), artist, art.pixbuf, item, 2,
                      category, composer])
-                self._cache.lookup(
-                    item, ArtSize.SMALL, self._on_lookup_ready, _iter)
                 self._artists[artist.casefold()] = {
                     'iter': _iter,
                     'albums': []
                 }
-
             self._artists[artist.casefold()]['albums'].append(item)
+        art.iter = _iter
+        art.connect('finished', self._retrieval_finished)
 
         if self.model.iter_n_children(self._head_iters[group]) == 1:
             path = self.model.get_path(self._head_iters[group])
diff --git a/gnomemusic/widgets/albumwidget.py b/gnomemusic/widgets/albumwidget.py
index 5f6841c..0167867 100644
--- a/gnomemusic/widgets/albumwidget.py
+++ b/gnomemusic/widgets/albumwidget.py
@@ -26,7 +26,7 @@ from gettext import gettext as _, ngettext
 from gi.repository import GdkPixbuf, GLib, GObject, Gtk
 
 from gnomemusic import log
-from gnomemusic.albumartcache import AlbumArtCache, DefaultIcon, ArtSize
+from gnomemusic.albumartcache import Art, ArtSize
 from gnomemusic.grilo import grilo
 from gnomemusic.widgets.disclistboxwidget import DiscBox, DiscListBox
 import gnomemusic.utils as utils
@@ -55,12 +55,6 @@ class AlbumWidget(Gtk.EventBox):
 
         self._songs = []
 
-        scale = self.get_scale_factor()
-        self._cache = AlbumArtCache(scale)
-        self._loading_icon_surface = DefaultIcon(scale).get(
-            DefaultIcon.Type.loading,
-            ArtSize.LARGE)
-
         self._player = player
         self._iter_to_clean = None
 
@@ -143,10 +137,8 @@ class AlbumWidget(Gtk.EventBox):
         self.selection_toolbar = selection_toolbar
         self._header_bar = header_bar
         self._album = album
-        self._builder.get_object('cover').set_from_surface(
-            self._loading_icon_surface)
-        self._cache.lookup(item, ArtSize.LARGE, self._on_lookup, None)
         self._duration = 0
+        Art(self._builder.get_object('cover'), ArtSize.LARGE, item)
 
         GLib.idle_add(grilo.populate_album_songs, item, self.add_item)
         header_bar._select_button.connect(
@@ -289,16 +281,6 @@ class AlbumWidget(Gtk.EventBox):
 
             self.show_all()
 
-    @log
-    def _on_lookup(self, surface, data=None):
-        """Albumart retrieved callback.
-
-        :param surface: The Cairo surface retrieved
-        :param path: The filesystem location the pixbuf
-        :param data: User data
-        """
-        self._builder.get_object('cover').set_from_surface(surface)
-
     @log
     def _update_model(self, player, playlist, current_iter):
         """Player changed callback.
diff --git a/gnomemusic/widgets/artistalbumwidget.py b/gnomemusic/widgets/artistalbumwidget.py
index 71bc01b..9289683 100644
--- a/gnomemusic/widgets/artistalbumwidget.py
+++ b/gnomemusic/widgets/artistalbumwidget.py
@@ -25,7 +25,7 @@
 from gi.repository import GLib, GObject, Gtk
 
 from gnomemusic import log
-from gnomemusic.albumartcache import AlbumArtCache, DefaultIcon, ArtSize
+from gnomemusic.albumartcache import Art, ArtSize
 from gnomemusic.grilo import grilo
 from gnomemusic.widgets.disclistboxwidget import DiscBox
 import gnomemusic.utils as utils
@@ -48,11 +48,6 @@ class ArtistAlbumWidget(Gtk.Box):
 
         self._size_group = size_group
         self._cover_size_group = cover_size_group
-        scale = self.get_scale_factor()
-        self._cache = AlbumArtCache(scale)
-        self._loading_icon_surface = DefaultIcon(scale).get(
-            DefaultIcon.Type.loading,
-            ArtSize.MEDIUM)
 
         self._media = media
         self._player = player
@@ -72,7 +67,7 @@ class ArtistAlbumWidget(Gtk.Box):
         ui.add_from_resource('/org/gnome/Music/ArtistAlbumWidget.ui')
 
         self.cover = ui.get_object('cover')
-        self.cover.set_from_surface(self._loading_icon_surface)
+        Art(self.cover, ArtSize.MEDIUM, self._media)
 
         self._disc_listbox = ui.get_object('disclistbox')
         self._disc_listbox.set_selection_mode_allowed(
@@ -93,7 +88,6 @@ class ArtistAlbumWidget(Gtk.Box):
 
         self.pack_start(ui.get_object('ArtistAlbumWidget'), True, True, 0)
 
-        GLib.idle_add(self._update_album_art)
         grilo.populate_album_songs(self._media, self._add_item)
 
     def create_disc_box(self, disc_nr, disc_songs):
@@ -159,15 +153,6 @@ class ArtistAlbumWidget(Gtk.Box):
         if remaining == 0:
             self.emit("songs-loaded")
 
-    @log
-    def _update_album_art(self):
-        self._cache.lookup(self._media, ArtSize.MEDIUM, self._get_album_cover,
-                           None)
-
-    @log
-    def _get_album_cover(self, surface, data=None):
-        self.cover.set_from_surface(surface)
-
     @log
     def _song_activated(self, widget, song_widget):
         if (not song_widget.can_be_played


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