[gnome-music/wip/jfelder/playlists-fixes: 12/12] grltrackerplaylists: Restore smart playlists update



commit d920337afaa9060fd0121fe87069085c4f0c6085
Author: Jean Felder <jfelder src gnome org>
Date:   Tue Feb 18 14:29:10 2020 +0100

    grltrackerplaylists: Restore smart playlists update
    
    This feature was lost during the core rewrite.
    
    Everytime a change is triggered, two smart playlists might need to be
    updated:
     * the PlayerPlaylist (active_playlist)
     * the currently actively viewed playlist
    Besides, a smart playlist needs to be updated every time it becomes
    visible.
    
    An update method is introduced to update a smart playlist content. The
    model of the SmartPlaylist is queried and compared with the previous
    one to perform the correct insert and remove operations.

 gnomemusic/coremodel.py                         |  1 +
 gnomemusic/grilowrappers/grltrackerplaylists.py | 59 +++++++++++++++++++++++++
 gnomemusic/grilowrappers/grltrackerwrapper.py   |  1 +
 gnomemusic/widgets/playlistswidget.py           | 11 +++++
 4 files changed, 72 insertions(+)
---
diff --git a/gnomemusic/coremodel.py b/gnomemusic/coremodel.py
index 37c8f230..4e76c346 100644
--- a/gnomemusic/coremodel.py
+++ b/gnomemusic/coremodel.py
@@ -68,6 +68,7 @@ class CoreModel(GObject.GObject):
     __gsignals__ = {
         "artists-loaded": (GObject.SignalFlags.RUN_FIRST, None, ()),
         "playlist-loaded": (GObject.SignalFlags.RUN_FIRST, None, (int,)),
+        "smart-playlist-change": (GObject.SignalFlags.RUN_FIRST, None, ())
     }
 
     active_playlist = GObject.Property(type=Playlist, default=None)
diff --git a/gnomemusic/grilowrappers/grltrackerplaylists.py b/gnomemusic/grilowrappers/grltrackerplaylists.py
index f63da4af..3aa09f2a 100644
--- a/gnomemusic/grilowrappers/grltrackerplaylists.py
+++ b/gnomemusic/grilowrappers/grltrackerplaylists.py
@@ -257,6 +257,20 @@ class GrlTrackerPlaylists(GObject.GObject):
         self._tracker.update_blank_async(
             query, GLib.PRIORITY_LOW, None, _create_cb, None)
 
+    def check_smart_playlist_change(self):
+        """Check if smart playlists need to be updated.
+
+        A smart playlist needs to be updated in two cases:
+        * it is being played (active_playlist)
+        * it is visible in PlaylistsView
+        """
+        active_playlist = self._coremodel.props.active_playlist
+        if (active_playlist is not None
+                and active_playlist.props.is_smart is True):
+            active_playlist.update()
+        else:
+            self._coremodel.emit("smart-playlist-change")
+
 
 class Playlist(GObject.GObject):
     """ Base class of all playlists """
@@ -729,6 +743,51 @@ class SmartPlaylist(Playlist):
 
         return self._model
 
+    def update(self):
+        """Updates playlist model."""
+        if self._model is None:
+            return
+
+        new_model_medias = []
+
+        def _fill_new_model(source, op_id, media, remaining, error):
+            if error:
+                return
+
+            if not media:
+                self._finish_update(new_model_medias)
+                return
+
+            new_model_medias.append(media)
+
+        options = self._fast_options.copy()
+        self._source.query(
+            self.props.query, self.METADATA_KEYS, options, _fill_new_model)
+
+    def _finish_update(self, new_model_medias):
+        if not new_model_medias:
+            self._model.remove_all()
+            return
+
+        current_models_ids = [coresong.props.media.get_id()
+                              for coresong in self._model]
+        new_model_ids = [media.get_id() for media in new_model_medias]
+
+        idx_to_delete = []
+        for idx, media_id in enumerate(current_models_ids):
+            if media_id not in new_model_ids:
+                idx_to_delete.insert(0, idx)
+
+        for idx in idx_to_delete:
+            self._model.remove(idx)
+            self.props.count -= 1
+
+        for idx, media in enumerate(new_model_medias):
+            if media.get_id() not in current_models_ids:
+                coresong = CoreSong(media, self._coreselection, self._grilo)
+                self._model.append(coresong)
+                self.props.count += 1
+
 
 class MostPlayed(SmartPlaylist):
     """Most Played smart playlist"""
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index fb3568a4..0e3b559a 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -166,6 +166,7 @@ class GrlTrackerWrapper(GObject.GObject):
 
         self._check_album_change()
         self._check_artist_change()
+        self._tracker_playlists.check_smart_playlist_change()
 
         self._batch_changed_media_ids = {}
         self._content_changed_timeout = None
diff --git a/gnomemusic/widgets/playlistswidget.py b/gnomemusic/widgets/playlistswidget.py
index 216269ac..164e9a73 100644
--- a/gnomemusic/widgets/playlistswidget.py
+++ b/gnomemusic/widgets/playlistswidget.py
@@ -75,12 +75,17 @@ class PlaylistsWidget(Gtk.Box):
         playlist_play_action = self._window.lookup_action("playlist_play")
         playlist_play_action.connect("activate", self._on_play_playlist)
 
+        self._coremodel.connect(
+            "smart-playlist-change", self._on_smart_playlist_change)
+
     def _on_current_playlist_changed(self, playlists_view, value):
         """Update view with content from selected playlist"""
         playlist = self._playlists_view.props.current_playlist
 
         self._songs_list.bind_model(
             playlist.props.model, self._create_song_widget, playlist)
+        if playlist.props.is_smart:
+            playlist.update()
 
         self._pl_ctrls.props.playlist = playlist
 
@@ -121,6 +126,12 @@ class PlaylistsWidget(Gtk.Box):
         current_playlist = self._playlists_view.props.current_playlist
         current_playlist.reorder(source_position, target_position)
 
+    def _on_smart_playlist_change(self, coremodel):
+        current_playlist = self._playlists_view.props.current_playlist
+        if (current_playlist is not None
+                and current_playlist.props.is_smart):
+            current_playlist.update()
+
     @Gtk.Template.Callback()
     def _songs_list_right_click(self, gesture, n_press, x, y):
         requested_row = self._songs_list.get_row_at_y(y)


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