[gnome-music/wip/mschraal/grltracker-rework-disc-splitting] Rework Tracker album query




commit 6d49590b936976c5484ba2a88939356b1dd0e29f
Author: Marinus Schraal <mschraal gnome org>
Date:   Tue May 3 21:12:23 2022 +0200

    Rework Tracker album query
    
    Do one large query per album instead of several.

 gnomemusic/corealbum.py                       | 61 ++++++++++++------
 gnomemusic/coredisc.py                        | 33 ++++------
 gnomemusic/coregrilo.py                       | 10 +++
 gnomemusic/grilowrappers/grltrackerwrapper.py | 93 ++++++++++++++++++++++++++-
 4 files changed, 155 insertions(+), 42 deletions(-)
---
diff --git a/gnomemusic/corealbum.py b/gnomemusic/corealbum.py
index af3a1198c..c62aec7ff 100644
--- a/gnomemusic/corealbum.py
+++ b/gnomemusic/corealbum.py
@@ -31,6 +31,7 @@ from gi.repository import Gio, Grl, Gtk, GObject
 import gnomemusic.utils as utils
 
 from gnomemusic.albumart import AlbumArt
+from gnomemusic.coredisc import CoreDisc
 
 
 class CoreAlbum(GObject.GObject):
@@ -57,8 +58,11 @@ class CoreAlbum(GObject.GObject):
 
         self._application = application
         self._coregrilo = application.props.coregrilo
+
         self._model = None
         self._selected = False
+        self._songs_model = Gtk.FilterListModel.new(
+            application.props.coremodel.props.songs, Gtk.AnyFilter())
         self._thumbnail = None
 
         self.update(media)
@@ -75,6 +79,23 @@ class CoreAlbum(GObject.GObject):
         self.props.url = media.get_url()
         self.props.year = utils.get_media_year(media)
 
+    def _update_disc_model(self) -> None:
+        discs = set()
+        for coresong in self._songs_model:
+            discs.add(coresong.props.media.get_album_disc_number())
+
+        model_discs = []
+        for coredisc in self._model:
+            model_discs.append(coredisc.props.disc_nr)
+
+        missing = discs.difference(model_discs)
+
+        for disc_nr in missing:
+            self._model.props.model.append(
+                CoreDisc(
+                    self._application, self.props.media, disc_nr,
+                    self._songs_model))
+
     def _get_album_model(self):
         disc_model = Gio.ListStore()
         disc_model_sort = Gtk.SortListModel.new(disc_model)
@@ -101,30 +122,28 @@ class CoreAlbum(GObject.GObject):
         flags=GObject.ParamFlags.READABLE)
     def model(self):
         if self._model is None:
-            self._model = self._get_album_model()
-            self._model.connect("items-changed", self._on_list_items_changed)
-            self._model.items_changed(0, 0, self._model.get_n_items())
+            self._model = Gtk.SortListModel.new(Gio.ListStore())
 
-        return self._model
-
-    def _on_list_items_changed(self, model, position, removed, added):
-        with self.freeze_notify():
-            for coredisc in model:
-                coredisc.props.selected = self.props.selected
-
-            if added > 0:
-                for i in range(added):
-                    coredisc = model[position + i]
-                    coredisc.connect(
-                        "notify::duration", self._on_duration_changed)
+            self._songs_model.connect("items-changed", self._on_songs_list_changed)
+            self._coregrilo.get_album_songs(self.props.media, self._songs_model)
 
-    def _on_duration_changed(self, coredisc, duration):
-        duration = 0
-
-        for coredisc in self.props.model:
-            duration += coredisc.props.duration
+        return self._model
 
-        self.props.duration = duration
+    def _on_songs_list_changed(
+            self, model: Gtk.FilterListModel, position: int, removed: int,
+            added: int) -> None:
+        with model.freeze_notify():
+            #for song in model:
+            #     coredisc.props.selected = self.props.selected
+
+            if (added > 0
+                    or removed > 0):
+                self._update_disc_model()
+                duration = 0
+                for coresong in model:
+                    duration += coresong.props.duration
+
+                self.props.duration = duration
 
     @GObject.Property(type=bool, default=False)
     def selected(self):
