[gnome-music/wip/jfelder/playlists-core-rewrite-prep-work: 5/19] playlists: Dynamically instantiate the smart playlists



commit 76cf0a5e800aed4701ebc32aa70e63af39b6aa40
Author: Jean Felder <jfelder src gnome org>
Date:   Mon Jul 1 21:28:26 2019 +0200

    playlists: Dynamically instantiate the smart playlists
    
    This commit makes the smart playlists be allocated when the
    Playlists class is instantiated. This allows to correctly emit the
    smart playlists only once.
    
    It is based on some work done by Georges Basile Stavracas Neto.

 gnomemusic/playlists.py                 | 153 ++++++++++++++++++--------------
 gnomemusic/widgets/songwidget.py        |   5 +-
 gnomemusic/widgets/starhandlerwidget.py |   5 +-
 3 files changed, 92 insertions(+), 71 deletions(-)
---
diff --git a/gnomemusic/playlists.py b/gnomemusic/playlists.py
index a87c4329..6a5260d3 100644
--- a/gnomemusic/playlists.py
+++ b/gnomemusic/playlists.py
@@ -32,7 +32,6 @@ from gi.repository import Grl, GLib, GObject
 from gnomemusic.grilo import grilo
 from gnomemusic.query import Query
 from gettext import gettext as _
-import inspect
 
 from gnomemusic import log
 import logging
@@ -59,62 +58,64 @@ class Playlist(GObject.Object):
         self.props.title = title
 
 
-class SmartPlaylists:
+class MostPlayed(Playlist):
+    """Most Played smart playlist"""
 
-    def __repr__(self):
-        return '<SmartPlaylists>'
+    def __init__(self):
+        super().__init__()
 
-    class MostPlayed(Playlist):
-        TAG_TEXT = "MOST_PLAYED"
+        self.props.tag_text = "MOST_PLAYED"
         # TRANSLATORS: this is a playlist name
-        TITLE = _("Most Played")
+        self.props.title = _("Most Played")
+        self.props.query = Query.get_never_played_songs()
 
-    class NeverPlayed(Playlist):
-        TAG_TEXT = "NEVER_PLAYED"
-        # TRANSLATORS: this is a playlist name
-        TITLE = _("Never Played")
 
-    class RecentlyPlayed(Playlist):
-        TAG_TEXT = "RECENTLY_PLAYED"
-        # TRANSLATORS: this is a playlist name
-        TITLE = _("Recently Played")
+class NeverPlayed(Playlist):
+    """Never Played smart playlist"""
 
-    class RecentlyAdded(Playlist):
-        TAG_TEXT = "RECENTLY_ADDED"
+    def __init__(self):
+        super().__init__()
+
+        self.props.tag_text = "NEVER_PLAYED"
         # TRANSLATORS: this is a playlist name
-        TITLE = _("Recently Added")
+        self.props.title = _("Never Played")
+        self.props.query = Query.get_never_played_songs()
+
 
-    class Favorites(Playlist):
-        TAG_TEXT = "FAVORITES"
+class RecentlyPlayed(Playlist):
+    """Recently Played smart playlist"""
+
+    def __init__(self):
+        super().__init__()
+
+        self.props.tag_text = "RECENTLY_PLAYED"
         # TRANSLATORS: this is a playlist name
-        TITLE = _("Favorite Songs")
+        self.props.title = _("Recently Played")
+        self.props.query = Query.get_recently_played_songs()
+
+
+class RecentlyAdded(Playlist):
+    """Recently Added smart playlist"""
 
     def __init__(self):
-        Query()
-        self.MostPlayed.QUERY = Query.get_most_played_songs()
-        self.NeverPlayed.QUERY = Query.get_never_played_songs()
-        self.RecentlyPlayed.QUERY = Query.get_recently_played_songs()
-        self.RecentlyAdded.QUERY = Query.get_recently_added_songs()
-        self.Favorites.QUERY = Query.get_favorite_songs()
-
-    @staticmethod
-    def get_ids():
-        """Get all smart playlist IDs
-
-        :return: A list of tracker.id's
-        :rtype: A list of integers
-        """
-        return [str(playlist.ID) for playlist in SmartPlaylists.get_all()]
+        super().__init__()
 
