Example #1
0
    def get_songs_with_any_match(name):
        name = Tracker.sparql_escape_string(
            GLib.utf8_normalize(GLib.utf8_casefold(name, -1), -1,
                                GLib.NormalizeMode.NFKD))
        query = """
            {
                SELECT DISTINCT
                    ?song
                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) .
                    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")
                    )
                }
            }
            """.replace('\n', ' ').strip() % {
            'name': name
        }

        return Query.songs(query)
Example #2
0
    def get_songs_with_any_match(name):
        name = Tracker.sparql_escape_string(GLib.utf8_normalize(GLib.utf8_casefold(name, -1), -1, GLib.NormalizeMode.NFKD))
        query = """
            {
                SELECT DISTINCT
                    ?song
                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': name}

        return Query.songs(query)
Example #3
0
    def _normalize_and_hash(self, input_str):
        normalized = " "

        if input_str and len(input_str) > 0:
            normalized = self._strip_invalid_entities(input_str)
            normalized = GLib.utf8_normalize(normalized, -1, GLib.NormalizeMode.NFKD)
            normalized = normalized.lower()

        return GLib.compute_checksum_for_string(GLib.ChecksumType.MD5, normalized, -1)
Example #4
0
    def _normalize_and_hash(self, input_str):
        normalized = ' '

        if input_str and len(input_str) > 0:
            normalized = self._strip_invalid_entities(input_str)
            normalized = GLib.utf8_normalize(normalized, -1,
                                             GLib.NormalizeMode.NFKD)
            normalized = normalized.lower()

        return GLib.compute_checksum_for_string(GLib.ChecksumType.MD5,
                                                normalized, -1)
Example #5
0
    def normalizeAndHash(self, input_str):
        normalized = " "

        if input_str is not None and input_str.length() > 0:
            normalized = self.stripInvalidEntities(input_str)
            normalized = GLib.utf8_normalize(normalized, -1,
                                             GLib.NormalizeMode.NFKD)
            normalized = normalized.toLowerCase()

        return GLib.compute_checksum_for_string(GLib.ChecksumType.MD5,
                                                normalized, -1)
Example #6
0
def string_for_search(s):
    return GLib.utf8_casefold(GLib.utf8_normalize(s, -1, GLib.NormalizeMode.ALL), -1)
Example #7
0
# DO NOT MODIFY IT!
# See locale/README.md to know how to import dictionaries

"""

# Open UnicodeData.txt
with open(unicodeDataPath, "r", newline='') as uDataFile:
    uDataCSV = csv.reader(uDataFile, delimiter=';')
    fontVariantsDict = SymbolsDictionary("literal")
    for uCharacter in uDataCSV:
        # The 6th field starts with <font> code for all font variants
        # So get the code after <font> and the first field content to have the character and the one to pronounce
        if uCharacter[5].startswith("<font>"):
            fontVariant = chr(int("0x" + uCharacter[0], 16))
            pronouncedCharacter = chr(int("0x" + uCharacter[5][7:], 16))
            if pronouncedCharacter != GLib.utf8_normalize(
                    fontVariant, -1, GLib.NormalizeMode.ALL_COMPOSE):
                fontVariantsDict[fontVariant] = pronouncedCharacter

# Write the dictionary to en/font-variants.dic with documentation at the top
with codecs.open("base/font-variants.dic", "w", "utf_8_sig",
                 errors="replace") as dicFile:
    dicFile.write(docHeader)
    dicFile.write(str(fontVariantsDict))

# Copy all NVDA symbols dictionaries
# Browse the "locale" directory, check if item is a directory and if it contains a symbols.dic file to copy it fter creating this directory in Speech-Dispatcher if not exists
NVDALocale = path.join(NVDADir, "source", "locale")
for NVDALang in os.listdir(NVDALocale):
    langPath = path.join(NVDALocale, NVDALang)
    if not path.isdir(langPath):
        continue
