[gnome-music/wip/jfelder/coredisc-updates: 2/2] Handle CoreDisc updates



commit 37d7de347af078e0737e48e8e6c6025d44bd8ba9
Author: Jean Felder <jfelder src gnome org>
Date:   Fri Feb 21 21:41:45 2020 +0100

    Handle CoreDisc updates
    
    CoreDiscs need to be updated in 3 cases:
    1. a song is removed from an album
    2. a song is added to an album
    3. a song disc number changes
    
    1. a song is removed from an album
    This case is already partially handled. Indeed, the song is already
    part of the initial list of songs. So, when, the song is removed from
    the list, the associated coredisc model gets updated. However, if the
    coredisc becomes empty, it is not removed.
    This issue is fixed by checking the album model when a CoreDisc
    becomes empty (GrlTrackerWrapper.check_album_disc_changes is called).
    
    2. a song is added to an album
    In that case, the song is not added to the album (and the
    associated coredisc) if model has already been loaded because the
    filter func has not been updated.
    This issue is fixed by adding a _check_album_content_change method to
    GrlTrackerWrapper when a new song is added or changed. This function
    checks that the album already exists and updates the album if
    necessary (check_album_disc_changes is called).
    A new CoreDiscc may be created, but not CoreDisc will be removed.
    
    3. a song disc number changes
    It's almost the same case as the previous
    one. _check_album_content_change method is also called. In that case,
    a CoreDisc may be added and an other CoreDisc may be removed.
    
    This change is based on an initial implementation by Marinus Schraal.

 gnomemusic/corealbum.py                       |   8 ++
 gnomemusic/coredisc.py                        |  11 +++
 gnomemusic/coregrilo.py                       |   8 ++
 gnomemusic/coremodel.py                       |   7 ++
 gnomemusic/grilowrappers/grltrackerwrapper.py | 104 ++++++++++++++++++++++++++
 5 files changed, 138 insertions(+)
---
diff --git a/gnomemusic/corealbum.py b/gnomemusic/corealbum.py
index 26033810..2155dc47 100644
--- a/gnomemusic/corealbum.py
+++ b/gnomemusic/corealbum.py
@@ -96,6 +96,14 @@ class CoreAlbum(GObject.GObject):
                     coredisc.connect(
                         "notify::duration", self._on_duration_changed)
 
+    def update_discs(self):
+        """Update CoreDiscs model"""
+        if not self.props.model_loaded:
+            return
+
+        for coredisc in self.props.model:
+            coredisc.update_content()
+
     def _on_duration_changed(self, coredisc, duration):
         duration = 0
 
diff --git a/gnomemusic/coredisc.py b/gnomemusic/coredisc.py
index a7def610..bc487cef 100644
--- a/gnomemusic/coredisc.py
+++ b/gnomemusic/coredisc.py
@@ -65,6 +65,12 @@ class CoreDisc(GObject.GObject):
         return self._model
 
     def _on_disc_changed(self, model, position, removed, added):
+        # If a CoreDisc becomes empty, it needs to be removed.
+        if (removed > 0
+                and model.get_n_items() == 0):
+            self._coremodel.check_album_disc_changes(self.props.media)
+            return
+
         with self.freeze_notify():
             duration = 0
             for coresong in model:
@@ -73,6 +79,11 @@ class CoreDisc(GObject.GObject):
 
             self.props.duration = duration
 
+    def update_content(self):
+        """Update the model"""
+        self._get_album_disc(
+            self.props.media, self.props.disc_nr, self._filter_model)
+
     def _disc_sort(self, song_a, song_b):
         return song_a.props.track_number - song_b.props.track_number
 
diff --git a/gnomemusic/coregrilo.py b/gnomemusic/coregrilo.py
index 3fcdb3ec..0a01af7e 100644
--- a/gnomemusic/coregrilo.py
+++ b/gnomemusic/coregrilo.py
@@ -169,6 +169,14 @@ class CoreGrilo(GObject.GObject):
         for wrapper in self._wrappers.values():
             wrapper.populate_album_disc_songs(media, discnr, callback)
 
