[gnome-music/wip/mschraal/core-thumb-property: 2/18] artistart: Split out retrieval and cache storage
- From: Marinus Schraal <mschraal src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/mschraal/core-thumb-property: 2/18] artistart: Split out retrieval and cache storage
- Date: Mon, 6 Jul 2020 09:33:25 +0000 (UTC)
commit 225995b4214abaed2cece4522232a9999927b2e3
Author: Marinus Schraal <mschraal gnome org>
Date: Sun Jun 14 23:37:34 2020 +0200
artistart: Split out retrieval and cache storage
Add StoreArtistArt with the bits and pieces of cache retrieval and
storage from ArtistArt.
gnomemusic/artcache.py | 201 +++++++++++++++++++++++++
gnomemusic/artistart.py | 208 +-------------------------
gnomemusic/coreartist.py | 4 +-
gnomemusic/grilowrappers/grltrackerwrapper.py | 3 +-
gnomemusic/storeartistart.py | 14 +-
gnomemusic/widgets/artistartstack.py | 4 +-
6 files changed, 218 insertions(+), 216 deletions(-)
---
diff --git a/gnomemusic/artcache.py b/gnomemusic/artcache.py
new file mode 100644
index 00000000..a9fcd8b0
--- /dev/null
+++ b/gnomemusic/artcache.py
@@ -0,0 +1,201 @@
+# Copyright 2020 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 enum import Enum
+from math import pi
+
+import cairo
+from gi.repository import Gdk, GdkPixbuf, Gio, Gtk, GLib, GObject
+
+from gnomemusic.musiclogger import MusicLogger
+
+
+def _make_icon_frame(icon_surface, art_size=None, scale=1, default_icon=False):
+ icon_w = icon_surface.get_width()
+ icon_h = icon_surface.get_height()
+ ratio = icon_h / icon_w
+
+ # Scale down the image according to the biggest axis
+ if ratio > 1:
+ w = int(art_size.width / ratio)
+ h = art_size.height
+ else:
+ w = art_size.width
+ h = int(art_size.height * ratio)
+
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w * scale, h * scale)
+ surface.set_device_scale(scale, scale)
+ ctx = cairo.Context(surface)
+
+ matrix = cairo.Matrix()
+
+ line_width = 0.6
+ ctx.new_sub_path()
+ ctx.arc(w / 2, h / 2, (w / 2) - line_width, 0, 2 * pi)
+ ctx.set_source_rgba(0, 0, 0, 0.7)
+ ctx.set_line_width(line_width)
+ ctx.stroke_preserve()
+
+ if default_icon:
+ ctx.set_source_rgb(1, 1, 1)
+ ctx.fill()
+ ctx.set_source_rgba(0, 0, 0, 0.3)
+ ctx.mask_surface(icon_surface, w / 3, h / 3)
+ ctx.fill()
+ else:
+ matrix.scale(icon_w / (w * scale), icon_h / (h * scale))
+ ctx.set_source_surface(icon_surface, 0, 0)
+
+ pattern = ctx.get_source()
+ pattern.set_matrix(matrix)
+ ctx.fill()
+
+ ctx.arc(w / 2, h / 2, w / 2, 0, 2 * pi)
+ ctx.clip()
+
+ return surface
+
+
+class DefaultIcon(GObject.GObject):
+ """Provides the symbolic fallback and loading icons."""
+
+ class Type(Enum):
+ LOADING = "content-loading-symbolic"
+ ARTIST = "avatar-default-symbolic"
+
+ _cache = {}
+ _default_theme = Gtk.IconTheme.get_default()
+
+ def __init__(self):
+ super().__init__()
+
+ def _make_default_icon(self, icon_type, art_size, scale):
+ icon_info = self._default_theme.lookup_icon_for_scale(
+ icon_type.value, art_size.width / 3, scale, 0)
+ icon = icon_info.load_surface()
+
+ icon_surface = _make_icon_frame(icon, art_size, scale, True)
+
+ return icon_surface
+
+ def get(self, icon_type, art_size, scale=1):
+ """Returns the requested symbolic icon
+
+ Returns a cairo surface of the requested symbolic icon in the
+ given size.
+
+ :param enum icon_type: The DefaultIcon.Type of the icon
+ :param enum art_size: The Art.Size requested
+
+ :return: The symbolic icon
+ :rtype: cairo.Surface
+ """
+ if (icon_type, art_size, scale) not in self._cache.keys():
+ new_icon = self._make_default_icon(icon_type, art_size, scale)
+ self._cache[(icon_type, art_size, scale)] = new_icon
+
+ return self._cache[(icon_type, art_size, scale)]
+
+
+class ArtCache(GObject.GObject):
+ """Handles retrieval of MediaArt cache art
+
+ Uses signals to indicate success or failure and always returns a
+ Cairo.Surface.
+ """
+
+ __gtype_name__ = "ArtCache"
+
+ __gsignals__ = {
+ "result": (GObject.SignalFlags.RUN_FIRST, None, (object, ))
+ }
+
+ _log = MusicLogger()
+
+ def __init__(self, size, scale):
+ super().__init__()
+
+ self._size = size
+ self._scale = scale
+
+ self._loading_icon = DefaultIcon().get(
+ DefaultIcon.Type.LOADING, self._size, self._scale)
+ self._default_icon = DefaultIcon().get(
+ DefaultIcon.Type.ARTIST, self._size, self._scale)
+
+ def query(self, coreartist):
+ """Start the cache query
+
+ :param CoreArtist coreartist: The object to search art for
+ """
+ thumbnail_uri = coreartist.props.thumbnail
+ if thumbnail_uri == "loading":
+ self.emit("result", self._loading_icon)
+ return
+ elif thumbnail_uri == "generic":
+ self.emit("result", self._default_icon)
+ return
+
+ thumb_file = Gio.File.new_for_uri(thumbnail_uri)
+ if thumb_file:
+ thumb_file.read_async(
+ GLib.PRIORITY_LOW, None, self._open_stream, None)
+ return
+
+ self.emit("result", self._default_icon)
+
+ def _open_stream(self, thumb_file, result, arguments):
+ try:
+ stream = thumb_file.read_finish(result)
+ except GLib.Error as error:
+ self._log.warning(
+ "Error: {}, {}".format(error.domain, error.message))
+ self.emit("result", self._default_icon)
+ return
+
+ GdkPixbuf.Pixbuf.new_from_stream_async(
+ stream, None, self._pixbuf_loaded, None)
+
+ def _pixbuf_loaded(self, stream, result, data):
+ try:
+ pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(result)
+ except GLib.Error as error:
+ self._log.warning(
+ "Error: {}, {}".format(error.domain, error.message))
+ self.emit("result", self._default_icon)
+ 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("result", surface)
+
+ def _close_stream(self, stream, result, data):
+ try:
+ stream.close_finish(result)
+ except GLib.Error as error:
+ self._log.warning(
+ "Error: {}, {}".format(error.domain, error.message))
diff --git a/gnomemusic/artistart.py b/gnomemusic/artistart.py
index 7edfae42..9cb0e3c6 100644
--- a/gnomemusic/artistart.py
+++ b/gnomemusic/artistart.py
@@ -22,112 +22,17 @@
# 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 enum import Enum
-from math import pi
-
-import cairo
import gi
gi.require_version("MediaArt", "2.0")
-gi.require_version("Soup", "2.4")
-from gi.repository import (Gdk, GdkPixbuf, Gio, GLib, GObject, Gtk, MediaArt,
- Soup)
-
-from gnomemusic.musiclogger import MusicLogger
-
-
-def _make_icon_frame(icon_surface, art_size=None, scale=1, default_icon=False):
- icon_w = icon_surface.get_width()
- icon_h = icon_surface.get_height()
- ratio = icon_h / icon_w
-
- # Scale down the image according to the biggest axis
- if ratio > 1:
- w = int(art_size.width / ratio)
- h = art_size.height
- else:
- w = art_size.width
- h = int(art_size.height * ratio)
-
- surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w * scale, h * scale)
- surface.set_device_scale(scale, scale)
- ctx = cairo.Context(surface)
-
- matrix = cairo.Matrix()
-
- line_width = 0.6
- ctx.new_sub_path()
- ctx.arc(w / 2, h / 2, (w / 2) - line_width, 0, 2 * pi)
- ctx.set_source_rgba(0, 0, 0, 0.7)
- ctx.set_line_width(line_width)
- ctx.stroke_preserve()
-
- if default_icon:
- ctx.set_source_rgb(1, 1, 1)
- ctx.fill()
- ctx.set_source_rgba(0, 0, 0, 0.3)
- ctx.mask_surface(icon_surface, w / 3, h / 3)
- ctx.fill()
- else:
- matrix.scale(icon_w / (w * scale), icon_h / (h * scale))
- ctx.set_source_surface(icon_surface, 0, 0)
-
- pattern = ctx.get_source()
- pattern.set_matrix(matrix)
- ctx.fill()
-
- ctx.arc(w / 2, h / 2, w / 2, 0, 2 * pi)
- ctx.clip()
-
- return surface
-
-
-class DefaultIcon(GObject.GObject):
- """Provides the symbolic fallback and loading icons."""
-
- class Type(Enum):
- LOADING = "content-loading-symbolic"
- ARTIST = "avatar-default-symbolic"
-
- _cache = {}
- _default_theme = Gtk.IconTheme.get_default()
-
- def __init__(self):
- super().__init__()
-
- def _make_default_icon(self, icon_type, art_size, scale):
- icon_info = self._default_theme.lookup_icon_for_scale(
- icon_type.value, art_size.width / 3, scale, 0)
- icon = icon_info.load_surface()
-
- icon_surface = _make_icon_frame(icon, art_size, scale, True)
-
- return icon_surface
-
- def get(self, icon_type, art_size, scale=1):
- """Returns the requested symbolic icon
-
- Returns a cairo surface of the requested symbolic icon in the
- given size.
-
- :param enum icon_type: The DefaultIcon.Type of the icon
- :param enum art_size: The Art.Size requested
-
- :return: The symbolic icon
- :rtype: cairo.Surface
- """
- if (icon_type, art_size, scale) not in self._cache.keys():
- new_icon = self._make_default_icon(icon_type, art_size, scale)
- self._cache[(icon_type, art_size, scale)] = new_icon
-
- return self._cache[(icon_type, art_size, scale)]
+from gi.repository import GObject, MediaArt
class ArtistArt(GObject.GObject):
-
- _log = MusicLogger()
+ """Artist art retrieval object
+ """
def __init__(self, application, coreartist):
- """Initialize the ArtistArt.
+ """Initialize.
:param Application application: The application object
:param CoreArtist coreartist: The coreartist to use
@@ -136,7 +41,6 @@ class ArtistArt(GObject.GObject):
self._coreartist = coreartist
self._artist = self._coreartist.props.artist
- self._soup_session = Soup.Session.new()
if self._in_cache():
return
@@ -151,108 +55,6 @@ class ArtistArt(GObject.GObject):
self._coreartist.props.thumbnail = "loading"
return False
- self._coreartist.props.thumbnail = thumb_file.get_path()
+ self._coreartist.props.thumbnail = thumb_file.get_uri()
return True
-
-
-class ArtistCache(GObject.GObject):
- """Handles retrieval of MediaArt cache art
-
- Uses signals to indicate success or failure.
- """
-
- __gtype_name__ = "ArtistCache"
-
- __gsignals__ = {
- "result": (GObject.SignalFlags.RUN_FIRST, None, (object, ))
- }
-
- _log = MusicLogger()
-
- def __init__(self, size, scale):
- super().__init__()
-
- self._size = size
- self._scale = scale
-
- self._loading_icon = DefaultIcon().get(
- DefaultIcon.Type.LOADING, self._size, self._scale)
- self._default_icon = DefaultIcon().get(
- DefaultIcon.Type.ARTIST, self._size, self._scale)
-
- cache_dir = GLib.build_filenamev(
- [GLib.get_user_cache_dir(), "media-art"])
- cache_dir_file = Gio.File.new_for_path(cache_dir)
- cache_dir_file.query_info_async(
- Gio.FILE_ATTRIBUTE_ACCESS_CAN_READ, Gio.FileQueryInfoFlags.NONE,
- GLib.PRIORITY_LOW, None, self._cache_dir_info_read, None)
-
- def _cache_dir_info_read(self, cache_dir_file, res, data):
- try:
- cache_dir_file.query_info_finish(res)
- return
- except GLib.Error:
- # directory does not exist yet
- try:
- cache_dir_file.make_directory(None)
- except GLib.Error as error:
- self._log.warning(
- "Error: {}, {}".format(error.domain, error.message))
-
- def query(self, coreartist):
- """Start the cache query
-
- :param CoreSong coresong: The CoreSong object to search art for
- """
- thumbnail_uri = coreartist.props.thumbnail
- if thumbnail_uri == "loading":
- self.emit("result", self._loading_icon)
- return
- elif thumbnail_uri == "generic":
- self.emit("result", self._default_icon)
- return
-
- thumb_file = Gio.File.new_for_path(thumbnail_uri)
- if thumb_file:
- thumb_file.read_async(
- GLib.PRIORITY_LOW, None, self._open_stream, None)
- return
-
- self.emit("result", self._default_icon)
-
- def _open_stream(self, thumb_file, result, arguments):
- try:
- stream = thumb_file.read_finish(result)
- except GLib.Error as error:
- self._log.warning(
- "Error: {}, {}".format(error.domain, error.message))
- self.emit("result", self._default_icon)
- return
-
- GdkPixbuf.Pixbuf.new_from_stream_async(
- stream, None, self._pixbuf_loaded, None)
-
- def _pixbuf_loaded(self, stream, result, data):
- try:
- pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(result)
- except GLib.Error as error:
- self._log.warning(
- "Error: {}, {}".format(error.domain, error.message))
- self.emit("result", self._default_icon)
- 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("result", surface)
-
- def _close_stream(self, stream, result, data):
- try:
- stream.close_finish(result)
- except GLib.Error as error:
- self._log.warning(
- "Error: {}, {}".format(error.domain, error.message))
diff --git a/gnomemusic/coreartist.py b/gnomemusic/coreartist.py
index a5d954bf..036f4c17 100644
--- a/gnomemusic/coreartist.py
+++ b/gnomemusic/coreartist.py
@@ -111,7 +111,7 @@ class CoreArtist(GObject.GObject):
def thumbnail(self):
"""Artist art thumbnail retrieval
- :return: The artist art location or "generic" or "loading"
+ :return: The artist art uri or "generic" or "loading"
:rtype: string
"""
if self._thumbnail is None:
@@ -124,6 +124,6 @@ class CoreArtist(GObject.GObject):
def thumbnail(self, value):
"""Artist art thumbnail setter
- :param string value: path, "generic" or "loading"
+ :param string value: uri, "generic" or "loading"
"""
self._thumbnail = value
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index df0664dd..f1caec59 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -1004,8 +1004,7 @@ class GrlTrackerWrapper(GObject.GObject):
coreartist.props.thumbnail = "generic"
return
- media.set_thumbnail(resolved_media.get_thumbnail())
- StoreArtistArt(coreartist)
+ StoreArtistArt(coreartist, resolved_media.get_thumbnail())
full_options = Grl.OperationOptions()
full_options.set_resolution_flags(
diff --git a/gnomemusic/storeartistart.py b/gnomemusic/storeartistart.py
index 95aaecd3..b6c6d4ed 100644
--- a/gnomemusic/storeartistart.py
+++ b/gnomemusic/storeartistart.py
@@ -33,16 +33,16 @@ class StoreArtistArt(GObject.Object):
"""Stores Art in the MediaArt cache.
"""
- def __init__(self, coreartist):
+ def __init__(self, coreartist, uri):
"""Initialize StoreArtistArt
- :param CoreArtist coreartist: The core artist to store art for
+ :param coreartist: The CoreArtist to store art for
+ :param string uri: The art uri
"""
self._coreartist = coreartist
self._log = MusicLogger()
self._soup_session = Soup.Session.new()
- uri = coreartist.props.media.get_thumbnail()
if (uri is None
or uri == ""):
self._coreartist.props.thumbnail = "generic"
@@ -117,7 +117,7 @@ class StoreArtistArt(GObject.Object):
self._coreartist.props.thumbnail = "generic"
return
- success, cache_path = MediaArt.get_path(
+ success, cache_file = MediaArt.get_file(
self._coreartist.props.artist, None, "artist")
if not success:
@@ -126,15 +126,15 @@ class StoreArtistArt(GObject.Object):
try:
# FIXME: I/O blocking
- MediaArt.file_to_jpeg(tmp_file.get_path(), cache_path)
+ MediaArt.file_to_jpeg(tmp_file.get_path(), cache_file.get_path())
except GLib.Error as error:
self._log.warning(
"Error: {}, {}".format(error.domain, error.message))
self._coreartist.props.thumbnail = "generic"
return
- self._coreartist.props.media.set_thumbnail(cache_path)
- self._coreartist.props.thumbnail = cache_path
+ self._coreartist.props.media.set_thumbnail(cache_file.get_uri())
+ self._coreartist.props.thumbnail = cache_file.get_uri()
tmp_file.delete_async(
GLib.PRIORITY_LOW, None, self._delete_callback, None)
diff --git a/gnomemusic/widgets/artistartstack.py b/gnomemusic/widgets/artistartstack.py
index 35306774..75f2a4af 100644
--- a/gnomemusic/widgets/artistartstack.py
+++ b/gnomemusic/widgets/artistartstack.py
@@ -25,7 +25,7 @@
from gi.repository import GObject, Gtk
from gnomemusic.albumartcache import Art
-from gnomemusic.artistart import ArtistCache, DefaultIcon
+from gnomemusic.artcache import ArtCache, DefaultIcon
from gnomemusic.coreartist import CoreArtist
@@ -107,7 +107,7 @@ class ArtistArtStack(Gtk.Stack):
self._on_thumbnail_changed(self._coreartist, None)
def _on_thumbnail_changed(self, coreartist, uri):
- self._cache = ArtistCache(self.props.size, self.props.scale_factor)
+ self._cache = ArtCache(self.props.size, self.props.scale_factor)
self._handler_id = self._cache.connect("result", self._on_cache_result)
self._cache.query(coreartist)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]