diff options
author | Marinus Schraal <mschraal@gnome.org> | 2020-07-06 00:33:01 +0200 |
---|---|---|
committer | Marinus Schraal <mschraal@gnome.org> | 2020-07-06 11:32:40 +0200 |
commit | f22f41a78ba35bb08dc6a1ae406d447d5cfbc691 (patch) | |
tree | d5b99324bfd695ddfae774650cb22db0a8e1489e | |
parent | 51d59bab79ce6f7c59693a66cdb08d8d5bfc6a2b (diff) |
corealbum: Add thumbnail property
Use the ArtistArt machinery to retrieve and store art.
-rw-r--r-- | data/ui/AlbumCover.ui | 2 | ||||
-rw-r--r-- | data/ui/AlbumWidget.ui | 2 | ||||
-rw-r--r-- | data/ui/ArtistAlbumWidget.ui | 2 | ||||
-rw-r--r-- | gnomemusic/albumart.py | 61 | ||||
-rw-r--r-- | gnomemusic/artcache.py | 36 | ||||
-rw-r--r-- | gnomemusic/corealbum.py | 26 | ||||
-rw-r--r-- | gnomemusic/coregrilo.py | 8 | ||||
-rw-r--r-- | gnomemusic/grilowrappers/grltrackerwrapper.py | 36 | ||||
-rw-r--r-- | gnomemusic/storeartistart.py | 36 | ||||
-rw-r--r-- | gnomemusic/widgets/albumcover.py | 6 | ||||
-rw-r--r-- | gnomemusic/widgets/albumwidget.py | 6 | ||||
-rw-r--r-- | gnomemusic/widgets/artistalbumwidget.py | 8 |
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) |