[gnome-music/wip/mschraal/searchview-rework: 11/15] Make retrieval work
- From: Marinus Schraal <mschraal src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/mschraal/searchview-rework: 11/15] Make retrieval work
- Date: Sat, 3 Aug 2019 10:29:24 +0000 (UTC)
commit 96cc1085dfa44619f43d855834751b6298e73fcb
Author: Marinus Schraal <mschraal gnome org>
Date: Fri Aug 2 17:57:30 2019 +0200
Make retrieval work
data/ui/ArtistSearchTile.ui | 2 +-
gnomemusic/albumartcache.py | 102 ++++++++++++++++++-
gnomemusic/widgets/artistartstack.py | 178 +++++++++++++++++++++++++++++++++
gnomemusic/widgets/artistsearchtile.py | 8 +-
4 files changed, 285 insertions(+), 5 deletions(-)
---
diff --git a/data/ui/ArtistSearchTile.ui b/data/ui/ArtistSearchTile.ui
index 8eb4acb1..7deaae63 100644
--- a/data/ui/ArtistSearchTile.ui
+++ b/data/ui/ArtistSearchTile.ui
@@ -20,7 +20,7 @@
<property name="can_focus">False</property>
<signal name="button-release-event" handler="_on_artist_event" swapped="no"/>
<child>
- <object class="CoverStack" id="_cover_stack">
+ <object class="ArtistArtStack" id="_artistart_stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="vexpand">True</property>
diff --git a/gnomemusic/albumartcache.py b/gnomemusic/albumartcache.py
index 18e65532..b1c6a069 100644
--- a/gnomemusic/albumartcache.py
+++ b/gnomemusic/albumartcache.py
@@ -191,9 +191,11 @@ class ArtistArt(GObject.GObject):
self._artist, None, "artist")
if (not success
or not thumb_file.query_exists()):
- self._coreartist.props.cached_thumbnail_uri = thumb_file.get_path()
return False
+ print("setting cached uri")
+ self._coreartist.props.cached_thumbnail_uri = thumb_file.get_path()
+
return True
def _on_thumbnail_changed(self, coreartist, thumbnail):
@@ -270,6 +272,104 @@ class ArtistArt(GObject.GObject):
except GLib.Error as error:
logger.warning("Error: {}, {}".format(error.domain, error.message))
+
+class ArtistCache(GObject.GObject):
+ """Handles retrieval of MediaArt cache art
+
+ Uses signals to indicate success or failure.
+ """
+
+ __gtype_name__ = "ArtistCache"
+
+ __gsignals__ = {
+ 'miss': (GObject.SignalFlags.RUN_FIRST, None, ()),
+ 'hit': (GObject.SignalFlags.RUN_FIRST, None, (object, ))
+ }
+
+ def __repr__(self):
+ return "<ArtistCache>"
+
+ @log
+ def __init__(self):
+ super().__init__()
+
+ # FIXME
+ self._size = Art.Size.MEDIUM
+ self._scale = 1
+
+ # FIXME: async
+ self.cache_dir = os.path.join(GLib.get_user_cache_dir(), 'media-art')
+ if not os.path.exists(self.cache_dir):
+ try:
+ Gio.file_new_for_path(self.cache_dir).make_directory(None)
+ except GLib.Error as error:
+ logger.warning(
+ "Error: {}, {}".format(error.domain, error.message))
+ return
+
+ @log
+ def query(self, coreartist):
+ """Start the cache query
+
+ :param CoreSong coresong: The CoreSong object to search art for
+ """
+ print("query")
+ thumb_file = Gio.File.new_for_path(
+ coreartist.props.cached_thumbnail_uri)
+ if thumb_file:
+ thumb_file.read_async(
+ GLib.PRIORITY_LOW, None, self._open_stream, None)
+ return
+
+ self.emit('miss')
+
+ @log
+ def _open_stream(self, thumb_file, result, arguments):
+ try:
+ stream = thumb_file.read_finish(result)
+ except GLib.Error as error:
+ print("hier")
+ logger.warning("Error: {}, {}".format(error.domain, error.message))
+ self.emit('miss')
+ return
+
+ GdkPixbuf.Pixbuf.new_from_stream_async(
+ stream, None, self._pixbuf_loaded, None)
+
+ @log
+ def _pixbuf_loaded(self, stream, result, data):
+ try:
+ pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(result)
+ except GLib.Error as error:
+ print("hm")
+ logger.warning("Error: {}, {}".format(error.domain, error.message))
+ self.emit('miss')
+ return
+
+ stream.close_async(GLib.PRIORITY_LOW, None, self._close_stream, None)
+
+ surface = Gdk.cairo_surface_create_from_pixbuf(
+ pixbuf, self._scale, None)
+ surface = _make_icon_frame(surface, self._size, self._scale)
+
+ self.emit("hit", surface)
+
+ @log
+ def _close_stream(self, stream, result, data):
+ try:
+ stream.close_finish(result)
+ except GLib.Error as error:
+ logger.warning("Error: {}, {}".format(error.domain, error.message))
+
+ def _cache_hit(self, klass, pixbuf):
+ surface = Gdk.cairo_surface_create_from_pixbuf(
+ pixbuf, self._scale, None)
+ surface = _make_icon_frame(surface, self._size, self._scale)
+ self._surface = surface
+
+ self.emit('finished')
+
+
class Art(GObject.GObject):
"""Retrieves art for an album or song
diff --git a/gnomemusic/widgets/artistartstack.py b/gnomemusic/widgets/artistartstack.py
new file mode 100644
index 00000000..35599c13
--- /dev/null
+++ b/gnomemusic/widgets/artistartstack.py
@@ -0,0 +1,178 @@
+# Copyright 2019 The GNOME Music developers
+#
+# GNOME Music is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GNOME Music is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with GNOME Music; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# The GNOME Music authors hereby grant permission for non-GPL compatible
+# GStreamer plugins to be used and distributed together with GStreamer
+# and GNOME Music. This permission is above and beyond the permissions
+# granted by the GPL license by which GNOME Music is covered. If you
+# modify this code, you may extend this exception to your version of the
+# code, but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version.
+
+from gi.repository import GLib, GObject, Gtk
+
+from gnomemusic import log
+from gnomemusic.albumartcache import Art, ArtistCache, DefaultIcon
+from gnomemusic.coreartist import CoreArtist
+
+
+class ArtistArtStack(Gtk.Stack):
+ """Provides a smooth transition between image states
+
+ Uses a Gtk.Stack to provide an in-situ transition between an image
+ state. Either between the 'loading' state versus the 'loaded' state
+ or in between songs.
+ """
+
+ __gtype_name__ = "ArtistArtStack"
+
+ __gsignals__ = {
+ 'updated': (GObject.SignalFlags.RUN_FIRST, None, ())
+ }
+
+ _default_icon = DefaultIcon()
+
+ def __repr__(self):
+ return "ArtistArtStack"
+
+ @log
+ def __init__(self, size=Art.Size.MEDIUM):
+ """Initialize the CoverStack
+
+ :param Art.Size size: The size of the art used for the cover
+ """
+ super().__init__()
+
+ self._art = None
+ self._handler_id = None
+ self._size = None
+ self._timeout = None
+
+ self._loading_cover = Gtk.Image()
+ self._cover_a = Gtk.Image()
+ self._cover_b = Gtk.Image()
+
+ self.add_named(self._loading_cover, "loading")
+ self.add_named(self._cover_a, "A")
+ self.add_named(self._cover_b, "B")
+
+ self._active_child = "loading"
+
+ self.props.size = size
+ self.props.transition_type = Gtk.StackTransitionType.CROSSFADE
+ self.props.visible_child_name = "loading"
+
+ self.show_all()
+
+ @GObject.Property(type=object, flags=GObject.ParamFlags.READWRITE)
+ def size(self):
+ """Size of the cover
+
+ :returns: The size used
+ :rtype: Art.Size
+ """
+ return self._size
+
+ @size.setter
+ def size(self, value):
+ """Set the cover size
+
+ :param Art.Size value: The size to use for the cover
+ """
+ self._size = value
+
+ icon = self._default_icon.get(
+ DefaultIcon.Type.LOADING, self.props.size, self.props.scale_factor)
+ self._loading_cover.props.surface = icon
+
+ @GObject.Property(type=CoreArtist, default=None)
+ def coreartist(self):
+ return self._coreartist
+
+ @coreartist.setter
+ def coreartist(self, coreartist):
+ self._coreartist = coreartist
+
+ print("connecting")
+ self._coreartist.connect(
+ "notify::cached-thumbnail-uri", self._on_thumbnail_changed)
+
+ if self._coreartist.props.cached_thumbnail_uri is not None:
+ self._on_thumbnail_changed(self._coreartist, None)
+
+ def _on_thumbnail_changed(self, coreartist, uri):
+ print("thumbnail changed")
+ cache = ArtistCache()
+ cache.connect("hit", self._on_cache_hit)
+
+ cache.query(coreartist)
+
+ def _on_cache_hit(self, cache, surface):
+ print("surface", surface)
+ if self._active_child == "B":
+ self._cover_a.props.surface = surface
+ self.props.visible_child_name = "A"
+ else:
+ self._cover_b.props.surface = surface
+ self.props.visible_child_name = "B"
+
+ @log
+ def update(self, coresong):
+ """Update the stack with the given CoreSong
+
+ Update the stack with the art retrieved from the given Coresong.
+ :param CoreSong coresong: The CoreSong object
+ """
+ if self._handler_id and self._art:
+ # Remove a possible dangling 'finished' callback if update
+ # is called again, but it is still looking for the previous
+ # art.
+ self._art.disconnect(self._handler_id)
+ # Set the loading state only after a delay to make between
+ # song transitions smooth if loading time is negligible.
+ self._timeout = GLib.timeout_add(100, self._set_loading_child)
+
+ self._active_child = self.props.visible_child_name
+
+ self._art = Art(self.props.size, coresong, self.props.scale_factor)
+ self._handler_id = self._art.connect('finished', self._art_retrieved)
+ self._art.lookup()
+
+ @log
+ def _set_loading_child(self):
+ self.props.visible_child_name = "loading"
+ self._active_child = self.props.visible_child_name
+ self._timeout = None
+
+ return GLib.SOURCE_REMOVE
+
+ @log
+ def _art_retrieved(self, klass):
+ if self._timeout:
+ GLib.source_remove(self._timeout)
+ self._timeout = None
+
+ if self._active_child == "B":
+ self._cover_a.props.surface = klass.surface
+ self.props.visible_child_name = "A"
+ else:
+ self._cover_b.props.surface = klass.surface
+ self.props.visible_child_name = "B"
+
+ self._active_child = self.props.visible_child_name
+ self._art = None
+
+ self.emit('updated')
diff --git a/gnomemusic/widgets/artistsearchtile.py b/gnomemusic/widgets/artistsearchtile.py
index a3a9d341..c2ca153d 100644
--- a/gnomemusic/widgets/artistsearchtile.py
+++ b/gnomemusic/widgets/artistsearchtile.py
@@ -25,6 +25,7 @@
from gi.repository import Gdk, GObject, Gtk
from gnomemusic.albumartcache import Art
+from gnomemusic.widgets.artistartstack import ArtistArtStack
from gnomemusic.widgets.twolinetip import TwoLineTip
@@ -38,7 +39,7 @@ class ArtistSearchTile(Gtk.FlowBoxChild):
__gtype_name__ = "ArtistSearchTile"
_check = Gtk.Template.Child()
- _cover_stack = Gtk.Template.Child()
+ _artistart_stack = Gtk.Template.Child()
_artist_label = Gtk.Template.Child()
_events = Gtk.Template.Child()
@@ -59,6 +60,9 @@ class ArtistSearchTile(Gtk.FlowBoxChild):
self._coreartist = coreartist
+ self._artistart_stack.props.size = Art.Size.MEDIUM
+ self._artistart_stack.props.coreartist = self._coreartist
+
self._tooltip = TwoLineTip()
artist = self._coreartist.props.artist
@@ -87,8 +91,6 @@ class ArtistSearchTile(Gtk.FlowBoxChild):
self._events.add_events(Gdk.EventMask.TOUCH_MASK)
- self._cover_stack.props.size = Art.Size.MEDIUM
-
self.show()
def _on_thumbnail_changed(self, klass, data):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]