diff --git a/gnomemusic/coredisc.py b/gnomemusic/coredisc.py
index ece33ba2c..64b827cbd 100644
--- a/gnomemusic/coredisc.py
+++ b/gnomemusic/coredisc.py
@@ -33,7 +33,7 @@ class CoreDisc(GObject.GObject):
     duration = GObject.Property(type=int, default=None)
     media = GObject.Property(type=Grl.Media, default=None)
 
-    def __init__(self, application, media, nr):
+    def __init__(self, application, media, nr, album_model=None):
         """Initialize a CoreDisc object
 
         :param Application application: The application object
@@ -42,6 +42,7 @@ class CoreDisc(GObject.GObject):
         """
         super().__init__()
 
+        self._album_model = album_model
         self._coregrilo = application.props.coregrilo
         self._coremodel = application.props.coremodel
         self._filter_model = None
@@ -66,32 +67,24 @@ class CoreDisc(GObject.GObject):
             else:
                 return Gtk.Ordering.EQUAL
 
+        def _disc_nr_filter(coresong):
+            cs_dn = coresong.props.media.get_album_disc_number()
+            return cs_dn == self.props.disc_nr
+
         if self._model is None:
             self._filter_model = Gtk.FilterListModel.new(
-                self._coremodel.props.songs)
-            self._filter_model.set_filter(Gtk.AnyFilter())
+                self._album_model)
+            filter = Gtk.CustomFilter()
+            filter.set_filter_func(_disc_nr_filter)
+            self._filter_model.set_filter(filter)
 
             self._model = Gtk.SortListModel.new(self._filter_model)
-            disc_sorter = Gtk.CustomSorter()
-            disc_sorter.set_sort_func(_disc_sort)
-            self._model.set_sorter(disc_sorter)
-
-            self._model.connect("items-changed", self._on_disc_changed)
-
-            self._coregrilo.get_album_disc(
-                self.props.media, self.props.disc_nr, self._filter_model)
+            song_sorter = Gtk.CustomSorter()
+            song_sorter.set_sort_func(_disc_sort)
+            self._model.set_sorter(song_sorter)
 
         return self._model
 
-    def _on_disc_changed(self, model, position, removed, added):
-        with self.freeze_notify():
-            duration = 0
-            for coresong in model:
-                coresong.props.selected = self._selected
-                duration += coresong.props.duration
-
-            self.props.duration = duration
-
     @GObject.Property(
         type=bool, default=False, flags=GObject.ParamFlags.READWRITE)
     def selected(self):
diff --git a/gnomemusic/coregrilo.py b/gnomemusic/coregrilo.py
index e5c17acab..ccb4db3a3 100644
--- a/gnomemusic/coregrilo.py
+++ b/gnomemusic/coregrilo.py
@@ -204,6 +204,16 @@ class CoreGrilo(GObject.GObject):
         source = media.get_source()
         self._wrappers[source].get_album_disc(media, discnr, model)
 