+    def check_album_disc_changes(self, media):
+        """Update album and corediscs model
+
+        :param Grl.Media media: media with the album id
+        """
+        for wrapper in self._wrappers.values():
+            wrapper.check_album_disc_changes(media)
+
     def writeback(self, media, key):
         """Store the values associated with the key.
 
diff --git a/gnomemusic/coremodel.py b/gnomemusic/coremodel.py
index 37c8f230..0b0feb1a 100644
--- a/gnomemusic/coremodel.py
+++ b/gnomemusic/coremodel.py
@@ -222,6 +222,13 @@ class CoreModel(GObject.GObject):
 
         return albums_model_sort
 
+    def check_album_disc_changes(self, media):
+        """Update album and corediscs model
+
+        :param Grl.Media media: media with the album id
+        """
+        self.props.grilo.check_album_disc_changes(media)
+
     def set_player_model(self, playlist_type, model):
         """Set the model for PlayerPlaylist to use
 
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index fb3568a4..31443ac9 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -292,6 +292,108 @@ class GrlTrackerWrapper(GObject.GObject):
         self.props.source.query(
             query, self.METADATA_KEYS, options, check_artist_cb)
 
+    def check_album_disc_changes(self, media):
+        """Update album and corediscs model
+
+        :param Grl.Media media: media with the album id
+        """
+        def _check_discs_cb(source, op_id, media, remaining, error):
+            if error:
+                self._log.warning(
+                    "Error on album disc change check: {}".format(error))
+                return
+
+            if not media:
+                corealbum = self._album_ids[album_id]
+                new_discs = [media.get_album_disc_number() for media in medias]
+                discs = [disc.props.disc_nr for disc in corealbum.props.model]
+                changed_discs = set(new_discs) ^ set(discs)
+                album_model = corealbum.props.model.get_model()
+                for disc_nr in changed_discs:
+                    if disc_nr in new_discs:
+                        for media in medias:
+                            if media.get_album_disc_number() == disc_nr:
+                                break
+                        self._log.debug(
+                            "Add Disc {} to album {}".format(
+                                disc_nr, corealbum.props.title))
+                        coredisc = CoreDisc(
+                            media, disc_nr, self._coremodel)
+                        album_model.append(coredisc)
+                    elif disc_nr in discs:
+                        for idx, coredisc in enumerate(album_model):
+                            if coredisc.props.disc_nr == disc_nr:
+                                self._log.debug(
+                                    "Remove disc {} from album {}".format(
+                                        disc_nr, corealbum.props.title))
+                                coredisc = corealbum.props.model[idx]
+                                album_model.remove(idx)
+                                break
+
+                corealbum.update_discs()
+                return
+
+            medias.append(media)
+
+        medias = []
+        album_id = media.get_id()
+        query = """
+        SELECT DISTINCT
+            rdf:type(?song)
+            tracker:id(?album) AS ?id
+            nmm:setNumber(nmm:musicAlbumDisc(?song)) as ?album_disc_number
+        WHERE {
+            ?song a nmm:MusicPiece;
+                    nmm:musicAlbum ?album .
+            FILTER ( tracker:id(?album) = %(album_id)s )
+            %(location_filter)s
+        }
+        ORDER BY ?album_disc_number
+        """.replace("\n", " ").strip() % {
+            "album_id": int(album_id),
+            "location_filter": self._tracker_wrapper.location_filter()
+        }
+
+        options = self._fast_options.copy()
+        self.props.source.query(
+            query, [Grl.METADATA_KEY_ID, Grl.METADATA_KEY_ALBUM_DISC_NUMBER],
+            options, _check_discs_cb)
+
+    def _check_album_content_change(self, song_id):
+        def _check_change_cb(source, op_id, media, remaining, error):
+            if error:
+                self._log.warning(
+                    "Error on album content change check: {}".format(error))
+                return
+
+            if not media:
+                return
+
+            corealbum = self._album_ids.get(media.get_id(), None)
+            if (corealbum is None
+                    or not corealbum.props.model_loaded):
+                return
+
+            self.check_album_disc_changes(media)
+
+        query = """
+        SELECT
+            rdf:type(?album)
+            tracker:id(?album) AS ?id
+        WHERE {
+            ?album a nmm:MusicAlbum .
+            ?song a nmm:MusicPiece ;
+                    nmm:musicAlbum ?album .
+            FILTER ( tracker:id(?song) = %(song_id)s )
+        }
+        """.replace("\n", " ").strip() % {
+            "song_id": song_id
+        }
+
+        options = self._fast_options.copy()
+        self.props.source.query(
+            query, [Grl.METADATA_KEY_ID], options, _check_change_cb)
+
     def _remove_media(self, media_ids):
         for media_id in media_ids:
             try:
@@ -363,6 +465,8 @@ class GrlTrackerWrapper(GObject.GObject):
             else:
                 self._hash[media.get_id()].update(media)
 
+            self._check_album_content_change(media.get_id())
+
         options = self._fast_options.copy()
 
         self.props.source.query(


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