[gnome-music/wip/mschraal/core-thumb-property: 15/20] coresong: Add thumbnail property



commit 183e55b9851e9e5d4da879275b469821b5b34f24
Author: Marinus Schraal <mschraal gnome org>
Date:   Sun Jun 14 16:57:28 2020 +0200

    coresong: Add thumbnail property
    
    Use the same machinery as album and artist art retrieval.

 data/ui/PlayerToolbar.ui                      |  2 +-
 gnomemusic/artcache.py                        |  7 ++-
 gnomemusic/coregrilo.py                       |  8 +++
 gnomemusic/coresong.py                        | 25 +++++++++
 gnomemusic/grilowrappers/grltrackerwrapper.py | 40 +++++++++++++++
 gnomemusic/songart.py                         | 73 +++++++++++++++++++++++++++
 gnomemusic/storeart.py                        |  5 ++
 gnomemusic/widgets/playertoolbar.py           |  7 ++-
 8 files changed, 160 insertions(+), 7 deletions(-)
---
diff --git a/data/ui/PlayerToolbar.ui b/data/ui/PlayerToolbar.ui
index 142a74fc..79c37bbc 100644
--- a/data/ui/PlayerToolbar.ui
+++ b/data/ui/PlayerToolbar.ui
@@ -120,7 +120,7 @@
         <property name="spacing">8</property>
         <signal name="query-tooltip" handler="_on_tooltip_query"/>
         <child>
-          <object class="CoverStack" id="_cover_stack">
+          <object class="ArtStack" id="_art_stack">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
           </object>
diff --git a/gnomemusic/artcache.py b/gnomemusic/artcache.py
index 8627b938..28f29cdc 100644
--- a/gnomemusic/artcache.py
+++ b/gnomemusic/artcache.py
@@ -29,6 +29,7 @@ from gi.repository import Gdk, GdkPixbuf, Gio, Gtk, GLib, GObject
 
 from gnomemusic.corealbum import CoreAlbum
 from gnomemusic.coreartist import CoreArtist
+from gnomemusic.coresong import CoreSong
 from gnomemusic.musiclogger import MusicLogger
 
 
@@ -203,7 +204,8 @@ class ArtCache(GObject.GObject):
             self._default_icon = DefaultIcon().get(
                 DefaultIcon.Type.ARTIST, self._size, self._scale,
                 round_shape=True)
