summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarinus Schraal <mschraal@gnome.org>2020-07-06 00:33:01 +0200
committerMarinus Schraal <mschraal@gnome.org>2020-07-06 11:32:40 +0200
commitf22f41a78ba35bb08dc6a1ae406d447d5cfbc691 (patch)
treed5b99324bfd695ddfae774650cb22db0a8e1489e
parent51d59bab79ce6f7c59693a66cdb08d8d5bfc6a2b (diff)
corealbum: Add thumbnail property
Use the ArtistArt machinery to retrieve and store art.
-rw-r--r--data/ui/AlbumCover.ui2
-rw-r--r--data/ui/AlbumWidget.ui2
-rw-r--r--data/ui/ArtistAlbumWidget.ui2
-rw-r--r--gnomemusic/albumart.py61
-rw-r--r--gnomemusic/artcache.py36
-rw-r--r--gnomemusic/corealbum.py26
-rw-r--r--gnomemusic/coregrilo.py8
-rw-r--r--gnomemusic/grilowrappers/grltrackerwrapper.py36
-rw-r--r--gnomemusic/storeartistart.py36
-rw-r--r--gnomemusic/widgets/albumcover.py6
-rw-r--r--gnomemusic/widgets/albumwidget.py6
-rw-r--r--gnomemusic/widgets/artistalbumwidget.py8
12 files changed, 194 insertions, 35 deletions
diff --git a/data/ui/AlbumCover.ui b/data/ui/AlbumCover.ui
index d825c1cc..7ac403c2 100644
--- a/data/ui/AlbumCover.ui
+++ b/data/ui/AlbumCover.ui
@@ -16,7 +16,7 @@
<property name="can_focus">False</property>
<property name="margin_bottom">4</property>
<child>
- <object class="CoverStack" id="_cover_stack">
+ <object class="ArtStack" id="_art_stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="vexpand">True</property>
diff --git a/data/ui/AlbumWidget.ui b/data/ui/AlbumWidget.ui
index 9fdb87cf..e5848709 100644
--- a/data/ui/AlbumWidget.ui
+++ b/data/ui/AlbumWidget.ui
@@ -32,7 +32,7 @@
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<child>
- <object class="CoverStack" id="_cover_stack">
+ <object class="ArtStack" id="_art_stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
diff --git a/data/ui/ArtistAlbumWidget.ui b/data/ui/ArtistAlbumWidget.ui
index 7462799b..e25083ce 100644
--- a/data/ui/ArtistAlbumWidget.ui
+++ b/data/ui/ArtistAlbumWidget.ui
@@ -7,7 +7,7 @@
<property name="margin_right">120</property>
<property name="visible">True</property>
<child>
- <object class="CoverStack" id="_cover_stack">
+ <object class="ArtStack" id="_art_stack">
<property name="visible">True</property>
<property name="margin_top">20</property>
<property name="margin_right">30</property>
diff --git a/gnomemusic/albumart.py b/gnomemusic/albumart.py
new file mode 100644
index 00000000..84008a35
--- /dev/null
+++ b/gnomemusic/albumart.py
@@ -0,0 +1,61 @@
+# 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.
+
+import gi
+gi.require_version("MediaArt", "2.0")
+from gi.repository import GObject, MediaArt
+
+
+class AlbumArt(GObject.GObject):
+ """AlbumArt retrieval object
+ """
+
+ def __init__(self, application, corealbum):
+ """Initialize AlbumArt
+
+ :param Application application: The application object
+ :param CoreAlbum corealbum: The corealbum to use
+ """
+ super().__init__()
+
+ self._corealbum = corealbum
+ self._album = self._corealbum.props.title
+ self._artist = self._corealbum.props.artist
+
+ if self._in_cache():
+ return
+
+ application.props.coregrilo.get_album_art(self._corealbum)
+
+ def _in_cache(self):
+ success, thumb_file = MediaArt.get_file(
+ self._artist, self._album, "album")
+ if (not success
+ or not thumb_file.query_exists()):
+ self._corealbum.props.thumbnail = "loading"
+ return False
+
+ self._corealbum.props.thumbnail = thumb_file.get_uri()
+
+ return True
diff --git a/gnomemusic/artcache.py b/gnomemusic/artcache.py
index f4bc45fa..b067ef2e 100644
--- a/gnomemusic/artcache.py
+++ b/gnomemusic/artcache.py
@@ -27,6 +27,8 @@ from math import pi
import cairo
from gi.repository import Gdk, GdkPixbuf, Gio, Gtk, GLib, GObject
+from gnomemusic.corealbum import CoreAlbum
+from gnomemusic.coreartist import CoreArtist
from gnomemusic.musiclogger import MusicLogger
@@ -120,6 +122,7 @@ class DefaultIcon(GObject.GObject):
class Type(Enum):
ARTIST = "avatar-default-symbolic"
+ ARTIST_LOADING = "content-loading-symbolic"
LOADING = "content-loading-symbolic"
MUSIC = "folder-music-symbolic"
@@ -182,17 +185,29 @@ class ArtCache(GObject.GObject):
self._size = size
self._scale = scale
- self._loading_icon = DefaultIcon().get(
- DefaultIcon.Type.LOADING, self._size, self._scale, True)
- self._default_icon = DefaultIcon().get(
- DefaultIcon.Type.ARTIST, self._size, self._scale, True)
+ self._coreobject = None
+ self._default_icon = None
+ self._loading_icon = None
- def query(self, coreartist):
+ def query(self, coreobject):
"""Start the cache query
- :param CoreArtist coreartist: The object to search art for
+ :param coreobject: The object to search art for
"""
- thumbnail_uri = coreartist.props.thumbnail
+ self._coreobject = coreobject
+
+ if isinstance(coreobject, CoreArtist):
+ self._loading_icon = DefaultIcon().get(
+ DefaultIcon.Type.ARTIST_LOADING, self._size, self._scale, True)
+ self._default_icon = DefaultIcon().get(
+ DefaultIcon.Type.ARTIST, self._size, self._scale, True)
+ elif isinstance(coreobject, CoreAlbum):
+ self._loading_icon = DefaultIcon().get(
+ DefaultIcon.Type.LOADING, self._size, self._scale)
+ self._default_icon = DefaultIcon().get(
+ DefaultIcon.Type.MUSIC, self._size, self._scale)
+
+ thumbnail_uri = coreobject.props.thumbnail
if thumbnail_uri == "loading":
self.emit("result", self._loading_icon)
return
@@ -233,8 +248,11 @@ class ArtCache(GObject.GObject):
surface = Gdk.cairo_surface_create_from_pixbuf(
pixbuf, self._scale, None)
- surface = _make_icon_frame(
- surface, self._size, self._scale, round_shape=True)
+ if isinstance(self._coreobject, CoreArtist):
+ surface = _make_icon_frame(
+ surface, self._size, self._scale, round_shape=True)
+ elif isinstance(self._coreobject, CoreAlbum):
+ surface = _make_icon_frame(surface, self._size, self._scale)
self.emit("result", surface)
diff --git a/gnomemusic/corealbum.py b/gnomemusic/corealbum.py
index 588001f7..d279e811 100644
--- a/gnomemusic/corealbum.py
+++ b/gnomemusic/corealbum.py
@@ -28,6 +28,8 @@ from gi.repository import Gfm, Gio, Grl, GObject
import gnomemusic.utils as utils
+from gnomemusic.albumart import AlbumArt
+
class CoreAlbum(GObject.GObject):
"""Exposes a Grl.Media with relevant data as properties
@@ -49,9 +51,12 @@ class CoreAlbum(GObject.GObject):
"""
super().__init__()
+ self._application = application
self._coregrilo = application.props.coregrilo
self._model = None
self._selected = False
+ self._thumbnail = None
+
self.update(media)
def update(self, media):
@@ -126,3 +131,24 @@ class CoreAlbum(GObject.GObject):
# is requested, it will trigger the filled model update as
# well.
self.props.model.items_changed(0, 0, 0)
+
+ @GObject.Property(type=str, default=None)
+ def thumbnail(self):
+ """Album art thumbnail retrieval
+
+ :return: The album art uri or "generic" or "loading"
+ :rtype: string
+ """
+ if self._thumbnail is None:
+ self._thumbnail = "loading"
+ AlbumArt(self._application, self)
+
+ return self._thumbnail
+
+ @thumbnail.setter
+ def thumbnail(self, value):
+ """Album art thumbnail setter
+
+ :param string value: uri, "generic" or "loading"
+ """
+ self._thumbnail = value
diff --git a/gnomemusic/coregrilo.py b/gnomemusic/coregrilo.py
index 1540559d..6074e463 100644
--- a/gnomemusic/coregrilo.py
+++ b/gnomemusic/coregrilo.py
@@ -221,6 +221,14 @@ class CoreGrilo(GObject.GObject):
self._wrappers["grl-tracker-source"].get_album_art_for_item(
coresong, callback)
+ def get_album_art(self, corealbum):
+ """Retrieve album art for the given CoreAlbum
+
+ :param CoreAlbum corealbum: CoreAlbum to retrieve art for
+ """
+ if "grl-tracker-source" in self._wrappers:
+ self._wrappers["grl-tracker-source"].get_album_art(corealbum)
+
def get_artist_art(self, coreartist):
"""Retrieve artist art for the given CoreArtist
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index f1caec59..67291ddd 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -985,6 +985,42 @@ class GrlTrackerWrapper(GObject.GObject):
return query
+ def get_album_art(self, corealbum):
+ """Retrieve album art for the given CoreAlbum
+
+ :param CoreAlbum corealbum: CoreAlbum to get art for
+ """
+ media = corealbum.props.media
+
+ def art_retrieved_cb(source, op_id, queried_media, remaining, error):
+ if error:
+ self._log.warning("Error: {}".format(error))
+ corealbum.props.thumbnail = "generic"
+ return
+
+ if (remaining == 0
+ and queried_media is None):
+ # art_retrieved_cb should be called two times, the
+ # empty result can be ignored.
+ return
+
+ thumbnail_uri = queried_media.get_thumbnail()
+ if thumbnail_uri is None:
+ corealbum.props.thumbnail = "generic"
+ else:
+ StoreArtistArt(corealbum, thumbnail_uri)
+
+ album_id = media.get_id()
+ query = self._get_album_for_album_id(album_id)
+
+ full_options = Grl.OperationOptions()
+ full_options.set_resolution_flags(
+ Grl.ResolutionFlags.FULL | Grl.ResolutionFlags.IDLE_RELAY)
+
+ self._source.query(
+ query, self.METADATA_THUMBNAIL_KEYS, full_options,
+ art_retrieved_cb)
+
def get_artist_art(self, coreartist):
"""Retrieve artist art for the given CoreArtist
diff --git a/gnomemusic/storeartistart.py b/gnomemusic/storeartistart.py
index b6c6d4ed..ca44e910 100644
--- a/gnomemusic/storeartistart.py
+++ b/gnomemusic/storeartistart.py
@@ -27,25 +27,28 @@ gi.require_versions({"MediaArt": "2.0", "Soup": "2.4"})
from gi.repository import Gio, GLib, GObject, MediaArt, Soup
from gnomemusic.musiclogger import MusicLogger
+from gnomemusic.coreartist import CoreArtist
+from gnomemusic.corealbum import CoreAlbum
class StoreArtistArt(GObject.Object):
"""Stores Art in the MediaArt cache.
"""
- def __init__(self, coreartist, uri):
+ def __init__(self, coreobject, uri):
"""Initialize StoreArtistArt
- :param coreartist: The CoreArtist to store art for
+ :param coreobject: The CoreArtist or CoreAlbum to store art for
:param string uri: The art uri
"""
- self._coreartist = coreartist
+ self._coreobject = coreobject
+
self._log = MusicLogger()
self._soup_session = Soup.Session.new()
if (uri is None
or uri == ""):
- self._coreartist.props.thumbnail = "generic"
+ self._coreobject.props.thumbnail = "generic"
return
cache_dir = GLib.build_filenamev(
@@ -65,7 +68,7 @@ class StoreArtistArt(GObject.Object):
except GLib.Error as error:
self._log.warning(
"Error: {}, {}".format(error.domain, error.message))
- self._coreartist.props.thumbnail = "generic"
+ self._coreobject.props.thumbnail = "generic"
return
msg = Soup.Message.new("GET", uri)
@@ -83,7 +86,7 @@ class StoreArtistArt(GObject.Object):
except GLib.Error as error:
self._log.warning(
"Error: {}, {}".format(error.domain, error.message))
- self._coreartist.props.thumbnail = "generic"
+ self._coreobject.props.thumbnail = "generic"
return
istream = Gio.MemoryInputStream.new_from_bytes(
@@ -114,14 +117,21 @@ class StoreArtistArt(GObject.Object):
except GLib.Error as error:
self._log.warning(
"Error: {}, {}".format(error.domain, error.message))
- self._coreartist.props.thumbnail = "generic"
+ self._coreobject.props.thumbnail = "generic"
return
- success, cache_file = MediaArt.get_file(
- self._coreartist.props.artist, None, "artist")
+ if isinstance(self._coreobject, CoreArtist):
+ success, cache_file = MediaArt.get_file(
+ self._coreobject.props.artist, None, "artist")
+ elif isinstance(self._coreobject, CoreAlbum):
+ success, cache_file = MediaArt.get_file(
+ self._coreobject.props.artist, self._coreobject.props.title,
+ "album")
+ else:
+ success = False
if not success:
- self._coreartist.props.thumbnail = "generic"
+ self._coreobject.props.thumbnail = "generic"
return
try:
@@ -130,11 +140,11 @@ class StoreArtistArt(GObject.Object):
except GLib.Error as error:
self._log.warning(
"Error: {}, {}".format(error.domain, error.message))
- self._coreartist.props.thumbnail = "generic"
+ self._coreobject.props.thumbnail = "generic"
return
- self._coreartist.props.media.set_thumbnail(cache_file.get_uri())
- self._coreartist.props.thumbnail = cache_file.get_uri()
+ self._coreobject.props.media.set_thumbnail(cache_file.get_uri())
+ self._coreobject.props.thumbnail = cache_file.get_uri()
tmp_file.delete_async(
GLib.PRIORITY_LOW, None, self._delete_callback, None)
diff --git a/gnomemusic/widgets/albumcover.py b/gnomemusic/widgets/albumcover.py
index 9bd31e6b..557b318b 100644
--- a/gnomemusic/widgets/albumcover.py
+++ b/gnomemusic/widgets/albumcover.py
@@ -40,7 +40,7 @@ class AlbumCover(Gtk.FlowBoxChild):
__gtype_name__ = 'AlbumCover'
- _cover_stack = Gtk.Template.Child()
+ _art_stack = Gtk.Template.Child()
_check = Gtk.Template.Child()
_title_label = Gtk.Template.Child()
_artist_label = Gtk.Template.Child()
@@ -81,7 +81,7 @@ class AlbumCover(Gtk.FlowBoxChild):
self.connect('query-tooltip', self._on_tooltip_query)
- self._cover_stack.props.size = Art.Size.MEDIUM
+ self._art_stack.props.size = Art.Size.MEDIUM
self.show()
@@ -95,7 +95,7 @@ class AlbumCover(Gtk.FlowBoxChild):
return
self._retrieved = True
- self._cover_stack.update(self._corealbum)
+ self._art_stack.props.coreobject = self._corealbum
@GObject.Property(type=CoreAlbum, flags=GObject.ParamFlags.READABLE)
def corealbum(self):
diff --git a/gnomemusic/widgets/albumwidget.py b/gnomemusic/widgets/albumwidget.py
index 635b19cd..6bc81f5b 100644
--- a/gnomemusic/widgets/albumwidget.py
+++ b/gnomemusic/widgets/albumwidget.py
@@ -45,7 +45,7 @@ class AlbumWidget(Gtk.EventBox):
_artist_label = Gtk.Template.Child()
_composer_label = Gtk.Template.Child()
_composer_info_label = Gtk.Template.Child()
- _cover_stack = Gtk.Template.Child()
+ _art_stack = Gtk.Template.Child()
_disc_list_box = Gtk.Template.Child()
_released_info_label = Gtk.Template.Child()
_running_info_label = Gtk.Template.Child()
@@ -68,7 +68,7 @@ class AlbumWidget(Gtk.EventBox):
self._duration_signal_id = None
self._model_signal_id = None
- self._cover_stack.props.size = Art.Size.LARGE
+ self._art_stack.props.size = Art.Size.LARGE
self._player = self._application.props.player
self.bind_property(
@@ -89,7 +89,7 @@ class AlbumWidget(Gtk.EventBox):
self._corealbum = corealbum
- self._cover_stack.update(self._corealbum)
+ self._art_stack.props.coreobject = self._corealbum
album_name = self._corealbum.props.title
artist = self._corealbum.props.artist
diff --git a/gnomemusic/widgets/artistalbumwidget.py b/gnomemusic/widgets/artistalbumwidget.py
index 47f35f4f..b80be261 100644
--- a/gnomemusic/widgets/artistalbumwidget.py
+++ b/gnomemusic/widgets/artistalbumwidget.py
@@ -40,7 +40,7 @@ class ArtistAlbumWidget(Gtk.Box):
__gtype_name__ = 'ArtistAlbumWidget'
_album_box = Gtk.Template.Child()
- _cover_stack = Gtk.Template.Child()
+ _art_stack = Gtk.Template.Child()
_disc_list_box = Gtk.Template.Child()
_title_year = Gtk.Template.Child()
@@ -66,8 +66,8 @@ class ArtistAlbumWidget(Gtk.Box):
self._selection_mode = False
- self._cover_stack.props.size = Art.Size.MEDIUM
- self._cover_stack.update(corealbum)
+ self._art_stack.props.size = Art.Size.MEDIUM
+ self._art_stack.props.coreobject = corealbum
self.bind_property(
'selection-mode', self._disc_list_box, 'selection-mode',
@@ -83,7 +83,7 @@ class ArtistAlbumWidget(Gtk.Box):
self._size_group.add_widget(self._album_box)
if self._cover_size_group:
- self._cover_size_group.add_widget(self._cover_stack)
+ self._cover_size_group.add_widget(self._art_stack)
corealbum.props.model.connect_after(
"items-changed", self._on_model_items_changed)