[gnome-music/wip/mschraal/core] Rename Grilo sources to wrappers



commit 2e1dde73d7341fa738d509b19f91634566b7b473
Author: Marinus Schraal <mschraal gnome org>
Date:   Sun Jul 14 01:06:35 2019 +0200

    Rename Grilo sources to wrappers
    
    The symbols might clash with Grilo and it is more fitting.

 gnomemusic/coregrilo.py                            |   8 +-
 .../{grldleynasource.py => grldleynawrapper.py}    |   4 +-
 gnomemusic/grilowrappers/grltrackerwrapper.py      | 801 +++++++++++++++++++++
 3 files changed, 807 insertions(+), 6 deletions(-)
---
diff --git a/gnomemusic/coregrilo.py b/gnomemusic/coregrilo.py
index 3ec1368d..cd0ab67b 100644
--- a/gnomemusic/coregrilo.py
+++ b/gnomemusic/coregrilo.py
@@ -2,9 +2,9 @@ import gi
 gi.require_version('Grl', '0.3')
 from gi.repository import Grl, GLib, GObject
 
-# from gnomemusic.grilowrappers.grldleynasource import GrlDLeynaSource
+# from gnomemusic.grilowrappers.grldleynawrapper import GrlDLeynaWrapper
 from gnomemusic.grilowrappers.grlsearchwrapper import GrlSearchWrapper
-from gnomemusic.grilowrappers.grltrackersource import GrlTrackerSource
+from gnomemusic.grilowrappers.grltrackerwrapper import GrlTrackerWrapper
 
 
 class CoreGrilo(GObject.GObject):
@@ -77,11 +77,11 @@ class CoreGrilo(GObject.GObject):
 
         if (source.props.source_id == "grl-tracker-source"
                 and source.props.source_id not in self._wrappers.keys()):