-        elif isinstance(coreobject, CoreAlbum):
+        elif (isinstance(coreobject, CoreAlbum)
+                or isinstance(coreobject, CoreSong)):
             self._loading_icon = DefaultIcon().get(
                 DefaultIcon.Type.LOADING, self._size, self._scale)
             self._default_icon = DefaultIcon().get(
@@ -253,7 +255,8 @@ class ArtCache(GObject.GObject):
         if isinstance(self._coreobject, CoreArtist):
             surface = _make_icon_frame(
                 surface, self._size, self._scale, round_shape=True)
-        elif isinstance(self._coreobject, CoreAlbum):
+        elif (isinstance(self._coreobject, CoreAlbum)
+                or isinstance(self._coreobject, CoreSong)):
             surface = _make_icon_frame(surface, self._size, self._scale)
 
         self.emit("result", surface)
diff --git a/gnomemusic/coregrilo.py b/gnomemusic/coregrilo.py
index cf78bd58..9ecc7a4d 100644
--- a/gnomemusic/coregrilo.py
+++ b/gnomemusic/coregrilo.py
@@ -204,6 +204,14 @@ class CoreGrilo(GObject.GObject):
             self._wrappers["grl-tracker-source"].get_album_art_for_item(
                 coresong, callback)
 
+    def get_song_art(self, coresong):
+        """Retrieve song art for the given CoreSong
+
+        :param CoreSong coresong: CoreSong to retrieve art for
+        """
+        if "grl-tracker-source" in self._wrappers:
+            self._wrappers["grl-tracker-source"].get_song_art(coresong)
+
     def get_album_art(self, corealbum):
         """Retrieve album art for the given CoreAlbum
 
diff --git a/gnomemusic/coresong.py b/gnomemusic/coresong.py
index eaa67fc5..73ab54c6 100644
--- a/gnomemusic/coresong.py
+++ b/gnomemusic/coresong.py
@@ -28,6 +28,7 @@ import gi
 gi.require_version('Grl', '0.3')
 from gi.repository import Grl, GLib, GObject
 
+from gnomemusic.songart import SongArt
 import gnomemusic.utils as utils
 
 
@@ -63,10 +64,12 @@ class CoreSong(GObject.GObject):
         """
         super().__init__()
 
+        self._application = application
         self._coregrilo = application.props.coregrilo
         self._coreselection = application.props.coreselection
         self._favorite = False
         self._selected = False
+        self._thumbnail = None
 
         self.props.grlid = media.get_source() + media.get_id()
         self._is_tracker = media.get_source() == "grl-tracker-source"
@@ -116,6 +119,28 @@ class CoreSong(GObject.GObject):
         self._selected = value
         self._coreselection.update_selection(self, self._selected)
 
+    @GObject.Property(type=str, default=None)
+    def thumbnail(self):
+        """Song art thumbnail retrieval
+
+        :return: The song art location or "generic" or "loading"
+        :rtype: string
+        """
+        if self._thumbnail is None:
+            self._thumbnail = "loading"
+            self.notify("thumbnail")
+            SongArt(self._application, self)
+
+        return self._thumbnail
+
+    @thumbnail.setter
+    def thumbnail(self, value):
+        """Song art thumbnail setter
+
+        :param string value: path, "generic" or "loading"
+        """
+        self._thumbnail = value
+
     def update(self, media):
         self.props.media = media
         self.props.album = utils.get_album_title(media)
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index bf92553e..9fd4b3f1 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -982,6 +982,46 @@ class GrlTrackerWrapper(GObject.GObject):
 
         return query
 
+    def get_song_art(self, coresong):
+        """Retrieve song art for the given CoreSong
+
+        Since MediaArt does not really support per-song art this
+        uses the songs album information as base to retrieve relevant
+        art.
+
+        :param CoreSong coresong: CoreSong to get art for
+        """
+        media = coresong.props.media
+
+        def art_retrieved_cb(source, op_id, queried_media, remaining, error):
+            if error:
+                self._log.warning("Error: {}".format(error))
+                coresong.props.thumbnail = "generic"
+                return
+
+            if (remaining == 0
+                    and queried_media is None):
+                coresong.props.thumbnail = "generic"
+                return
+
+            thumbnail_uri = queried_media.get_thumbnail()
+            if thumbnail_uri is None:
+                coresong.props.thumbnail = "generic"
+            else:
+                media.set_thumbnail(thumbnail_uri)
+                StoreArt(coresong)
+
+        song_id = media.get_id()
+        query = self._get_album_for_song_id(song_id)
+
+        full_options = Grl.OperationOptions()
+        full_options.set_resolution_flags(
+            Grl.ResolutionFlags.FULL | Grl.ResolutionFlags.IDLE_RELAY)
+
+        self.props.source.query(
+            query, self.METADATA_THUMBNAIL_KEYS, full_options,
+            art_retrieved_cb)
+
     def get_album_art(self, corealbum):
         """Retrieve album art for the given CoreAlbum
 
diff --git a/gnomemusic/songart.py b/gnomemusic/songart.py
new file mode 100644
index 00000000..95fd6b1c
--- /dev/null
+++ b/gnomemusic/songart.py
@@ -0,0 +1,73 @@
+# 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
+
+from gnomemusic.embeddedart import EmbeddedArt
+from gnomemusic.musiclogger import MusicLogger
+
+
+class SongArt(GObject.GObject):
+
+    _log = MusicLogger()
+
+    def __init__(self, application, coresong):
+        """Initialize SongArt
+
+        :param Application application: The application object
+        :param CoreSong coresong: The coresong to use
+        """
+        super().__init__()
+
+        self._application = application
+        self._coresong = coresong
+        self._album = self._coresong.props.album
+        self._artist = self._coresong.props.artist
+
+        if self._in_cache():
+            return
+
+        embedded = EmbeddedArt()
+        embedded.connect("art-found", self._on_embedded_art_found)
+        embedded.query(coresong)
+
+    def _on_embedded_art_found(self, embeddedart, found):
+        if found:
+            self._in_cache()
+        else:
+            self._application.props.coregrilo.get_song_art(self._coresong)
+
+    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._coresong.props.thumbnail = "loading"
+            return False
+
+        self._coresong.props.thumbnail = thumb_file.get_path()
+
+        return True
diff --git a/gnomemusic/storeart.py b/gnomemusic/storeart.py
index 6f0f6595..c397b083 100644
--- a/gnomemusic/storeart.py
+++ b/gnomemusic/storeart.py
@@ -29,6 +29,7 @@ from gi.repository import Gio, GLib, GObject, MediaArt, Soup
 from gnomemusic.musiclogger import MusicLogger
 from gnomemusic.coreartist import CoreArtist
 from gnomemusic.corealbum import CoreAlbum
+from gnomemusic.coresong import CoreSong
 
 
 class StoreArt(GObject.Object):
@@ -127,6 +128,10 @@ class StoreArt(GObject.Object):
             success, cache_path = MediaArt.get_path(
                 self._coreobject.props.artist, self._coreobject.props.title,
                 "album")
+        elif isinstance(self._coreobject, CoreSong):
+            success, cache_path = MediaArt.get_path(
+                self._coreobject.props.artist, self._coreobject.props.album,
+                "album")
         else:
             success = False
 
diff --git a/gnomemusic/widgets/playertoolbar.py b/gnomemusic/widgets/playertoolbar.py
index e060f02d..ba72f7af 100644
--- a/gnomemusic/widgets/playertoolbar.py
+++ b/gnomemusic/widgets/playertoolbar.py
@@ -28,7 +28,6 @@ from gi.repository import GObject, Gtk
 from gnomemusic.albumartcache import Art
 from gnomemusic.gstplayer import Playback
 from gnomemusic.player import Player, RepeatMode
-from gnomemusic.widgets.coverstack import CoverStack  # noqa: F401
 from gnomemusic.widgets.smoothscale import SmoothScale  # noqa: F401
 from gnomemusic.widgets.twolinetip import TwoLineTip
 import gnomemusic.utils as utils
@@ -44,7 +43,7 @@ class PlayerToolbar(Gtk.ActionBar):
     __gtype_name__ = 'PlayerToolbar'
 
     _artist_label = Gtk.Template.Child()
-    _cover_stack = Gtk.Template.Child()
+    _art_stack = Gtk.Template.Child()
     _duration_label = Gtk.Template.Child()
     _next_button = Gtk.Template.Child()
     _pause_image = Gtk.Template.Child()
@@ -69,7 +68,7 @@ class PlayerToolbar(Gtk.ActionBar):
 
         self._player = None
 
-        self._cover_stack.props.size = Art.Size.XSMALL
+        self._art_stack.props.size = Art.Size.XSMALL
 
         self._tooltip = TwoLineTip()
 
@@ -177,7 +176,7 @@ class PlayerToolbar(Gtk.ActionBar):
         self._tooltip.props.title = title
         self._tooltip.props.subtitle = artist
 
-        self._cover_stack.update(coresong)
+        self._art_stack.props.coreobject = coresong
 
     @Gtk.Template.Callback()
     def _on_tooltip_query(self, widget, x, y, kb, tooltip, data=None):


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]