summaryrefslogtreecommitdiff
path: root/gnomemusic/artistart.py
diff options
context:
space:
mode:
Diffstat (limited to 'gnomemusic/artistart.py')
-rw-r--r--gnomemusic/artistart.py299
1 files changed, 9 insertions, 290 deletions
diff --git a/gnomemusic/artistart.py b/gnomemusic/artistart.py
index c810198b..9cb0e3c6 100644
--- a/gnomemusic/artistart.py
+++ b/gnomemusic/artistart.py
@@ -22,113 +22,20 @@
# 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")
-from gi.repository import Gdk, GdkPixbuf, Gio, GLib, GObject, Gtk, MediaArt
-
-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):
+ """Artist art retrieval object
+ """
- _log = MusicLogger()
-
- def __init__(self, coreartist, coremodel):
- """Initialize the ArtistArt.
+ def __init__(self, application, coreartist):
+ """Initialize.
+ :param Application application: The application object
:param CoreArtist coreartist: The coreartist to use
- :param CoreModel coremodel: The main coremodel
"""
super().__init__()
@@ -138,204 +45,16 @@ class ArtistArt(GObject.GObject):
if self._in_cache():
return
- grilo = coremodel.props.grilo
-
- self._coreartist.connect(
- "notify::thumbnail", self._on_thumbnail_changed)
-
- grilo.get_artist_art(self._coreartist)
+ application.props.coregrilo.get_artist_art(self._coreartist)
def _in_cache(self):
success, thumb_file = MediaArt.get_file(
self._artist, None, "artist")
if (not success
or not thumb_file.query_exists()):
+ self._coreartist.props.thumbnail = "loading"
return False
- self._coreartist.props.cached_thumbnail_uri = thumb_file.get_path()
+ self._coreartist.props.thumbnail = thumb_file.get_uri()
return True
-
- def _on_thumbnail_changed(self, coreartist, thumbnail):
- uri = coreartist.props.thumbnail
-
- if (uri is None
- or uri == ""):
- self._coreartist.props.cached_thumbnail_uri = ""
- return
-
- src = Gio.File.new_for_uri(uri)
- src.read_async(
- GLib.PRIORITY_LOW, None, self._read_callback, None)
-
- def _read_callback(self, src, result, data):
- try:
- istream = src.read_finish(result)
- except GLib.Error as error:
- self._log.warning(
- "Error: {}, {}".format(error.domain, error.message))
- self._coreartist.props.cached_thumbnail_uri = ""
- return
-
- try:
- [tmp_file, iostream] = Gio.File.new_tmp()
- except GLib.Error as error:
- self._log.warning(
- "Error: {}, {}".format(error.domain, error.message))
- self._coreartist.props.cached_thumbnail_uri = ""
- return
-
- ostream = iostream.get_output_stream()
- # FIXME: Passing the iostream here, otherwise it gets
- # closed. PyGI specific issue?
- ostream.splice_async(
- istream, Gio.OutputStreamSpliceFlags.CLOSE_SOURCE
- | Gio.OutputStreamSpliceFlags.CLOSE_TARGET, GLib.PRIORITY_LOW,
- None, self._splice_callback, [tmp_file, iostream])
-
- def _delete_callback(self, src, result, data):
- try:
- src.delete_finish(result)
- except GLib.Error as error:
- self._log.warning(
- "Error: {}, {}".format(error.domain, error.message))
-
- def _splice_callback(self, src, result, data):
- tmp_file, iostream = data
-
- iostream.close_async(
- GLib.PRIORITY_LOW, None, self._close_iostream_callback, None)
-
- try:
- src.splice_finish(result)
- except GLib.Error as error:
- self._log.warning(
- "Error: {}, {}".format(error.domain, error.message))
- self._coreartist.props.cached_thumbnail_uri = ""
- return
-
- success, cache_path = MediaArt.get_path(self._artist, None, "artist")
-
- if not success:
- self._coreartist.props.cached_thumbnail_uri = ""
- return
-
- try:
- # FIXME: I/O blocking
- MediaArt.file_to_jpeg(tmp_file.get_path(), cache_path)
- except GLib.Error as error:
- self._log.warning(
- "Error: {}, {}".format(error.domain, error.message))
- self._coreartist.props.cached_thumbnail_uri = ""
- return
-
- self._in_cache()
-
- tmp_file.delete_async(
- GLib.PRIORITY_LOW, None, self._delete_callback, None)
-
- def _close_iostream_callback(self, src, result, data):
- try:
- src.close_finish(result)
- except GLib.Error as error:
- self._log.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__ = {
- "result": (GObject.SignalFlags.RUN_FIRST, None, (object, ))
- }
-
- _log = MusicLogger()
-
- def __init__(self, size, scale):
- super().__init__()
-
- self._size = size
- self._scale = 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.cached_thumbnail_uri
- if thumbnail_uri == "":
- self.emit("result", self._default_icon)
- return
- elif thumbnail_uri is None:
- 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))