-    @staticmethod
-    def get_all():
-        """Get all smart playlist classes
+        self.props.tag_text = "RECENTLY_ADDED"
+        # TRANSLATORS: this is a playlist name
+        self.props.title = _("Recently Added")
+        self.props.query = Query.get_recently_added_songs()
 
-        :return: All SmartPlaylists innerclasses
-        :rtype: A list of classes
-        """
-        return [cls for name, cls in inspect.getmembers(SmartPlaylists)
-                if inspect.isclass(cls) and not name == "__class__"]
+
+class Favorites(Playlist):
+    """Favorites smart playlist"""
+
+    def __init__(self):
+        super().__init__()
+
+        self.props.tag_text = "FAVORITES"
+        # TRANSLATORS: this is a playlist name
+        self.props.title = _("Favorite Songs")
+        self.props.query = Query.get_favorite_songs()
 
 
 class Playlists(GObject.GObject):
@@ -127,9 +128,7 @@ class Playlists(GObject.GObject):
         'playlist-deleted': (
             GObject.SignalFlags.RUN_FIRST, None, (str,)
         ),
-        'playlist-updated': (
-            GObject.SignalFlags.RUN_FIRST, None, (int,)
-        ),
+        "playlist-updated": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
         'playlist-renamed': (
             GObject.SignalFlags.RUN_FIRST, None, (Grl.Media,)
         ),
@@ -156,15 +155,23 @@ class Playlists(GObject.GObject):
     def __init__(self):
         super().__init__()
 
-        self._smart_playlists = SmartPlaylists()
+        Query()
+        self._smart_playlists = {
+            "MostPlayed": MostPlayed(),
+            "NeverPlayed": NeverPlayed(),
+            "RecentlyPlayed": RecentlyPlayed(),
+            "RecentlyAdded": RecentlyAdded(),
+            "Favorites": Favorites()
+        }
+
         self._pls_todelete = {}
 
         grilo.connect('ready', self._on_grilo_ready)
 
     @log
     def _on_grilo_ready(self, data=None):
-        """For all smart playlists: get ID, if exists; if not, create
-        the playlist and get ID."""
+        """For all smart playlists: get id, if exists; if not, create
+        the playlist and get id."""
 
         def playlist_id_fetched_cb(cursor, res, playlist):
             """ Called after the playlist id is fetched """
@@ -174,9 +181,9 @@ class Playlists(GObject.GObject):
                 logger.warning("Error: {}, {}".format(err.__class__, err))
                 return
 
-            playlist.ID = cursor.get_integer(1)
+            playlist.props.pl_id = cursor.get_integer(1)
 
-            if not playlist.ID:
+            if not playlist.props.pl_id:
                 # Create the smart playlist
                 self._create_smart_playlist(playlist)
             else:
@@ -191,14 +198,14 @@ class Playlists(GObject.GObject):
                 logger.warning("Error: {}, {}".format(err.__class__, err))
                 return
 
-            # Search for the playlist ID
+            # Search for the playlist id
             cursor.next_async(None, playlist_id_fetched_cb, playlist)
 
         self._tracker = grilo.tracker_sparql
         # Start fetching all the smart playlists
-        for playlist in self._smart_playlists.get_all():
+        for playlist in self._smart_playlists.values():
             self._tracker.query_async(
-                Query.get_playlist_with_tag(playlist.TAG_TEXT), None,
+                Query.get_playlist_with_tag(playlist.props.tag_text), None,
                 callback, playlist)
 
     @log
@@ -206,26 +213,26 @@ class Playlists(GObject.GObject):
         """ Create the tag and the smart playlist, and fetch the newly created
         playlist's songs.
         """
-        title = playlist.TITLE
-        tag_text = playlist.TAG_TEXT
+        title = playlist.props.title
+        tag_text = playlist.props.tag_text
 
         def playlist_next_async_cb(cursor, res, playlist):
             """ Called after we finished moving the Tracker cursor, and ready
             to retrieve the playlist id"""
-            # Update the playlist ID
+            # Update the playlist id
             try:
                 cursor.next_finish(res)
             except GLib.Error as err:
                 logger.warning("Error: {}, {}".format(err.__class__, err))
                 return
 
-            playlist.ID = cursor.get_integer(0)
+            playlist.props.pl_id = cursor.get_integer(0)
 
             # Fetch the playlist contents
             self.update_smart_playlist(playlist)
 
         def playlist_queried_cb(obj, res, playlist):
-            """ Called after the playlist is created and the ID is fetched """
+            """ Called after the playlist is created and the id is fetched """
             try:
                 cursor = obj.query_finish(res)
             except GLib.Error as err:
@@ -267,7 +274,7 @@ class Playlists(GObject.GObject):
         :param SmartPlaylist playlist: playlist to update
         """
         # Clear the smart playlist and then repopulate it
-        query = Query.clear_playlist_with_id(playlist.ID)
+        query = Query.clear_playlist_with_id(playlist.props.pl_id)
         self._tracker.update_async(
             query, GLib.PRIORITY_LOW, None, self._smart_playlist_cleared_cb,
             playlist)
@@ -277,7 +284,8 @@ class Playlists(GObject.GObject):
         """After clearing the playlist, start querying the playlist's songs"""
         # Get a list of matching songs
         self._tracker.query_async(
-            playlist.QUERY, None, self._smart_playlist_query_cb, playlist)
+            playlist.props.query, None, self._smart_playlist_query_cb,
+            playlist)
 
     @log
     def _smart_playlist_query_cb(self, connection, res, playlist):
@@ -301,7 +309,8 @@ class Playlists(GObject.GObject):
             # Only perform the update when the cursor reached the end
             if has_next:
                 uri = cursor.get_string(0)[0]
-                final_query += Query.add_song_to_playlist(playlist.ID, uri)
+                final_query += Query.add_song_to_playlist(
+                    playlist.props.pl_id, uri)
 
                 cursor.next_async(None, callback, final_query)
             else:
@@ -314,11 +323,11 @@ class Playlists(GObject.GObject):
 
     @log
     def _smart_playlist_update_finished(self, source, res, smart_playlist):
-        self.emit('playlist-updated', smart_playlist.ID)
+        self.emit('playlist-updated', smart_playlist.props.pl_id)
 
     @log
     def update_all_smart_playlists(self):
-        for playlist in self._smart_playlists.get_all():
+        for playlist in self._smart_playlists.values():
             self.update_smart_playlist(playlist)
 
     @log
@@ -452,6 +461,16 @@ class Playlists(GObject.GObject):
                 Query.change_song_position(playlist_id, item_id, new_position),
                 GLib.PRIORITY_LOW, None, update_callback, item)
 
+    @log
+    def get_smart_playlist(self, name):
+        """SmartPlaylist getter
+
+        :param str name: smart playlist name
+        :returns: Smart Playlist
+        :rtype: Playlist
+        """
+        return self._smart_playlists[name]
+
     @log
     def is_smart_playlist(self, playlist):
         """Checks whether the given playlist is smart or not
@@ -459,8 +478,8 @@ class Playlists(GObject.GObject):
         :return: True if the playlist is smart
         :rtype: bool
         """
-        for smart_playlist_id in self._smart_playlists.get_ids():
-            if playlist.get_id() == smart_playlist_id:
+        for smart_playlist in self._smart_playlists.values():
+            if playlist.get_id() == smart_playlist.props.pl_id:
                 return True
 
         return False
diff --git a/gnomemusic/widgets/songwidget.py b/gnomemusic/widgets/songwidget.py
index 78c87181..f3310d94 100644
--- a/gnomemusic/widgets/songwidget.py
+++ b/gnomemusic/widgets/songwidget.py
@@ -32,7 +32,7 @@ from gi.repository.Dazzle import BoldingLabel  # noqa: F401
 from gnomemusic import log
 from gnomemusic import utils
 from gnomemusic.grilo import grilo
-from gnomemusic.playlists import Playlists, SmartPlaylists
+from gnomemusic.playlists import Playlists
 from gnomemusic.widgets.starimage import StarImage  # noqa: F401
 
 
@@ -143,7 +143,8 @@ class SongWidget(Gtk.EventBox):
 
         # TODO: Rework and stop updating widgets from here directly.
         grilo.set_favorite(self._media, favorite)
-        self._playlists.update_smart_playlist(SmartPlaylists.Favorites)
+        favorite_playlist = self._playlists.get_smart_playlist("Favorites")
+        self._playlists.update_smart_playlist(favorite_playlist)
 
         return True
 
diff --git a/gnomemusic/widgets/starhandlerwidget.py b/gnomemusic/widgets/starhandlerwidget.py
index f9698598..e1745602 100644
--- a/gnomemusic/widgets/starhandlerwidget.py
+++ b/gnomemusic/widgets/starhandlerwidget.py
@@ -26,7 +26,7 @@ from gi.repository import GObject, Gtk
 
 from gnomemusic import log
 from gnomemusic.grilo import grilo
-from gnomemusic.playlists import Playlists, SmartPlaylists
+from gnomemusic.playlists import Playlists
 
 playlists = Playlists.get_default()
 
@@ -152,7 +152,8 @@ class StarHandlerWidget(object):
         self._parent.model[_iter][self._star_index] = new_value
         song_item = self._parent.model[_iter][5]
         grilo.toggle_favorite(song_item)
-        playlists.update_smart_playlist(SmartPlaylists.Favorites)
+        favorite_playlist = playlists.get_smart_playlist("Favorites")
+        playlists.update_smart_playlist(favorite_playlist)
 
         # Use this flag to ignore the upcoming _on_item_activated call
         self.star_renderer_click = True


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