-            new_wrapper = GrlTrackerSource(
+            new_wrapper = GrlTrackerWrapper(
                 source, self._coremodel, self._coreselection, self)
             self._wrappers[source.props.source_id] = new_wrapper
         # elif source.props.source_id[:10] == "grl-dleyna":
-        #     new_wrapper = GrlDLeynaSource(
+        #     new_wrapper = GrlDLeynaWrapper(
         #         source, self._coremodel, self._coreselection, self)
         #     self._wrappers.append(new_wrapper)
             print("wrapper", new_wrapper)
diff --git a/gnomemusic/grilowrappers/grldleynasource.py b/gnomemusic/grilowrappers/grldleynawrapper.py
similarity index 97%
rename from gnomemusic/grilowrappers/grldleynasource.py
rename to gnomemusic/grilowrappers/grldleynawrapper.py
index 12defbb7..5b199806 100644
--- a/gnomemusic/grilowrappers/grldleynasource.py
+++ b/gnomemusic/grilowrappers/grldleynawrapper.py
@@ -5,7 +5,7 @@ from gi.repository import Grl, GObject
 from gnomemusic.coreartist import CoreArtist
 
 
-class GrlDLeynaSource(GObject.GObject):
+class GrlDLeynaWrapper(GObject.GObject):
 
     METADATA_KEYS = [
         Grl.METADATA_KEY_ALBUM,
@@ -26,7 +26,7 @@ class GrlDLeynaSource(GObject.GObject):
     ]
 
     def __repr__(self):
-        return "<GrlDLeynaSource>"
+        return "<GrlDLeynaWrapper>"
 
     def __init__(self, source, coremodel, core_selection, grilo):
         super().__init__()
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
new file mode 100644
index 00000000..6c0d835b
--- /dev/null
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -0,0 +1,801 @@
+import gi
+gi.require_versions({"Gfm": "0.1", "Grl": "0.3", 'Tracker': "2.0"})
+from gi.repository import Gfm, Grl, GLib, GObject, Tracker
+
+from gnomemusic.corealbum import CoreAlbum
+from gnomemusic.coreartist import CoreArtist
+from gnomemusic.coredisc import CoreDisc
+from gnomemusic.coresong import CoreSong
+from gnomemusic.grilowrappers.grltrackerplaylists import GrlTrackerPlaylists
+
+
+class GrlTrackerWrapper(GObject.GObject):
+
+    METADATA_KEYS = [
+        Grl.METADATA_KEY_ALBUM,
+        Grl.METADATA_KEY_ALBUM_ARTIST,
+        Grl.METADATA_KEY_ALBUM_DISC_NUMBER,
+        Grl.METADATA_KEY_ARTIST,
+        Grl.METADATA_KEY_CREATION_DATE,
+        Grl.METADATA_KEY_COMPOSER,
+        Grl.METADATA_KEY_DURATION,
+        Grl.METADATA_KEY_FAVOURITE,
+        Grl.METADATA_KEY_ID,
+        Grl.METADATA_KEY_PLAY_COUNT,
+        Grl.METADATA_KEY_THUMBNAIL,
+        Grl.METADATA_KEY_TITLE,
+        Grl.METADATA_KEY_TRACK_NUMBER,
+        Grl.METADATA_KEY_URL
+    ]
+
+    METADATA_THUMBNAIL_KEYS = [
+        Grl.METADATA_KEY_ID,
+        Grl.METADATA_KEY_THUMBNAIL,
+    ]
+
+    def __repr__(self):
+        return "<GrlTrackerWrapper>"
+
+    def __init__(self, source, coremodel, coreselection, grilo):
+        super().__init__()
+
+        self._coremodel = coremodel
+        self._coreselection = coreselection
+        self._grilo = grilo
+        self._source = source
+        self._model = self._coremodel.props.songs
+        self._albums_model = self._coremodel.props.albums
+        self._album_ids = {}
+        self._artists_model = self._coremodel.props.artists
+        self._artist_ids = {}
+        self._hash = {}
+        self._song_search_proxy = self._coremodel.props.songs_search_proxy
+        self._album_search_model = self._coremodel.props.albums_search
+        self._artist_search_model = self._coremodel.props.artists_search
+
+        self._song_search_tracker = Gfm.FilterListModel.new(self._model)
+        self._song_search_tracker.set_filter_func(lambda a: False)
+        self._song_search_proxy.append(self._song_search_tracker)
+
+        self._fast_options = Grl.OperationOptions()
+        self._fast_options.set_resolution_flags(
+            Grl.ResolutionFlags.FAST_ONLY | Grl.ResolutionFlags.IDLE_RELAY)
+
+        self._initial_fill(self._source)
+        self._initial_albums_fill(self._source)
+        self._initial_artists_fill(self._source)
+
+        self._tracker_playlists = GrlTrackerPlaylists(
+            source, coremodel, coreselection, grilo)
+
+        self._source.notify_change_start()
+        self._source.connect("content-changed", self._on_content_changed)
+
+    @GObject.Property(
+        type=Grl.Source, default=None, flags=GObject.ParamFlags.READABLE)
+    def source(self):
+        return self._source
+
+    def _on_content_changed(self, source, medias, change_type, loc_unknown):
+        for media in medias:
+            if change_type == Grl.SourceChangeType.ADDED:
+                print("ADDED", media.get_id())
+                self._add_media(media)
+                self._check_album_change(media)
+                self._check_artist_change(media)
+            elif change_type == Grl.SourceChangeType.CHANGED:
+                print("CHANGED", media.get_id())
+                self._requery_media(media.get_id(), True)
+            elif change_type == Grl.SourceChangeType.REMOVED:
+                print("REMOVED", media.get_id())
+                self._remove_media(media)
+                self._check_album_change(media)
+                self._check_artist_change(media)
+
+    def _check_album_change(self, media):
+        album_ids = {}
+
+        query = """
+        SELECT
+            rdf:type(?album)
+            tracker:id(?album) AS ?id
+            nie:title(?album) AS ?title
+            ?composer AS ?composer
+            ?album_artist AS ?album_artist
+            nmm:artistName(?performer) AS ?artist
+            YEAR(MAX(nie:contentCreated(?song))) AS ?creation_date
+        {
+            ?album a nmm:MusicAlbum .
+            ?song a nmm:MusicPiece ;
+                    nmm:musicAlbum ?album ;
+                    nmm:performer ?performer .
+            OPTIONAL { ?song nmm:composer/nmm:artistName ?composer . }
+            OPTIONAL { ?album nmm:albumArtist/nmm:artistName ?album_artist . }
+        } GROUP BY ?album
+        """.replace('\n', ' ').strip()
+
+        def check_album_cb(source, op_id, media, user_data, error):
+            if error:
+                print("ERROR", error)
+                return
+
+            if not media:
+                changed_ids = set(
+                    album_ids.keys()) ^ set(self._album_ids.keys())
+                print("ALBUMS CHANGED", changed_ids)
+
+                for key in changed_ids:
+                    if key in album_ids:
+                        self._albums_model.append(album_ids[key])
+                    elif key in self._album_ids:
+                        for idx, corealbum in enumerate(self._albums_model):
+                            if corealbum.media.get_id() == key:
+                                self._albums_model.remove(idx)
+                                break
+
+                self._album_ids = album_ids
+                return
+
+            album = CoreAlbum(media, self._coremodel)
+            album_ids[media.get_id()] = album
+
+        options = self._fast_options.copy()
+
+        self._source.query(
+            query, self.METADATA_KEYS, options, check_album_cb)
+
+    def _check_artist_change(self, media):
+        artist_ids = {}
+
+        query = """
+        SELECT
+            rdf:type(?artist_class)
+            tracker:id(?artist_class) AS ?id
+            nmm:artistName(?artist_class) AS ?artist
+        {
+            ?artist_class a nmm:Artist .
+            ?song a nmm:MusicPiece;
+                    nmm:musicAlbum ?album;
+                    nmm:performer ?artist_class .
+        } GROUP BY ?artist_class
+        """.replace('\n', ' ').strip()
+
+        def check_artist_cb(source, op_id, media, user_data, error):
+            if error:
+                print("ERROR", error)
+                return
+
+            if not media:
+                changed_ids = set(
+                    artist_ids.keys()) ^ set(self._artist_ids.keys())
+                print("ARTISTS CHANGED", changed_ids)
+
+                for key in changed_ids:
+                    if key in artist_ids:
+                        self._artists_model.append(artist_ids[key])
+                    elif key in self._artist_ids:
+                        for idx, coreartist in enumerate(self._artists_model):
+                            if coreartist.media.get_id() == key:
+                                self._artists_model.remove(idx)
+                                break
+
+                self._artist_ids = artist_ids
+                return
+
+            artist = CoreArtist(media, self._coremodel)
+            artist_ids[media.get_id()] = artist
+
+        options = self._fast_options.copy()
+
+        self._source.query(
+            query, self.METADATA_KEYS, options, check_artist_cb)
+
+    def _remove_media(self, media):
+        try:
+            coresong = self._hash.pop(media.get_id())
+        except KeyError:
+            return
+
+        for idx, coresong_model in enumerate(self._model):
+            if coresong_model is coresong:
+                print(
+                    "removing", coresong.props.media.get_id(),
+                    coresong.props.title)
+                self._model.remove(idx)
+                break
+
+    def _requery_media(self, grilo_id, only_update=False):
+        query = """
+            SELECT DISTINCT
+                rdf:type(?song)
+                ?song AS ?tracker_urn
+                nie:title(?song) AS ?title
+                tracker:id(?song) AS ?id
+                ?song
+                nie:url(?song) AS ?url
+                nie:title(?song) AS ?title
+                nmm:artistName(nmm:performer(?song)) AS ?artist
+                nie:title(nmm:musicAlbum(?song)) AS ?album
+                nfo:duration(?song) AS ?duration
+                nie:usageCounter(?song) AS ?play_count
+                nmm:trackNumber(?song) AS ?track_number
+                nmm:setNumber(nmm:musicAlbumDisc(?song)) AS ?album_disc_number
+                ?tag AS ?favourite
+            WHERE {
+                ?song a nmm:MusicPiece .
+                OPTIONAL {
+                    ?song nao:hasTag ?tag .
+                    FILTER (?tag = nao:predefined-tag-favorite)
+                }
+                FILTER ( tracker:id(?song) = %(grilo_id)s )
+            }
+        """.replace('\n', ' ').strip() % {
+            'grilo_id': grilo_id
+        }
+
+        options = self._fast_options.copy()
+
+        if only_update:
+            self._source.query(
+                query, self.METADATA_KEYS, options, self._only_update_media)
+        else:
+            self._source.query(
+                query, self.METADATA_KEYS, options, self._update_media)
+
+    def _add_media(self, media):
+        self._requery_media(media.get_id())
+
+    def _only_update_media(self, source, op_id, media, user_data, error):
+        if error:
+            print("ERROR", error)
+            return
+
+        if not media:
+            return
+
+        print("ONLY UPDATE")
+        self._hash[media.get_id()].update(media)
+        print("UPDATE ID", media.get_id(), media.get_title())
+
+    def _update_media(self, source, op_id, media, user_data, error):
+        if error:
+            print("ERROR", error)
+            return
+
+        if not media:
+            return
+
+        # FIXME: Figure out why we get double additions.
+        if media.get_id() in self._hash.keys():
+            print("ALREADY ADDED")
+            return
+
+        song = CoreSong(media, self._coreselection, self._grilo)
+        self._model.append(song)
+        self._hash[media.get_id()] = song
+
+        print("UPDATE ID", media.get_id(), media.get_title())
+
+    def _on_source_removed(self, registry, source):
+        print("removed", source.props.source_id)
+
+    def _initial_fill(self, source):
+        options = self._fast_options.copy()
+        query = """
+            SELECT
+                rdf:type(?song)
+                ?song AS ?tracker_urn
+                nie:title(?song) AS ?title
+                tracker:id(?song) AS ?id
+                ?song
+                nie:url(?song) AS ?url
+                nie:title(?song) AS ?title
+                nmm:artistName(nmm:performer(?song)) AS ?artist
+                nie:title(nmm:musicAlbum(?song)) AS ?album
+                nfo:duration(?song) AS ?duration
+                nie:usageCounter(?song) AS ?play_count
+                nmm:trackNumber(?song) AS ?track_number
+                nmm:setNumber(nmm:musicAlbumDisc(?song)) AS ?album_disc_number
+                ?tag AS ?favourite
+            WHERE {
+                ?song a nmm:MusicPiece .
+                OPTIONAL {
+                    ?song nao:hasTag ?tag .
+                    FILTER (?tag = nao:predefined-tag-favorite)
+                }
+            }
+        """.replace('\n', ' ').strip()
+
+        self._source.query(
+            query, self.METADATA_KEYS, options, self._add_to_model)
+
+    def _add_to_model(self, source, op_id, media, user_data, error):
+        if error:
+            print("ERROR", error)
+            return
+
+        if not media:
+            return
+
+        song = CoreSong(media, self._coreselection, self._grilo)
+        self._model.append(song)
+        self._hash[media.get_id()] = song
+
+    def _initial_albums_fill(self, source):
+        query = """
+        SELECT
+            rdf:type(?album)
+            tracker:id(?album) AS ?id
+            nie:title(?album) AS ?title
+            ?composer AS ?composer
+            ?album_artist AS ?album_artist
+            nmm:artistName(?performer) AS ?artist
+            YEAR(MAX(nie:contentCreated(?song))) AS ?creation_date
+        {
+            ?album a nmm:MusicAlbum .
+            ?song a nmm:MusicPiece ;
+                    nmm:musicAlbum ?album ;
+                    nmm:performer ?performer .
+            OPTIONAL { ?song nmm:composer/nmm:artistName ?composer . }
+            OPTIONAL { ?album nmm:albumArtist/nmm:artistName ?album_artist . }
+        } GROUP BY ?album
+        """.replace('\n', ' ').strip()
+
+        options = self._fast_options.copy()
+
+        source.query(
+            query, self.METADATA_KEYS, options, self._add_to_albums_model)
+
+    def _add_to_albums_model(self, source, op_id, media, user_data, error):
+        if error:
+            print("ERROR", error)
+            return
+
+        if not media:
+            print("NO MEDIA", source, op_id, media, error)
+            return
+
+        album = CoreAlbum(media, self._coremodel)
+        self._albums_model.append(album)
+        self._album_ids[media.get_id()] = album
+
+    def _initial_artists_fill(self, source):
+        query = """
+        SELECT
+            rdf:type(?artist_class)
+            tracker:id(?artist_class) AS ?id
+            nmm:artistName(?artist_class) AS ?artist
+        {
+            ?artist_class a nmm:Artist .
+            ?song a nmm:MusicPiece;
+                    nmm:musicAlbum ?album;
+                    nmm:performer ?artist_class .
+        } GROUP BY ?artist_class
+        """.replace('\n', ' ').strip()
+
+        options = self._fast_options.copy()
+
+        source.query(
+            query, self.METADATA_KEYS, options, self._add_to_artists_model)
+
+    def _add_to_artists_model(self, source, op_id, media, user_data, error):
+        if error:
+            print("ERROR", error)
+            return
+
+        if not media:
+            print("NO MEDIA", source, op_id, media, error)
+            self._coremodel.emit("artists-loaded")
+            return
+
+        artist = CoreArtist(media, self._coremodel)
+        self._artists_model.append(artist)
+        self._artist_ids[media.get_id()] = artist
+
+    def get_artist_albums(self, media, model):
+        artist_id = media.get_id()
+
+        query = """
+        SELECT DISTINCT
+            rdf:type(?album)
+            tracker:id(?album) AS ?id
+            nie:title(?album) AS ?title
+        WHERE
+        {
+            ?album a nmm:MusicAlbum .
+            OPTIONAL { ?album  nmm:albumArtist ?album_artist . }
+            ?song a nmm:MusicPiece;
+                    nmm:musicAlbum ?album;
+                    nmm:performer ?artist .
+            FILTER ( tracker:id(?album_artist) = %(artist_id)s
+                     || tracker:id(?artist) = %(artist_id)s )
+        }
+        """.replace('\n', ' ').strip() % {
+            'artist_id': int(artist_id)
+        }
+
+        options = self._fast_options.copy()
+
+        albums = []
+
+        def query_cb(source, op_id, media, user_data, error):
+            if error:
+                print("ERROR", error)
+                return
+
+            if not media:
+                model.set_filter_func(albums_filter, albums)
+                return
+
+            albums.append(media)
+
+        def albums_filter(corealbum, albums):
+            for media in albums:
+                if media.get_id() == corealbum.props.media.get_id():
+                    return True
+
+            return False
+
+        self._source.query(query, self.METADATA_KEYS, options, query_cb)
+
+    def get_album_discs(self, media, disc_model):
+        album_id = media.get_id()
+
+        def _disc_nr_cb(source, op_id, media, user_data, error):
+            if error:
+                print("ERROR", error)
+                return
+
+            if not media:
+                return
+
+            disc_nr = media.get_album_disc_number()
+            coredisc = CoreDisc(media, disc_nr, self._coremodel)
+            disc_model.append(coredisc)
+
+        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 )
+        }
+        """.replace('\n', ' ').strip() % {
+            'album_id': int(album_id)
+        }
+
+        options = self._fast_options.copy()
+
+        self._source.query(query, self.METADATA_KEYS, options, _disc_nr_cb)
+
+    def populate_album_disc_songs(self, media, disc_nr, _callback):
+        album_id = media.get_id()
+
+        query = """
+        SELECT DISTINCT
+            rdf:type(?song)
+            ?song AS ?tracker_urn
+            tracker:id(?song) AS ?id
+            nie:url(?song) AS ?url
+            nie:title(?song) AS ?title
+            nmm:artistName(nmm:performer(?song)) AS ?artist
+            nie:title(nmm:musicAlbum(?song)) AS ?album
+            nfo:duration(?song) AS ?duration
+            nmm:trackNumber(?song) AS ?track_number
+            nmm:setNumber(nmm:musicAlbumDisc(?song)) AS ?album_disc_number
+            ?tag AS ?favourite
+            nie:usageCounter(?song) AS ?play_count
+        WHERE
+        {
+            ?song a nmm:MusicPiece ;
+                    nmm:musicAlbum ?album .
+            OPTIONAL { ?song nao:hasTag ?tag .
+                       FILTER (?tag = nao:predefined-tag-favorite) } .
+            FILTER ( tracker:id(?album) = %(album_id)s
+                     && nmm:setNumber(nmm:musicAlbumDisc(?song)) = %(disc_nr)s
+                   )
+        }
+        """.replace('\n', ' ').strip() % {
+            'album_id': album_id,
+            'disc_nr': disc_nr,
+        }
+
+        options = self._fast_options.copy()
+
+        self._source.query(query, self.METADATA_KEYS, options, _callback)
+
+    def populate_album_songs(self, media, _callback):
+        album_id = media.get_id()
+
+        query = """
+        SELECT DISTINCT
+            rdf:type(?song)
+            ?song AS ?tracker_urn
+            tracker:id(?song) AS ?id
+            nie:url(?song) AS ?url
+            nie:title(?song) AS ?title
+            nmm:artistName(nmm:performer(?song)) AS ?artist
+            nie:title(nmm:musicAlbum(?song)) AS ?album
+            nfo:duration(?song) AS ?duration
+            nmm:trackNumber(?song) AS ?track_number
+            nmm:setNumber(nmm:musicAlbumDisc(?song)) AS ?album_disc_number
+            ?tag AS ?favourite
+            nie:usageCounter(?song) AS ?play_count
+        WHERE
+        {
+            ?song a nmm:MusicPiece ;
+                    nmm:musicAlbum ?album .
+            OPTIONAL { ?song nao:hasTag ?tag .
+                       FILTER (?tag = nao:predefined-tag-favorite) } .
+            FILTER ( tracker:id(?album) = %(album_id)s )
+        }
+        """.replace('\n', ' ').strip() % {
+            'album_id': album_id,
+        }
+
+        options = self._fast_options.copy()
+
+        self._source.query(query, self.METADATA_KEYS, options, _callback)
+
+    def search(self, text):
+        term = Tracker.sparql_escape_string(
+            GLib.utf8_normalize(
+                GLib.utf8_casefold(text, -1), -1, GLib.NormalizeMode.NFKD))
+
+        query = """
+        SELECT DISTINCT
+            rdf:type(?song)
+            tracker:id(?song) AS ?id
+        WHERE {
+            ?song a nmm:MusicPiece .
+            BIND(tracker:normalize(
+                nie:title(nmm:musicAlbum(?song)), 'nfkd') AS ?match1) .
+            BIND(tracker:normalize(
+                nmm:artistName(nmm:performer(?song)), 'nfkd') AS ?match2) .
+            BIND(tracker:normalize(
+                nie:title(?song), 'nfkd') AS ?match3) .
+            BIND(
+                tracker:normalize(nmm:composer(?song), 'nfkd') AS ?match4) .
+            FILTER (
+                CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match1)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match1), "%(name)s")
+                || CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match2)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match2), "%(name)s")
+                || CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match3)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match3), "%(name)s")
+                || CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match4)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match4), "%(name)s")
+            )
+        }
+        """.replace('\n', ' ').strip() % {'name': term}
+
+        filter_ids = []
+
+        def songs_filter(coresong):
+            return coresong.media.get_id() in filter_ids
+
+        def search_cb(source, op_id, media, data, error):
+            if error:
+                print("ERROR", error)
+                return
+
+            if not media:
+                self._song_search_tracker.set_filter_func(songs_filter)
+                return
+
+            filter_ids.append(media.get_id())
+
+        options = self._fast_options.copy()
+
+        self._source.query(query, self.METADATA_KEYS, options, search_cb)
+
+        # Album search
+
+        query = """
+        SELECT DISTINCT
+            rdf:type(nmm:musicAlbum(?song))
+            tracker:id(nmm:musicAlbum(?song)) AS ?id
+        {
+            ?song a nmm:MusicPiece .
+            BIND(tracker:normalize(
+                nie:title(nmm:musicAlbum(?song)), 'nfkd') AS ?match1) .
+            BIND(tracker:normalize(
+                nmm:artistName(nmm:performer(?song)), 'nfkd') AS ?match2) .
+            BIND(tracker:normalize(nie:title(?song), 'nfkd') AS ?match3) .
+            BIND(tracker:normalize(nmm:composer(?song), 'nfkd') AS ?match4) .
+            FILTER (
+                CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match1)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match1), "%(name)s")
+                || CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match2)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match2), "%(name)s")
+                || CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match3)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match3), "%(name)s")
+                || CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match4)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match4), "%(name)s")
+            )
+        }
+        """.replace('\n', ' ').strip() % {'name': term}
+
+        album_filter_ids = []
+
+        def album_filter(corealbum):
+            return corealbum.media.get_id() in album_filter_ids
+
+        def albums_search_cb(source, op_id, media, data, error):
+            if error:
+                print("ERROR", error)
+                return
+
+            if not media:
+                self._album_search_model.set_filter_func(album_filter)
+                return
+
+            album_filter_ids.append(media.get_id())
+
+        options = self._fast_options.copy()
+
+        self._source.query(
+            query, self.METADATA_KEYS, options, albums_search_cb)
+
+        # Artist search
+
+        query = """
+        SELECT DISTINCT
+            rdf:type(?artist)
+            tracker:id(?artist) AS ?id
+        {
+            ?song a nmm:MusicPiece ;
+                    nmm:musicAlbum ?album ;
+                    nmm:performer ?artist .
+            BIND(tracker:normalize(
+                nie:title(nmm:musicAlbum(?song)), 'nfkd') AS ?match1) .
+            BIND(tracker:normalize(
+                nmm:artistName(nmm:performer(?song)), 'nfkd') AS ?match2) .
+            BIND(tracker:normalize(nie:title(?song), 'nfkd') AS ?match3) .
+            BIND(tracker:normalize(nmm:composer(?song), 'nfkd') AS ?match4) .
+            FILTER (
+                CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match1)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match1), "%(name)s")
+                || CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match2)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match2), "%(name)s")
+                || CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match3)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match3), "%(name)s")
+                || CONTAINS(tracker:case-fold(
+                    tracker:unaccent(?match4)), "%(name)s")
+                || CONTAINS(tracker:case-fold(?match4), "%(name)s")
+            )
+        }
+        """.replace('\n', ' ').strip() % {'name': term}
+
+        artist_filter_ids = []
+
+        def artist_filter(coreartist):
+            return coreartist.media.get_id() in artist_filter_ids
+
+        def artist_search_cb(source, op_id, media, data, error):
+            if error:
+                print("ERROR", error)
+                return
+
+            if not media:
+                self._artist_search_model.set_filter_func(artist_filter)
+                return
+
+            artist_filter_ids.append(media.get_id())
+
+        options = self._fast_options.copy()
+
+        self._source.query(
+            query, self.METADATA_KEYS, options, artist_search_cb)
+
+    def get_album_art_for_item(self, coresong, callback):
+        item_id = coresong.props.media.get_id()
+
+        if coresong.props.media.is_audio():
+            query = self._get_album_for_song_id(item_id)
+        else:
+            query = self._get_album_for_album_id(item_id)
+
+        full_options = Grl.OperationOptions()
+        full_options.set_resolution_flags(
+            Grl.ResolutionFlags.FULL
+            | Grl.ResolutionFlags.IDLE_RELAY)
+        full_options.set_count(1)
+
+        self.search_source.query(
+            query, self.METADATA_THUMBNAIL_KEYS, full_options, callback)
+
+    @staticmethod
+    def _get_album_for_album_id(album_id):
+        # Even though we check for the album_artist, we fill
+        # the artist key, since Grilo coverart plugins use
+        # only that key for retrieval.
+        query = """
+        SELECT DISTINCT
+            rdf:type(?album)
+            tracker:id(?album) AS ?id
+            tracker:coalesce(nmm:artistName(?album_artist),
+                             nmm:artistName(?song_artist)) AS ?artist
+            nie:title(?album) AS ?album
+        WHERE {
+            ?album a nmm:MusicAlbum .
+            ?song a nmm:MusicPiece ;
+                    nmm:musicAlbum ?album ;
+                    nmm:performer ?song_artist .
+            OPTIONAL { ?album nmm:albumArtist ?album_artist . }
+            FILTER (
+                tracker:id(?album) = %(album_id)s
+            )
+        }
+        """.replace("\n", " ").strip() % {
+                'album_id': album_id,
+        }
+
+        return query
+
+    @staticmethod
+    def _get_album_for_song_id(song_id):
+        # See get_album_for_album_id comment.
+        query = """
+        SELECT DISTINCT
+            rdf:type(?album)
+            tracker:id(?album) AS ?id
+            tracker:coalesce(nmm:artistName(?album_artist),
+                             nmm:artistName(?song_artist)) AS ?artist
+            nie:title(?album) AS ?album
+        WHERE {
+            ?song a nmm:MusicPiece ;
+                    nmm:musicAlbum ?album ;
+                    nmm:performer ?song_artist .
+            OPTIONAL { ?album nmm:albumArtist ?album_artist . }
+            FILTER (
+                tracker:id(?song) = %(song_id)s
+            )
+            FILTER (
+                NOT EXISTS { ?song a nmm:Video }
+                && NOT EXISTS { ?song a nmm:Playlist }
+            )
+        }
+        """.replace("\n", " ").strip() % {
+                'song_id': song_id
+        }
+
+        return query
+
+    def stage_playlist_deletion(self, playlist):
+        """Prepares playlist deletion.
+
+        :param Playlist playlist: playlist
+        """
+        self._tracker_playlists.stage_playlist_deletion(playlist)
+
+    def finish_playlist_deletion(self, playlist, deleted):
+        """Finishes playlist deletion.
+
+        :param Playlist playlist: playlist
+        :param bool deleted: indicates if the playlist has been deleted
+        """
+        self._tracker_playlists.finish_playlist_deletion(playlist, deleted)
+
+    def create_playlist(self, playlist_title, callback):
+        """Creates a new user playlist.
+
+        :param str playlist_title: playlist title
+        :param callback: function to perform once, the playlist is created
+        """
+        self._tracker_playlists.create_playlist(playlist_title, callback)


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