+    def get_album_songs(
+            self, media: Grl.Media, model: Gtk.FilterListModel) -> None:
+        """Get all songs from an album
+
+        :param Grl.Media media: An album
+        :param Gtk.FilterListModel model: The model to fill
+        """
+        source = media.get_source()
+        self._wrappers[source].get_album_songs(media, model)
+
     def writeback(self, media, key):
         """Store the values associated with the key.
 
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index d7c300744..a7e5c03b7 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -51,7 +51,7 @@ class GrlTrackerWrapper(GObject.GObject):
     """Wrapper for the Grilo Tracker source.
     """
 
-    _SPLICE_SIZE: int = 100
+    _SPLICE_SIZE: int = 1000
 
     _METADATA_ALBUM_CHANGED_KEYS: List[int] = [
         Grl.METADATA_KEY_ARTIST,
@@ -889,6 +889,97 @@ class GrlTrackerWrapper(GObject.GObject):
         self.props.source.query(
             query, metadata_keys, self._fast_options, _callback)
 
+    def get_album_songs(
+            self, media: Grl.Media, model: Gtk.FilterListModel) -> None:
+        """Get all songs of an album
+
+        :param Grl.Media media: The media with the album id
+        :param Gtk.FilterListModel model: The model to fill
+        """
+        album_id = media.get_id()
+
+        query = """
+        SELECT
+            ?type ?id ?url ?title
+            ?artist ?album
+            ?duration ?trackNumber ?albumDiscNumber
+            ?publicationDate
+            nie:usageCounter(?id) AS ?playCount
+            ?tag AS ?favorite
+        WHERE {
+            SERVICE <dbus:%(miner_fs_busname)s> {
+                GRAPH tracker:Audio {
+                    SELECT DISTINCT
+                        %(media_type)s AS ?type
+                        ?song AS ?id
+                        nie:isStoredAs(?song) AS ?url
+                        nie:title(?song) AS ?title
+                        nmm:artistName(nmm:artist(?song)) AS ?artist
+                        nie:title(nmm:musicAlbum(?song)) AS ?album
+                        nfo:duration(?song) AS ?duration
+                        nmm:trackNumber(?song) AS ?trackNumber
+                        nmm:setNumber(nmm:musicAlbumDisc(?song))
+                            AS ?albumDiscNumber
+                        YEAR(?date) AS ?publicationDate
+                    WHERE {
+                        ?song a nmm:MusicPiece ;
+                                nmm:musicAlbum ?album .
+                        OPTIONAL { ?song nie:contentCreated ?date . }
+                        FILTER (
+                            ?album = <%(album_id)s>
+                        )
+                        %(location_filter)s
+                    }
+                    ORDER BY ?albumDiscNumber ?trackNumber
+                }
+            }
+            OPTIONAL {
+                ?id nao:hasTag ?tag .
+                FILTER (?tag = nao:predefined-tag-favorite)
+            }
+        }
+        """.replace('\n', ' ').strip() % {
+            "media_type": int(Grl.MediaType.AUDIO),
+            'album_id': album_id,
+            'location_filter': self._tracker_wrapper.location_filter(),
+            'miner_fs_busname': self._tracker_wrapper.props.miner_fs_busname
+        }
+
+        metadata_keys: List[int] = [
+            Grl.METADATA_KEY_ALBUM,
+            Grl.METADATA_KEY_ALBUM_DISC_NUMBER,
+            Grl.METADATA_KEY_ARTIST,
+            Grl.METADATA_KEY_DURATION,
+            Grl.METADATA_KEY_FAVOURITE,
+            Grl.METADATA_KEY_ID,
+            Grl.METADATA_KEY_PLAY_COUNT,
+            Grl.METADATA_KEY_TITLE,
+            Grl.METADATA_KEY_URL
+        ]
+
+        album_song_ids: List[int] = []
+
+        def _filter_func(coresong: CoreSong) -> bool:
+            return coresong.props.grlid in album_song_ids
+
+        def _callback(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: GLib.Error) -> None:
+            if error:
+                self._log.warning(f"Error: {error.domain}, {error.message}")
+                return
+
+            if media is None:
+                custom_filter = Gtk.CustomFilter()
+                custom_filter.set_filter_func(_filter_func)
+                model.set_filter(custom_filter)
+                return
+
+            album_song_ids.append(media.get_source() + media.get_id())
+
+        self.props.source.query(
+            query, metadata_keys, self._fast_options, _callback)
+
     def search(self, text: str) -> None:
         # FIXME: Searches are limited to not bog down the UI with
         # widget creation ({List,Flow}Box limitations). The limit is


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