Example #8
0
    def search(self, text):
        # FIXME: Searches are limited to not bog down the UI with
        # widget creation ({List,Flow}Box limitations). The limit is
        # arbitrarily set to 50 and set in the Tracker query. It should
        # be possible to set it through Grilo options instead. This
        # does not work as expected and needs further investigation.
        term = Tracker.sparql_escape_string(
            GLib.utf8_normalize(GLib.utf8_casefold(text, -1), -1,
                                GLib.NormalizeMode.NFKD))

        # Artist search

        query = """
        SELECT DISTINCT
            rdf:type(?artist)
            tracker:id(?artist) AS ?id
        WHERE {
            ?song a nmm:MusicPiece ;
                    nmm:musicAlbum ?album ;
                    nmm:performer ?artist .
            BIND(tracker:normalize(
                nmm:artistName(nmm:albumArtist(?album)), 'nfkd') AS ?match1) .
            BIND(tracker:normalize(
                nmm:artistName(nmm:performer(?song)), 'nfkd') AS ?match2) .
            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(?match4)), "%(name)s")
                || CONTAINS(tracker:case-fold(?match4), "%(name)s")
            )
            %(location_filter)s
        }
        LIMIT 50
        """.replace('\n', ' ').strip() % {
            'location_filter': self._location_filter(),
            '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)

        # Album search

        query = """
        SELECT DISTINCT
            rdf:type(nmm:musicAlbum(?song))
            tracker:id(nmm:musicAlbum(?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(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(?match4)), "%(name)s")
                || CONTAINS(tracker:case-fold(?match4), "%(name)s")
            )
            %(location_filter)s
        }
        LIMIT 50
        """.replace('\n', ' ').strip() % {
            'location_filter': self._location_filter(),
            '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)

        # Song search

        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")
            )
            %(location_filter)s
        }
        LIMIT 50
        """.replace('\n', ' ').strip() % {
            'location_filter': self._location_filter(),
            'name': term
        }

        filter_ids = []

        def songs_filter(coresong):
            return coresong.media.get_id() in filter_ids

        def songs_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, songs_search_cb)
    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")
            )
            %(location_filter)s
        }
        """.replace('\n', ' ').strip() % {
            'location_filter': self._location_filter(),
            'name': term
        }

        filter_ids = []

        def songs_filter(coresong):
            return coresong.media.get_id() in filter_ids

        def songs_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, songs_search_cb)

        # Album search

        query = """
        SELECT DISTINCT
            rdf:type(nmm:musicAlbum(?song))
            tracker:id(nmm:musicAlbum(?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")
            )
            %(location_filter)s
        }
        """.replace('\n', ' ').strip() % {
            'location_filter': self._location_filter(),
            '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
        WHERE {
            ?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")
            )
            %(location_filter)s
        }
        """.replace('\n', ' ').strip() % {
            'location_filter': self._location_filter(),
            '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)
Example #10
0
    def search(self, text):
        # FIXME: Searches are limited to not bog down the UI with
        # widget creation ({List,Flow}Box limitations). The limit is
        # arbitrarily set to 50 and set in the Tracker query. It should
        # be possible to set it through Grilo options instead. This
        # does not work as expected and needs further investigation.
        term = Tracker.sparql_escape_string(
            GLib.utf8_normalize(GLib.utf8_casefold(text, -1), -1,
                                GLib.NormalizeMode.NFKD))

        # Artist search
        self._notificationmanager.push_loading()

        query = """
        SELECT
            ?type ?id
        WHERE {
            SERVICE <dbus:%(miner_fs_busname)s> {
                GRAPH tracker:Audio {
                    SELECT DISTINCT
                        %(media_type)s AS ?type
                        COALESCE(?album_artist, ?artist) AS ?id
                    WHERE {
                        ?song a nmm:MusicPiece ;
                                nmm:musicAlbum ?album ;
                                nmm:artist ?artist .
                        OPTIONAL {
                            ?album a nmm:MusicAlbum ;
                                     nmm:albumArtist ?album_artist .
                        }
                        BIND(COALESCE(nmm:artistName(?album_artist),
                                      nmm:artistName(?artist)) AS ?artist_bind)
                        BIND(tracker:normalize(nmm:artistName(
                                 nmm:albumArtist(?artist_bind)), 'nfkd')
                             AS ?match1) .
                        BIND(tracker:normalize(
                                 nmm:artistName(nmm:artist(?song)), 'nfkd')
                             AS ?match2) .
                        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(?match4)), "%(name)s")
                            || CONTAINS(tracker:case-fold(?match4), "%(name)s")
                        )
                        %(location_filter)s
                    }
                    LIMIT 50
                }
            }
        }
        """.replace('\n', ' ').strip() % {
            "miner_fs_busname": self._tracker_wrapper.props.miner_fs_busname,
            "media_type": int(Grl.MediaType.AUDIO),
            'location_filter': self._tracker_wrapper.location_filter(),
            '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, remaining, error):
            if error:
                self._log.warning("Error: {}".format(error))
                self._notificationmanager.pop_loading()
                return

            if not media:
                self._artist_search_model.set_filter_func(artist_filter)
                self._notificationmanager.pop_loading()
                return

            artist_filter_ids.append(media.get_id())

        self.props.source.query(query, [Grl.METADATA_KEY_ID],
                                self._fast_options, artist_search_cb)

        # Album search
        self._notificationmanager.push_loading()

        query = """
        SELECT
            ?type ?id
        WHERE {
            SERVICE <dbus:%(miner_fs_busname)s> {
                GRAPH tracker:Audio {
                    SELECT DISTINCT
                        %(media_type)s AS ?type
                        nmm:musicAlbum(?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:artist(?song)), 'nfkd')
                             AS ?match2) .
                        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(?match4)), "%(name)s")
                            || CONTAINS(tracker:case-fold(?match4), "%(name)s")
                        )
                        %(location_filter)s
                    }
                    LIMIT 50
                }
            }
        }
        """.replace('\n', ' ').strip() % {
            "miner_fs_busname": self._tracker_wrapper.props.miner_fs_busname,
            "media_type": int(Grl.MediaType.CONTAINER),
            'location_filter': self._tracker_wrapper.location_filter(),
            '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, remaining, error):
            if error:
                self._log.warning("Error: {}".format(error))
                self._notificationmanager.pop_loading()
                return

            if not media:
                self._album_search_model.set_filter_func(album_filter)
                self._notificationmanager.pop_loading()
                return

            album_filter_ids.append(media.get_id())

        self.props.source.query(query, [Grl.METADATA_KEY_ID],
                                self._fast_options, albums_search_cb)

        # Song search
        self._notificationmanager.push_loading()

        query = """
        SELECT
            ?type ?id
        WHERE {
            SERVICE <dbus:%(miner_fs_busname)s> {
                GRAPH tracker:Audio {
                    SELECT DISTINCT
                        %(media_type)s AS ?type
                        ?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:artist(?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")
                        )
                        %(location_filter)s
                    }
                    LIMIT 50
                }
            }
        }
        """.replace('\n', ' ').strip() % {
            "miner_fs_busname": self._tracker_wrapper.props.miner_fs_busname,
            "media_type": int(Grl.MediaType.AUDIO),
            'location_filter': self._tracker_wrapper.location_filter(),
            'name': term
        }

        filter_ids = []

        def songs_filter(coresong):
            return coresong.media.get_id() in filter_ids

        def songs_search_cb(source, op_id, media, remaining, error):
            if error:
                self._log.warning("Error: {}".format(error))
                self._notificationmanager.pop_loading()
                return

            if not media:
                self._song_search_tracker.set_filter_func(songs_filter)
                self._notificationmanager.pop_loading()
                return

            filter_ids.append(media.get_id())

        self.props.source.query(query, [Grl.METADATA_KEY_ID],
                                self._fast_options, songs_search_cb)