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)
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)
def get_songs_with_track_match(name): name = Tracker.sparql_escape_string(GLib.utf8_casefold(name, -1)) query = ''' { SELECT DISTINCT ?song WHERE { ?song a nmm:MusicPiece . FILTER ( fn:contains(tracker:case-fold(nie:title(?song)), "%(name)s") ) } } '''.replace('\n', ' ').strip() % {'name': name} return Query.songs(query)
def get_artists_with_album_match(name): name = Tracker.sparql_escape_string(GLib.utf8_casefold(name, -1)) query = ''' { SELECT DISTINCT ?album WHERE { ?album a nmm:MusicAlbum . FILTER ( fn:contains(tracker:case-fold(nie:title(?album)), "%(name)s") ) } } '''.replace('\n', ' ').strip() % {'name': name} return Query.artists(query)
def get_artists_with_any_match(name): name = Tracker.sparql_escape_string(GLib.utf8_casefold(name, -1)) query = ''' { SELECT DISTINCT nmm:musicAlbum(?song) AS album WHERE { ?song a nmm:MusicPiece . FILTER ( fn:contains(tracker:case-fold(nie:title(nmm:musicAlbum(?song))), "%(name)s") || fn:contains(tracker:case-fold(nmm:artistName(nmm:performer(?song))), "%(name)s") || fn:contains(tracker:case-fold(nie:title(?song)), "%(name)s") ) } } '''.replace('\n', ' ').strip() % {'name': name} return Query.artists(query)
def get_songs_with_track_match(name): name = Tracker.sparql_escape_string(GLib.utf8_casefold(name, -1)) query = ''' { SELECT DISTINCT ?song WHERE { ?song a nmm:MusicPiece . FILTER ( fn:contains(tracker:case-fold(nie:title(?song)), "%(name)s") ) } } '''.replace('\n', ' ').strip() % { 'name': name } return Query.songs(query)
def get_artists_with_album_match(name): name = Tracker.sparql_escape_string(GLib.utf8_casefold(name, -1)) query = ''' { SELECT DISTINCT ?album WHERE { ?album a nmm:MusicAlbum . FILTER ( fn:contains(tracker:case-fold(nie:title(?album)), "%(name)s") ) } } '''.replace('\n', ' ').strip() % { 'name': name } return Query.artists(query)
def get_artists_with_any_match(name): name = Tracker.sparql_escape_string(GLib.utf8_casefold(name, -1)) query = ''' { SELECT DISTINCT nmm:musicAlbum(?song) AS album WHERE { ?song a nmm:MusicPiece . FILTER ( fn:contains(tracker:case-fold(nie:title(nmm:musicAlbum(?song))), "%(name)s") || fn:contains(tracker:case-fold(nmm:artistName(nmm:performer(?song))), "%(name)s") || fn:contains(tracker:case-fold(nie:title(?song)), "%(name)s") ) } } '''.replace('\n', ' ').strip() % { 'name': name } return Query.artists(query)
def get_songs_with_album_match(name): name = Tracker.sparql_escape_string(GLib.utf8_casefold(name, -1)) query = """ { SELECT DISTINCT ?song WHERE { ?song a nmm:MusicPiece . FILTER ( fn:contains(tracker:case-fold(nie:title(nmm:musicAlbum(?song))), "%(name)s") ) } } """.replace( "\n", " " ).strip() % { "name": name } return Query.songs(query)
def get_artists_with_artist_match(name): name = Tracker.sparql_escape_string(GLib.utf8_casefold(name, -1)) query = """ { SELECT DISTINCT ?album WHERE { ?album a nmm:MusicAlbum ; nmm:albumArtist ?artist . FILTER ( fn:contains(tracker:case-fold(nmm:artistName(?artist)), "%(name)s") ) } } """.replace( "\n", " " ).strip() % { "name": name } return Query.artists(query)
def string_for_search(s): return GLib.utf8_casefold(GLib.utf8_normalize(s, -1, GLib.NormalizeMode.ALL), -1)
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)
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)