def get_text_matches(self, text, channel=None): """Do text search lookup Args: text (str): text to search for. channel (str): URL of the channel. """ query = """ SELECT nie:url(?msg) AS ?url nie:title(?msg) AS ?title nco:fullname(?creator) AS ?fullname nie:url(?website) AS ?author_homepage nco:emailAddress(?email) AS ?author_email nie:contentCreated(?msg) AS ?date_created nmo:htmlMessageContent(?msg) AS ?content nmo:isRead(?msg) AS ?is_read ?msg BOUND(?tag) AS ?is_starred { ?msg a mfo:FeedMessage; """ if channel: query += """ nmo:communicationChannel ?chan; ?chan nie:url "%s" . """ query += """ fts:match "%s" . OPTIONAL { ?msg nco:creator ?creator . ?msg nao:hasTag ?tag . FILTER(?tag = nao:predefined-tag-favorite) . OPTIONAL { ?creator nco:hasEmailAddress ?email } . OPTIONAL { ?creator nco:websiteUrl ?website }} } ORDER BY fts:rank(?msg) """ if channel: query = query % Trackr.sparql_escape_string(text), channel else: query = query % Trackr.sparql_escape_string(text) try: results = self.sparql.query(query) ret = [] while (results.next(None)): ret.append(self.parse_sparql(results)) return ret except Exception: return []
def get_text_matches(self, text, channel=None): """Do text search lookup Args: text (str): text to search for. channel (str): URL of the channel. """ query = """ SELECT nie:url(?msg) AS ?url nie:title(?msg) AS ?title nco:fullname(?creator) AS ?fullname nie:url(?website) AS ?author_homepage nco:emailAddress(?email) AS ?author_email nie:contentCreated(?msg) AS ?date_created nmo:htmlMessageContent(?msg) AS ?content nmo:isRead(?msg) AS ?is_read ?msg BOUND(?tag) as ?is_starred { ?msg a mfo:FeedMessage; """ if channel: query += """ nmo:communicationChannel ?chan; ?chan nie:url "%s" . """ query += """ fts:match "%s" . OPTIONAL { ?msg nco:creator ?creator . ?msg nao:hasTag ?tag . FILTER(?tag = nao:predefined-tag-favorite) . OPTIONAL { ?creator nco:hasEmailAddress ?email } . OPTIONAL { ?creator nco:websiteUrl ?website }} } ORDER BY fts:rank(?msg) """ if channel: query = query % Trackr.sparql_escape_string(text), channel else: query = query % Trackr.sparql_escape_string(text) try: results = self.sparql.query(query) ret = [] while (results.next(None)): ret.append(self.parse_sparql(results)) return ret except Exception: return []
def tracks(self, filter_artist_name=None, filter_album_name=None, track_search_text=None): '''Return a list of tracks.''' if track_search_text: track_pattern = """ FILTER (fn:contains(LCASE(?track_title), "%s")) """ % Tracker.sparql_escape_string(track_search_text.lower()) else: track_pattern = " " if filter_artist_name: artist_pattern = 'FILTER (LCASE(?artist_name) = "%s")' % \ Tracker.sparql_escape_string(filter_artist_name.lower()) else: artist_pattern = " " if filter_album_name: album_pattern = """ ?track nmm:musicAlbum [ nie:title ?albumTitle ] . FILTER (LCASE(?albumTitle) = "%s") """ % Tracker.sparql_escape_string(filter_album_name.lower()) else: album_pattern = "" query_tracks = """ SELECT ?track_title ?track_url ?artist_name WHERE { ?track a nmm:MusicPiece ; dc:title ?track_title ; nie:url ?track_url ; nmm:performer ?artist . %s %s %s ?artist nmm:artistName ?artist_name . } ORDER BY ?track_title ?artist_name """ % (track_pattern, artist_pattern, album_pattern) tracks = self.query(query_tracks) while tracks.next(): yield { 'track': tracks.get_string(0)[0], 'artist': tracks.get_string(2)[0], 'tracker.url': tracks.get_string(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) . 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_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_artist_match(name): name = Tracker.sparql_escape_string(name) query = '?performer fts:match "nmm:artistName: %(name)s*" . '.replace( '\n', ' ').strip() % { 'name': name } return Query.songs(query)
def get_songs_with_album_match(name): name = Tracker.sparql_escape_string(name) query = '?album fts:match "nie:title: %(name)s*" . '.replace( '\n', ' ').strip() % { 'name': name } return Query.songs(query)
def get_songs_with_composer_match(name): name = Tracker.sparql_escape_string(name) query = """?song nmm:composer [ fts:match '%(name)s*' ] . """.replace( '\n', ' ').strip() % { 'name': name } return Query.songs(query)
def get_albums_with_album_match(name): name = Tracker.sparql_escape_string(name) query = """?album fts:match '"nie:title" : %(name)s*' . """.replace( '\n', ' ').strip() % { 'name': name } return Query.albums(query)
def get_artists_with_artist_match(name): name = Tracker.sparql_escape_string(name) query = """?performer fts:match '"nmm:artistName" : %(name)s*' . """.replace( '\n', ' ').strip() % { 'name': name } return Query.artists(query)
def get_artists_with_track_match(name): name = Tracker.sparql_escape_string(name) query = """?song fts:match '"nie:title" : %(name)s*' . """.replace( '\n', ' ').strip() % { 'name': name } return Query.artists(query)
def tracks(self, filter_artist_name=None, filter_album_name=None, track_search_text=None): '''Return a list of tracks.''' if track_search_text: track_pattern = """ FILTER (fn:contains(LCASE(?track_title), "%s")) """ % Tracker.sparql_escape_string(track_search_text.lower()) else: track_pattern = " " if filter_artist_name: artist_pattern = 'FILTER (LCASE(?artist_name) = "%s")' % \ Tracker.sparql_escape_string(filter_artist_name.lower()) else: artist_pattern =" " if filter_album_name: album_pattern = """ ?track nmm:musicAlbum [ nie:title ?albumTitle ] . FILTER (LCASE(?albumTitle) = "%s") """ % Tracker.sparql_escape_string(filter_album_name.lower()) else: album_pattern = "" query_tracks = """ SELECT ?track_title ?track_url ?artist_name WHERE { ?track a nmm:MusicPiece ; dc:title ?track_title ; nie:url ?track_url ; nmm:performer ?artist . %s %s %s ?artist nmm:artistName ?artist_name . } ORDER BY ?track_title ?artist_name """ % (track_pattern, artist_pattern, album_pattern) tracks = self.query(query_tracks) while tracks.next(): yield { 'track': tracks.get_string(0)[0], 'artist': tracks.get_string(2)[0], 'tracker.url': tracks.get_string(1)[0], }
def set_text(self, text): text_escaped = Tracker.sparql_escape_string(text) path = pathlib.Path(self.path(self.testfile)) if path.exists(): old_text_escaped = Tracker.sparql_escape_string(path.read_text()) resource_id = self.tracker.get_content_resource_id(self.uri(self.testfile)) with self.tracker.await_content_update(DOCUMENTS_GRAPH, resource_id, f'nie:plainTextContent "{old_text_escaped}"', f'nie:plainTextContent "{text_escaped}"', timeout=cfg.AWAIT_TIMEOUT): path.write_text(text) else: url = self.uri(self.testfile) expected = f'a nfo:Document; nie:isStoredAs <{url}>; nie:plainTextContent "{text_escaped}"' with self.tracker.await_insert(DOCUMENTS_GRAPH, expected, timeout=cfg.AWAIT_TIMEOUT): path.write_text(text)
def get_songs_with_composer_match(name): name = Tracker.sparql_escape_string(name) query = """ ?song nmm:composer ?composer . ?composer fts:match '"nmm:artistName" : %(name)s*' . """.replace('\n', ' ').strip() % { 'name': name } return Query.songs(query)
def get_albums_with_composer_match(name): name = Tracker.sparql_escape_string(name) query = """ ?song nmm:composer ?composer . ?composer fts:match '"nmm:artistName" : %(name)s*' . """.replace('\n', ' ').strip() % { 'name': name } return Query.albums(query)
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 """ def _create_cb(conn, res, data): try: result = conn.update_blank_finish(res) except GLib.Error as error: self._log.warning( "Unable to create playlist {}: {}".format( playlist_title, error.message)) self._notificationmanager.pop_loading() if callback is not None: callback(None) return playlist_urn = result[0][0]['playlist'] query = """ SELECT %(media_type)s AS ?type ?playlist AS ?id nie:title(?playlist) AS ?title nrl:added(?playlist) AS ?creationDate nfo:entryCounter(?playlist) AS ?childCount WHERE { BIND ( <%(playlist_urn)s> AS ?playlist ) } """.replace("\n", " ").strip() % { "media_type": int(Grl.MediaType.CONTAINER), "playlist_urn": playlist_urn } self._source.query( query, self._METADATA_PLAYLIST_KEYS, self._fast_options, self._add_user_playlist, callback) self._notificationmanager.push_loading() query = """ INSERT { _:playlist a nmm:Playlist ; a nfo:MediaList ; nie:title "%(title)s" ; nfo:entryCounter 0 . } """.replace("\n", " ").strip() % { "title": Tracker.sparql_escape_string(playlist_title) } self._tracker.update_blank_async(query, None, _create_cb, None)
def __init__(self): try: Query.music_folder = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC) assert Query.music_folder is not None except (TypeError, AssertionError): logger.warn("XDG Music dir is not set") return Query.MUSIC_URI = Tracker.sparql_escape_string(GLib.filename_to_uri(Query.music_folder)) for folder in [Query.music_folder]: if os.path.islink(folder): logger.warn("%s is a symlink, this folder will be omitted", folder)
def __init__(self): try: Query.music_folder = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC) assert Query.music_folder is not None except (TypeError, AssertionError): logger.warning("XDG Music dir is not set") return Query.MUSIC_URI = Tracker.sparql_escape_string(GLib.filename_to_uri(Query.music_folder)) for folder in [Query.music_folder]: if os.path.islink(folder): logger.warning("{} is a symlink, this folder will be omitted".format(folder))
def albums_query (artist): artist = Tracker.sparql_escape_string(artist) return """ SELECT ?album_name ?album WHERE { ?album a nmm:MusicAlbum; nmm:albumArtist "%s"; nmm:albumTitle ?album_name. # make sure the album isn't empty ?song nmm:musicAlbum ?album . } GROUP BY ?album ORDER BY ?album_name """ % (artist, )
def __init__(self): try: Query.music_folder = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC) assert Query.music_folder is not None except (TypeError, AssertionError): logger.warn("XDG Music dir is not set") return try: Query.download_folder = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOWNLOAD) assert Query.download_folder is not None except (TypeError, AssertionError): logger.warn("XDG Download dir is not set") return Query.MUSIC_URI = Tracker.sparql_escape_string(GLib.filename_to_uri(Query.music_folder)) Query.DOWNLOAD_URI = Tracker.sparql_escape_string(GLib.filename_to_uri(Query.download_folder)) for folder in [Query.music_folder, Query.download_folder]: if os.path.islink(folder): logger.warn("%s is a symlink, this folder will be omitted", folder) else: i = len(next(os.walk(folder))[2]) logger.debug("Found %d files in %s", i, folder)
def songs_query (album): album = Tracker.sparql_escape_string(album) return """ SELECT ?song_name ?song ?filename WHERE { ?song a nmm:MusicPiece; nmm:musicAlbum "%s"; nmm:trackNumber ?trackNumber; nie:title ?song_name ; nie:url ?filename ; nmm:musicAlbumDisc [nmm:setNumber ?disc_number]. } # need to also sort by disk number ORDER BY ?disc_number ?trackNumber """ % (album, )
def location_filter(self): try: music_dir = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_MUSIC) assert music_dir is not None except (TypeError, AssertionError): self._log.message("XDG Music dir is not set") return None music_dir = Tracker.sparql_escape_string( GLib.filename_to_uri(music_dir)) query = "FILTER (STRSTARTS(nie:isStoredAs(?song), '{}/'))".format( music_dir) return 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_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 __init__(self): try: Query.music_folder = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC) assert Query.music_folder is not None except (TypeError, AssertionError): logger.warn("XDG Music dir is not set") return Query.MUSIC_URI = Tracker.sparql_escape_string(GLib.filename_to_uri(Query.music_folder)) for folder in [Query.music_folder]: if os.path.islink(folder): logger.warn("%s is a symlink, this folder will be omitted", folder) else: i = len(next(os.walk(folder))) logger.debug("Found %d files in %s", i, folder)
def await_document_inserted(self, path, content=None): """Wraps await_insert() context manager.""" if isinstance(path, pathlib.Path): url = path.as_uri() else: url = self.uri(path) expected = [ 'a nfo:Document', f'nie:isStoredAs <{url}>', ] if content: content_escaped = Tracker.sparql_escape_string(content) expected += [f'nie:plainTextContent "{content_escaped}"'] return self.tracker.await_insert(DOCUMENTS_GRAPH, '; '.join(expected), timeout=cfg.AWAIT_TIMEOUT)
def _setup_local_db(self): # Open a local Tracker database. try: self._local_db = Tracker.SparqlConnection.new( Tracker.SparqlConnectionFlags.NONE, Gio.File.new_for_path(self.cache_directory()), Tracker.sparql_get_ontology_nepomuk(), None) except GLib.Error as error: self._log.warning("Error: {}, {}".format(error.domain, error.message)) self.notify("tracker-available") return # Version checks against the local version of Tracker can be done # here, set `self._tracker_available = TrackerState.OUTDATED` if the # checks fail. self._local_db_available = TrackerState.AVAILABLE self.notify("tracker-available")
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 _location_filter(): try: music_dir = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_MUSIC) assert music_dir is not None except (TypeError, AssertionError): print("XDG Music dir is not set") return music_dir = Tracker.sparql_escape_string( GLib.filename_to_uri(music_dir)) query = """ FILTER (STRSTARTS(nie:url(?song), '%(music_dir)s/')) """.replace('\n', ' ').strip() % { 'music_dir': music_dir } return query
def new_feed_item_signal(self, fetcher, feed, item): try: uri = item.get_source() # if self.get_info_for_entry(uri) is not None: # logger.info("Item %s is already added" % uri) # return source_uri = feed.get_source() timestamp = item.get_publish_time() author = item.get_author() date = datetime.fromtimestamp(timestamp).isoformat() title = item.get_title() text = item.get_description() escaped_text = Tracker.sparql_escape_string(text) query = INSERT_QUERY % (uri, date, source_uri, author, escaped_text, title) logger.info("New feed: \n%s" % query) self.iface.SparqlUpdate(query) except Exception as e: logger.warn(str(e))
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 title(self, new_name): """Rename a playlist :param str new_name: new playlist name """ self._notificationmanager.push_loading() def update_cb(conn, res, data): try: conn.update_finish(res) except GLib.Error as e: self._log.warning( "Unable to rename playlist from {} to {}: {}".format( self._title, new_name, e.message)) else: self._title = new_name finally: self._notificationmanager.pop_loading() self.thaw_notify() query = """ INSERT OR REPLACE { ?playlist nie:title "%(title)s" } WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList . OPTIONAL { ?playlist nfo:hasMediaFileListEntry ?entry . } FILTER ( tracker:id(?playlist) = %(playlist_id)s ) } """.replace("\n", " ").strip() % { 'title': Tracker.sparql_escape_string(new_name), 'playlist_id': self.props.pl_id } self.freeze_notify() self._tracker.update_async(query, None, update_cb, None)
def main(): logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) tmpdir = tempfile.mkdtemp(prefix='tracker-test-') # Where the database is stored. store_path = Gio.File.new_for_path(tmpdir) # The database schemas. ontology_path = Tracker.sparql_get_ontology_nepomuk() if 'TEST_ONTOLOGIES_DIR' in os.environ: ontology_path = Gio.File.new_for_path( os.environ['TEST_ONTOLOGIES_DIR']) cancellable = None # Create a new, empty database. conn = Tracker.SparqlConnection.new(Tracker.SparqlConnectionFlags.NONE, store_path, ontology_path, cancellable) bus = Gio.bus_get_sync(Gio.BusType.SESSION, cancellable) unique_name = bus.get_unique_name() # Publish our endpoint on DBus. endpoint = Tracker.EndpointDBus.new(conn, bus, None, cancellable) print(f"Exposing a Tracker endpoint on bus name {unique_name}") print() print(f"Try connecting over D-Bus using `tracker3 sparql`:") print() print(f" tracker3 sparql --dbus-service={unique_name} -q ...") loop = GLib.MainLoop.new(None, False) if os.environ.get('TRACKER_EXAMPLES_AUTOMATED_TEST'): GLib.timeout_add(10, lambda *args: loop.quit(), None) else: print() print(f"Press CTRL+C to quit.") loop.run()
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 ensure_document_inserted(self, path, content=None): """Block until document is inserted. Use this if insertion may already have happened.""" if isinstance(path, pathlib.Path): url = path.as_uri() else: url = self.uri(path) expected = [ 'a nfo:Document', f'nie:isStoredAs <{url}>', ] if content: content_escaped = Tracker.sparql_escape_string(content) expected += [f'nie:plainTextContent "{content_escaped}"'] return self.tracker.ensure_resource(DOCUMENTS_GRAPH, ';'.join(expected), timeout=cfg.AWAIT_TIMEOUT)
def __init__(self): super().__init__() # FIXME: This is now duplicated here and in GrlTrackerWrapper. try: music_folder = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_MUSIC) assert music_folder is not None except (TypeError, AssertionError): print("XDG Music dir is not set") return music_folder = Tracker.sparql_escape_string( GLib.filename_to_uri(music_folder)) href_text = "<a href='{}'>{}</a>".format(music_folder, _("Music folder")) # TRANSLATORS: This is a label to display a link to open user's music # folder. {} will be replaced with the translated text 'Music folder' folder_text = _("The contents of your {} will appear here.") self._content_text = folder_text.format(href_text) self._state = EmptyView.State.INITIAL
def get_songs_with_artist_match(name): name = Tracker.sparql_escape_string(name) query = '?performer fts:match "nmm:artistName: %(name)s*" . '.replace('\n', ' ').strip() % {'name': name} return Query.songs(query)
"http://www.tracker-project.org/temp/nmm#Video": "videos", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SoftwareApplication": "software", "http://www.tracker-project.org/temp/nmm#Photo": "pictures", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Folder": "folders", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#FileDataObject": "files" } if __name__ == "__main__": results = {} conn = Tracker.SparqlConnection.get() words = sys.argv[1].split(" "); query_params = [] for i in words: if i: if i[0] == '"' and i[-1] == '"': query_params.append("?s fts:match \"" + Tracker.sparql_escape_string(i[1:-1]) + "\"") else: query_params.append("?s fts:match \"" + Tracker.sparql_escape_string(i) + "*\"") query = """SELECT ?s nie:url(?s) nmm:musicAlbum(?s) nmm:performer(?s) nmm:trackNumber(?s) nie:title(?s) nie:mimeType(?s) rdf:type(?s) WHERE { """ + " . ".join(query_params) + """ } ORDER BY DESC (fts:rank(?s)) LIMIT 100"""; cursor = conn.query(query) while cursor.next(): defined_type = None; if not cursor.get_string(7)[0] or not cursor.get_string(1)[0]: #If we have no defined file type or no file url, skip continue result_types = cursor.get_string(7)[0].split(",") while len(result_types) > 0 and defined_type == None: t = result_types.pop()
def songs(self, artist_name=None, album_name=None, track_name=None): '''Return all songs matching specific search criteria. These are grouped into their respective releases. Any tracks that aren't present on any releases will appear last. Any tracks that appear on multiple releases will appear multiple times. ''' if artist_name: artist_id = self.artist_id(artist_name) artist_select = "" artist_pattern = """ ?track nmm:performer <%s> . """ % artist_id else: artist_select = "nmm:artistName(nmm:performer(?track))" artist_pattern = "" if album_name: album_pattern = """ ?album nie:title ?albumTitle . FILTER (LCASE(?albumTitle) = "%s") """ % Tracker.sparql_escape_string(album_name.lower()) else: album_pattern = "" if track_name: track_pattern = """ ?track nie:title ?trackTitle . FILTER (LCASE(?trackTitle) = "%s") """ % Tracker.sparql_escape_string(track_name.lower()) else: track_pattern = "" query_songs_with_releases = """ SELECT nie:url(?track) nmm:albumTitle(?album) nie:title(?track) %s WHERE { ?album a nmm:MusicAlbum . ?track a nmm:MusicPiece ; nmm:musicAlbum ?album . %s %s %s } ORDER BY nmm:albumTitle(?album) nmm:trackNumber(?track) """ % (artist_select, artist_pattern, album_pattern, track_pattern) songs_with_releases = self.query(query_songs_with_releases) if not album_name: query_songs_without_releases = """ SELECT nie:url(?track) ?album nie:title(?track) %s WHERE { ?track a nmm:MusicPiece . %s %s OPTIONAL { ?track nmm:musicAlbum ?album } FILTER (! bound (?album)) } ORDER BY %s nie:title(?track) """ % (artist_select, artist_pattern, track_pattern, artist_select) songs_without_releases = self.query( query_songs_without_releases) else: songs_without_releases = None result = [] # The artist name may be returned as None if it's unknown to Tracker, # so we can't use None as an 'undefined' value. int(-1) will work, # as any artist named "-1" would be returned as str("-1"). prev_artist_name = -1 prev_album_name = -1 album_tracks = [] while songs_with_releases.next(): if artist_select: artist_name = songs_with_releases.get_string(3)[0] album_name = songs_with_releases.get_string(1)[0] if prev_artist_name is -1: prev_artist_name = artist_name if prev_album_name is -1: prev_album_name = album_name if album_name != prev_album_name: yield { 'artist': prev_artist_name, 'album': prev_album_name, 'tracks': album_tracks, } album_tracks = [] prev_artist_name = artist_name prev_album_name = album_name album_tracks.append({ 'track': songs_with_releases.get_string(2)[0], 'location': songs_with_releases.get_string(0)[0] }) if len(album_tracks) > 0: yield { 'artist': artist_name, 'album': album_name, 'tracks': album_tracks, } prev_artist_name = -1 catchall_tracks = [] if songs_without_releases: while songs_without_releases.next(): if artist_select: artist_name = songs_without_releases.get_string(3)[0] if prev_artist_name is -1: prev_artist_name = artist_name if prev_artist_name != artist_name: yield { 'artist': prev_artist_name, 'tracks': catchall_tracks } prev_artist_name = artist_name catchall_tracks = [] catchall_tracks.append({ 'track': songs_without_releases.get_string(2)[0], 'location': songs_without_releases.get_string(0)[0] }) if len(catchall_tracks) > 0: yield { 'artist': artist_name, 'tracks': catchall_tracks }
class Query(): music_folder = None MUSIC_URI = None download_folder = None DOWNLOAD_URI = None try: music_folder = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_MUSIC) MUSIC_URI = Tracker.sparql_escape_string( GLib.filename_to_uri(music_folder)) download_folder = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_DOWNLOAD) DOWNLOAD_URI = Tracker.sparql_escape_string( GLib.filename_to_uri(download_folder)) for folder in [music_folder, download_folder]: if os.path.islink(folder): logger.warn("%s is a symlink, this folder will be omitted" % folder) else: i = len(next(os.walk(folder))[2]) logger.debug("Found %d files in %s" % (i, folder)) except TypeError: logger.warn("XDG user dirs are not set") @staticmethod def order_by_statement(attr): """Returns a SPARQL ORDER BY statement sorting by the given attribute, ignoring articles as defined in _("the"). 'Attr' should be given without parentheses, e.g., "attr='?author'".""" return_statement = "fn:lower-case(%(attribute)s)" % {'attribute': attr} # TRANSLATORS: _("the") should be a space-separated list of all-lowercase articles # (such as 'the') that should be ignored when alphabetizing artists/albums. This # list should include 'the' regardless of language. If some articles occur more # frequently than others, most common should appear first, least common last. for article in reversed(_("the a an").split(" ")): return_statement = '''IF(fn:starts-with(fn:lower-case(%(attribute)s), "%(article)s"), fn:substring(fn:lower-case(%(attribute)s), %(substr_start)s), %(nested_if)s)''' % { 'attribute': attr, 'article': article + " ", 'substr_start': str(len(article) + 2), 'nested_if': return_statement } return return_statement @staticmethod def all_albums(): return Query.albums('?album a nmm:MusicAlbum .') @staticmethod def all_artists(): return Query.artists('?album a nmm:MusicAlbum .') @staticmethod def all_songs(): return Query.songs('?song a nmm:MusicPiece ; a nfo:FileDataObject .') @staticmethod def all_playlists(): return Query.playlists('?playlist a nmm:Playlist .') @staticmethod def all_songs_count(): query = ''' SELECT COUNT(?song) AS childcount WHERE { ?song a nmm:MusicPiece ; a nfo:FileDataObject FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?song) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?song) ) ) FILTER ( NOT EXISTS { ?song a nmm:Video } && NOT EXISTS { ?song a nmm:Playlist } ) } '''.replace('\n', ' ').strip() % { 'music_dir': Query.MUSIC_URI, 'download_dir': Query.DOWNLOAD_URI } return query @staticmethod def albums(where_clause): query = ''' SELECT DISTINCT rdf:type(?album) tracker:id(?album) AS id ( SELECT nmm:artistName(?artist) WHERE { ?album nmm:albumArtist ?artist } LIMIT 1 ) AS artist nie:title(?album) AS title nie:title(?album) AS album tracker:coalesce( ( SELECT GROUP_CONCAT( nmm:artistName(?artist), ',' ) WHERE { ?album nmm:albumArtist ?artist } ), ( SELECT GROUP_CONCAT( ( SELECT nmm:artistName(nmm:performer(?_12)) AS perf WHERE { ?_12 nmm:musicAlbum ?album } GROUP BY ?perf ), ',' ) AS album_performer WHERE { } ) ) AS author xsd:integer( tracker:coalesce( nmm:albumTrackCount(?album), ( SELECT COUNT(?_1) WHERE { ?_1 nmm:musicAlbum ?album ; tracker:available 'true' FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?_1) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?_1) ) ) FILTER ( NOT EXISTS { ?_1 a nmm:Video } && NOT EXISTS { ?_1 a nmm:Playlist } ) } ) ) ) AS childcount ( SELECT fn:year-from-dateTime(?c) WHERE { ?_2 nmm:musicAlbum ?album ; nie:contentCreated ?c ; tracker:available 'true' FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?_2) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?_2) ) ) FILTER ( NOT EXISTS { ?_2 a nmm:Video } && NOT EXISTS { ?_2 a nmm:Playlist } ) } LIMIT 1 ) AS creation-date { %(where_clause)s FILTER ( EXISTS { ?_3 nmm:musicAlbum ?album ; tracker:available 'true' FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?_3) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?_3) ) ) FILTER ( NOT EXISTS { ?_3 a nmm:Video } && NOT EXISTS { ?_3 a nmm:Playlist } ) } ) } ORDER BY %(album_order)s %(artist_order)s ?albumyear '''.replace('\n', ' ').strip() % { 'where_clause': where_clause.replace('\n', ' ').strip(), 'music_dir': Query.MUSIC_URI, 'download_dir': Query.DOWNLOAD_URI, 'album_order': Query.order_by_statement("?title"), 'artist_order': Query.order_by_statement("?author") } return query @staticmethod def artists(where_clause): query = ''' SELECT DISTINCT rdf:type(?album) tracker:id(?album) AS id ( SELECT nmm:artistName(?artist) WHERE { ?album nmm:albumArtist ?artist } LIMIT 1 ) AS artist nie:title(?album) AS title nie:title(?album) AS album tracker:coalesce( ( SELECT GROUP_CONCAT( nmm:artistName(?artist), ',' ) WHERE { ?album nmm:albumArtist ?artist } ), ( SELECT GROUP_CONCAT( ( SELECT nmm:artistName(nmm:performer(?_12)) AS perf WHERE { ?_12 nmm:musicAlbum ?album FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?_12) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?_12) ) ) FILTER ( NOT EXISTS { ?_12 a nmm:Video } && NOT EXISTS { ?_12 a nmm:Playlist } ) } GROUP BY ?perf ), ',' ) AS album_performer WHERE { } ) ) AS author xsd:integer( tracker:coalesce( nmm:albumTrackCount(?album), ( SELECT COUNT(?_1) WHERE { ?_1 nmm:musicAlbum ?album ; tracker:available 'true' FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?_1) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?_1) ) ) FILTER ( NOT EXISTS { ?_1 a nmm:Video } && NOT EXISTS { ?_1 a nmm:Playlist } ) } ) ) ) AS childcount ( SELECT fn:year-from-dateTime(?c) WHERE { ?_2 nmm:musicAlbum ?album ; nie:contentCreated ?c ; tracker:available 'true' FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?_2) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?_2) ) ) FILTER ( NOT EXISTS { ?_2 a nmm:Video } && NOT EXISTS { ?_2 a nmm:Playlist } ) } LIMIT 1 ) AS creation-date { %(where_clause)s FILTER ( EXISTS { ?_3 nmm:musicAlbum ?album ; tracker:available 'true' FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?_3) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?_3) ) ) FILTER ( NOT EXISTS { ?_3 a nmm:Video } && NOT EXISTS { ?_3 a nmm:Playlist } ) } ) } ORDER BY %(artist_order)s ?albumyear %(album_order)s '''.replace('\n', ' ').strip() % { 'where_clause': where_clause.replace('\n', ' ').strip(), 'music_dir': Query.MUSIC_URI, 'download_dir': Query.DOWNLOAD_URI, 'artist_order': Query.order_by_statement("?author"), 'album_order': Query.order_by_statement("nie:title(?album)") } return query @staticmethod def songs(where_clause): query = ''' SELECT DISTINCT rdf:type(?song) 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 IF(bound(?tag), 'truth!', '') AS lyrics { %(where_clause)s OPTIONAL { ?song nao:hasTag ?tag . FILTER( ?tag = nao:predefined-tag-favorite ) } FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?song) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?song) ) ) FILTER ( NOT EXISTS { ?song a nmm:Video } && NOT EXISTS { ?song a nmm:Playlist } ) } ORDER BY tracker:added(?song) '''.replace('\n', ' ').strip() % { 'where_clause': where_clause.replace('\n', ' ').strip(), 'music_dir': Query.MUSIC_URI, 'download_dir': Query.DOWNLOAD_URI } return query @staticmethod def playlists(where_clause): query = ''' SELECT DISTINCT rdf:type(?playlist) tracker:id(?playlist) AS id nie:title(?playlist) AS title nfo:entryCounter(?playlist) AS childcount { %(where_clause)s OPTIONAL { ?playlist a nfo:FileDataObject . FILTER ( EXISTS { ?playlist tracker:available 'true' FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?playlist) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?playlist) ) ) } ) } } ORDER BY fn:lower-case(?title) '''.replace('\n', ' ').strip() % { 'where_clause': where_clause.replace('\n', ' ').strip(), 'music_dir': Query.MUSIC_URI, 'download_dir': Query.DOWNLOAD_URI } return query @staticmethod def album_songs(album_id): query = ''' SELECT DISTINCT rdf:type(?song) 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 IF(bound(?tag), 'truth!', '') AS lyrics WHERE { ?song a nmm:MusicPiece ; a nfo:FileDataObject ; nmm:musicAlbum ?album . OPTIONAL { ?song nao:hasTag ?tag . FILTER( ?tag = nao:predefined-tag-favorite ) } FILTER ( tracker:id(?album) = %(album_id)s ) FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?song) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?song) ) ) FILTER ( NOT EXISTS { ?song a nmm:Video } && NOT EXISTS { ?song a nmm:Playlist } ) } ORDER BY nmm:setNumber(nmm:musicAlbumDisc(?song)) nmm:trackNumber(?song) tracker:added(?song) '''.replace('\n', ' ').strip() % { 'album_id': album_id, 'music_dir': Query.MUSIC_URI, 'download_dir': Query.DOWNLOAD_URI } return query @staticmethod def playlist_songs(playlist_id, filter_clause=None): query = ''' SELECT rdf:type(?song) tracker:id(?entry) 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 IF(bound(?tag), 'truth!', '') AS lyrics WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList ; nfo:hasMediaFileListEntry ?entry . ?entry a nfo:MediaFileListEntry ; nfo:entryUrl ?url . ?song a nmm:MusicPiece ; a nfo:FileDataObject ; nie:url ?url . OPTIONAL { ?song nao:hasTag ?tag . FILTER( ?tag = nao:predefined-tag-favorite ) } FILTER ( %(filter_clause)s ) FILTER ( NOT EXISTS { ?song a nmm:Video } && NOT EXISTS { ?song a nmm:Playlist } ) } ORDER BY nfo:listPosition(?entry) '''.replace('\n', ' ').strip() % { 'playlist_id': playlist_id, 'filter_clause': filter_clause or 'tracker:id(?playlist) = ' + playlist_id, 'music_dir': Query.MUSIC_URI, 'download_dir': Query.DOWNLOAD_URI } return query @staticmethod def get_album_for_album_id(album_id): query = """ SELECT DISTINCT rdf:type(?album) tracker:id(?album) AS id ( SELECT nmm:artistName(?artist) WHERE { ?album nmm:albumArtist ?artist } LIMIT 1 ) AS artist nie:title(?album) AS title nie:title(?album) AS album WHERE { ?album a nmm:MusicAlbum . FILTER ( tracker:id(?album) = %(album_id)s ) } """.replace("\n", " ").strip() % { 'album_id': album_id, 'music_dir': Query.MUSIC_URI, 'download_dir': Query.DOWNLOAD_URI } return query @staticmethod def get_album_for_song_id(song_id): query = """ SELECT DISTINCT rdf:type(?album) tracker:id(?album) AS id ( SELECT nmm:artistName(?artist) WHERE { ?album nmm:albumArtist ?artist } LIMIT 1 ) AS artist nie:title(?album) AS title nie:title(?album) AS album WHERE { ?song a nmm:MusicPiece ; nmm:musicAlbum ?album . FILTER ( tracker:id(?song) = %(song_id)s ) FILTER ( tracker:uri-is-descendant( '%(music_dir)s', nie:url(?song) ) || tracker:uri-is-descendant( '%(download_dir)s', nie:url(?song) ) ) FILTER ( NOT EXISTS { ?song a nmm:Video } && NOT EXISTS { ?song a nmm:Playlist } ) } """.replace("\n", " ").strip() % { 'song_id': song_id, 'music_dir': Query.MUSIC_URI, 'download_dir': Query.DOWNLOAD_URI } return query @staticmethod def update_playcount(song_url): query = """ INSERT OR REPLACE { ?song nie:usageCounter ?playcount . } WHERE { SELECT IF(bound(?usage), (?usage + 1), 1) AS playcount ?song WHERE { ?song a nmm:MusicPiece . OPTIONAL { ?song nie:usageCounter ?usage . } FILTER ( nie:url(?song) = "%(song_url)s" ) } } """.replace("\n", " ").strip() % { 'song_url': song_url } return query @staticmethod def update_last_played(song_url, time): query = """ INSERT OR REPLACE { ?song nfo:fileLastAccessed '%(time)s' . } WHERE { SELECT ?song WHERE { ?song a nmm:MusicPiece . FILTER ( nie:url(?song) = "%(song_url)s" ) } } """.replace("\n", " ").strip() % { 'song_url': song_url, 'time': time } return query @staticmethod def create_playlist(title): query = """ INSERT { _:playlist a nmm:Playlist ; a nfo:MediaList ; nie:title "%(title)s" ; nfo:entryCounter 0 . } """.replace("\n", " ").strip() % { 'title': title } return query @staticmethod def create_tag(tag_text): query = """ INSERT OR REPLACE { _:tag a nao:Tag ; rdfs:comment '%(tag_text)s'. } """.replace("\n", " ").strip() % { 'tag_text': tag_text } return query @staticmethod def create_playlist_with_tag(title, tag_text): # TODO: make this an extension of 'create playlist' rather than its own func.? # TODO: CREATE TAG IF IT DOESN'T EXIST! query = """ INSERT { _:playlist a nmm:Playlist ; a nfo:MediaList ; nie:title "%(title)s" ; nfo:entryCounter 0 ; nao:hasTag ?tag. } WHERE { SELECT ?tag WHERE { ?tag a nao:Tag ; rdfs:comment '%(tag_text)s'. } } """.replace("\n", " ").strip() % { 'title': title, 'tag_text': tag_text } return query @staticmethod def delete_playlist(playlist_id): query = """ DELETE { ?playlist a rdfs:Resource . ?entry a rdfs:Resource . } WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList . OPTIONAL { ?playlist nfo:hasMediaFileListEntry ?entry . } FILTER ( tracker:id(?playlist) = %(playlist_id)s ) } """.replace("\n", " ").strip() % { 'playlist_id': playlist_id } return query @staticmethod def add_song_to_playlist(playlist_id, song_uri): query = """ INSERT OR REPLACE { _:entry a nfo:MediaFileListEntry ; nfo:entryUrl "%(song_uri)s" ; nfo:listPosition ?position . ?playlist nfo:entryCounter ?position ; nfo:hasMediaFileListEntry _:entry . } WHERE { SELECT ?playlist (?counter + 1) AS position WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList ; nfo:entryCounter ?counter . FILTER ( tracker:id(?playlist) = %(playlist_id)s ) } } """.replace("\n", " ").strip() % { 'playlist_id': playlist_id, 'song_uri': song_uri } return query @staticmethod def remove_song_from_playlist(playlist_id, song_id): query = """ INSERT OR REPLACE { ?entry nfo:listPosition ?position . } WHERE { SELECT ?entry (?old_position - 1) AS position WHERE { ?entry a nfo:MediaFileListEntry ; nfo:listPosition ?old_position . ?playlist nfo:hasMediaFileListEntry ?entry . FILTER (?old_position > ?removed_position) { SELECT ?playlist ?removed_position WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList ; nfo:hasMediaFileListEntry ?removed_entry . ?removed_entry nfo:listPosition ?removed_position . FILTER ( tracker:id(?playlist) = %(playlist_id)s && tracker:id(?removed_entry) = %(song_id)s ) } } } } INSERT OR REPLACE { ?playlist nfo:entryCounter ?new_counter . } WHERE { SELECT ?playlist (?counter - 1) AS new_counter WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList ; nfo:entryCounter ?counter . FILTER ( tracker:id(?playlist) = %(playlist_id)s ) } } DELETE { ?playlist nfo:hasMediaFileListEntry ?entry . ?entry a rdfs:Resource . } WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList ; nfo:hasMediaFileListEntry ?entry . FILTER ( tracker:id(?playlist) = %(playlist_id)s && tracker:id(?entry) = %(song_id)s ) } """.replace("\n", " ").strip() % { 'playlist_id': playlist_id, 'song_id': song_id } return query @staticmethod def get_playlist_with_id(playlist_id): query = """ ?playlist a nmm:Playlist . FILTER ( tracker:id(?playlist) = %(playlist_id)s ) """.replace('\n', ' ').strip() % { 'playlist_id': playlist_id } return Query.playlists(query) @staticmethod def get_playlist_with_tag(playlist_tag): query = """ ?playlist a nmm:Playlist ; nao:hasTag ?tag . ?tag rdfs:comment ?tag_text . FILTER ( ?tag_text = '%(playlist_tag)s' ) """.replace('\n', ' ').strip() % { 'playlist_tag': playlist_tag } return Query.playlists(query) @staticmethod def get_playlist_with_urn(playlist_urn): query = """ SELECT DISTINCT tracker:id(<%(playlist_urn)s>) AS id WHERE { <%(playlist_urn)s> a nmm:Playlist } """.replace('\n', ' ').strip() % { 'playlist_urn': playlist_urn } return query @staticmethod def get_playlist_song_with_id(playlist_id, entry_id): return Query.playlist_songs(playlist_id, 'tracker:id(?entry) = ' + str(entry_id)) @staticmethod def get_playlist_song_with_urn(entry_urn): query = """ SELECT DISTINCT tracker:id(<%(entry_urn)s>) AS id WHERE { <%(entry_urn)s> a nfo:MediaFileListEntry } """.replace('\n', ' ').strip() % { 'entry_urn': entry_urn } return query @staticmethod def clear_playlist_with_id(playlist_id): query = """ DELETE { ?playlist nfo:hasMediaFileListEntry ?entry . ?entry a rdfs:Resource . } WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList ; nfo:hasMediaFileListEntry ?entry . FILTER ( tracker:id(?playlist) = %(playlist_id)s ) } """.replace('\n', ' ').strip() % { 'playlist_id': playlist_id } return query @staticmethod def get_most_played_songs(): # TODO: set playlist size somewhere? Currently default is 50. query = """ SELECT ?url WHERE { ?song a nmm:MusicPiece ; nie:usageCounter ?count ; nie:isStoredAs ?as . ?as nie:url ?url . } ORDER BY DESC(?count) LIMIT 50 """.replace('\n', ' ').strip() return query @staticmethod def get_never_played_songs(): query = """ SELECT ?url WHERE { ?song a nmm:MusicPiece ; nie:isStoredAs ?as . ?as nie:url ?url . FILTER ( NOT EXISTS { ?song nie:usageCounter ?count .} ) } ORDER BY nfo:fileLastAccessed(?song) """.replace('\n', ' ').strip() return query def get_recently_played_songs(): #TODO: or this could take comparison date as an argument so we don't need to make a date string in query.py... #TODO: set time interval somewhere? A settings file? (Default is maybe 2 weeks...?) days_difference = 7 # currently hardcoding time interval of 7 days seconds_difference = days_difference * SECONDS_PER_DAY compare_date = time.strftime( sparql_midnight_dateTime_format, time.gmtime(time.time() - seconds_difference)) query = """ SELECT ?url WHERE { ?song a nmm:MusicPiece ; nie:isStoredAs ?as ; nfo:fileLastAccessed ?last_played . ?as nie:url ?url . FILTER ( ?last_played > '%(compare_date)s'^^xsd:dateTime ) FILTER ( EXISTS { ?song nie:usageCounter ?count .} ) } ORDER BY DESC(?last_played) """.replace('\n', ' ').strip() % { 'compare_date': compare_date } return query def get_recently_added_songs(): #TODO: or this could take comparison date as an argument so we don't need to make a date string in query.py... #TODO: set time interval somewhere? A settings file? (Default is maybe 2 weeks...?) days_difference = 7 # currently hardcoding time interval of 7 days seconds_difference = days_difference * SECONDS_PER_DAY compare_date = time.strftime( sparql_midnight_dateTime_format, time.gmtime(time.time() - seconds_difference)) query = """ SELECT ?url WHERE { ?song a nmm:MusicPiece ; nie:isStoredAs ?as ; tracker:added ?added . ?as nie:url ?url . FILTER ( ?added > '%(compare_date)s'^^xsd:dateTime ) } ORDER BY DESC(?added) """.replace('\n', ' ').strip() % { 'compare_date': compare_date } return query def get_favorite_songs(): query = """ SELECT ?url WHERE { ?song a nmm:MusicPiece ; nie:isStoredAs ?as ; nao:hasTag nao:predefined-tag-favorite . ?as nie:url ?url . } ORDER BY DESC(tracker:added(?song)) """.replace('\n', ' ').strip() return query # Functions for search # TODO: make those queries actually return something @staticmethod def get_albums_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.albums(query) @staticmethod def get_albums_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.albums(query) @staticmethod def get_albums_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.albums(query) @staticmethod def get_albums_with_track_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(?song)), "%(name)s") ) } } '''.replace('\n', ' ').strip() % { 'name': name } return Query.albums(query) @staticmethod 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) @staticmethod 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) @staticmethod 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) @staticmethod def get_artists_with_track_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(?song)), "%(name)s") ) } } '''.replace('\n', ' ').strip() % { 'name': name } return Query.artists(query) @staticmethod def get_songs_with_any_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") || fn:contains(tracker:case-fold(nmm:artistName(nmm:performer(?song))), "%(name)s") || fn:contains(tracker:case-fold(nie:title(nmm:musicAlbum(?song))), "%(name)s") ) } } '''.replace('\n', ' ').strip() % { 'name': name } return Query.songs(query) @staticmethod def get_songs_with_artist_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(nmm:artistName(nmm:performer(?song))), "%(name)s") ) } } '''.replace('\n', ' ').strip() % { 'name': name } return Query.songs(query) @staticmethod 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) @staticmethod 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) @staticmethod def clear_playlist(playlist_id): # TODO is there a way to do this with only one FILTER statement? query = """ DELETE { ?playlist nfo:hasMediaFileListEntry ?entry . ?entry a rdfs:Resource . } WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList ; nfo:hasMediaFileListEntry ?entry . FILTER ( tracker:id(?playlist) = %(playlist_id)s ) } INSERT OR REPLACE { ?playlist nfo:entryCounter '0' } WHERE { ?playlist a nmm:Playlist ; a nfo:MediaList . FILTER ( tracker:id(?playlist) = %(playlist_id)s ) } """.replace("\n", " ").strip() % { 'playlist_id': playlist_id } return query def add_favorite(song_url): query = """ INSERT { ?song nao:hasTag nao:predefined-tag-favorite } WHERE { ?song a nmm:MusicPiece . FILTER ( nie:url(?song) = "%(song_url)s" ) } """.replace("\n", " ").strip() % { 'song_url': song_url } return query def remove_favorite(song_url): query = """ DELETE { ?song nao:hasTag nao:predefined-tag-favorite } WHERE { ?song a nmm:MusicPiece . FILTER ( nie:url(?song) = "%(song_url)s" ) } """.replace("\n", " ").strip() % { 'song_url': song_url } return query
#!/usr/bin/python3 import gi, sys from gi.repository import GLib, Gio, Tracker try: connection = Tracker.SparqlConnection.new( Tracker.SparqlConnectionFlags.NONE, None, # Database location, None creates it in-memory Tracker.sparql_get_ontology_nepomuk(), # Ontology location None) # Create a resource containing RDF data resource = Tracker.Resource.new(None) resource.set_uri('rdf:type', 'nmm:MusicPiece') # Create a batch, and add the resource to it batch = connection.create_batch() batch.add_resource(None, resource) # Execute the batch to insert the data batch.execute() connection.close() except Exception as e: print('Error: {0}'.format(e)) sys.exit(-1)
"http://www.tracker-project.org/temp/nmm#Video": "videos", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SoftwareApplication": "software", "http://www.tracker-project.org/temp/nmm#Photo": "pictures", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Folder": "folders", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#FileDataObject": "files" } if __name__ == "__main__": results = {} conn = Tracker.SparqlConnection.get() words = sys.argv[1].split(" ") query_params = [] for i in words: if i: if i[0] == '"' and i[-1] == '"': query_params.append("?s fts:match \"" + Tracker.sparql_escape_string(i[1:-1]) + "\"") else: query_params.append("?s fts:match \"" + Tracker.sparql_escape_string(i) + "*\"") query = """SELECT ?s nie:url(?s) nmm:musicAlbum(?s) nmm:performer(?s) nmm:trackNumber(?s) nie:title(?s) nie:mimeType(?s) rdf:type(?s) WHERE { """ + " . ".join(query_params) + """ } ORDER BY DESC (fts:rank(?s)) LIMIT 100""" cursor = conn.query(query) while cursor.next(): defined_type = None if not cursor.get_string(7)[0] or not cursor.get_string(1)[0]: #If we have no defined file type or no file url, skip continue result_types = cursor.get_string(7)[0].split(",") while len(result_types) > 0 and defined_type == None: t = result_types.pop()
def get_songs_with_album_match(name): name = Tracker.sparql_escape_string(name) query = """?song nmm:musicAlbum [ fts:match '%(name)s*' ] . """.replace('\n', ' ').strip() % {'name': name} return Query.songs(query)
def get_artists_with_track_match(name): name = Tracker.sparql_escape_string(name) query = """?song fts:match '"nie:title" : %(name)s*' . """.replace('\n', ' ').strip() % {'name': name} return Query.artists(query)
def get_artists_with_artist_match(name): name = Tracker.sparql_escape_string(name) query = """?performer fts:match '"nmm:artistName" : %(name)s*' . """.replace('\n', ' ').strip() % {'name': name} return Query.artists(query)
def get_songs_with_track_match(name): name = Tracker.sparql_escape_string(name) query = '?song fts:match "nie:title: %(name)s*" . '.replace('\n', ' ').strip() % {'name': name} return Query.songs(query)
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 get_artists_with_album_match(name): name = Tracker.sparql_escape_string(name) query = '?album fts:match "nie:title: %(name)s*" . '.replace('\n', ' ').strip() % {'name': name} return Query.artists(query)
"http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Folder": "folders", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#FileDataObject": "files" } if __name__ == "__main__": results = {} conn = Tracker.SparqlConnection.get() words = sys.argv[1].split(" ") query_params = [] for i in words: if i: if i[0] == '"' and i[-1] == '"': query_params.append("?s fts:match \"" + Tracker.sparql_escape_string(i[1:-1]) + "\"") else: query_params.append("?s fts:match \"" + Tracker.sparql_escape_string(i) + "*\"") query = """SELECT ?s nie:url(?s) nmm:musicAlbum(?s) nmm:performer(?s) nmm:trackNumber(?s) nie:title(?s) nie:mimeType(?s) rdf:type(?s) WHERE { """ + " . ".join(query_params) + """ } ORDER BY DESC (fts:rank(?s)) LIMIT 100""" cursor = conn.query(query) while cursor.next(): defined_type = None if not cursor.get_string(7)[0] or not cursor.get_string(1)[0]: #If we have no defined file type or no file url, skip continue result_types = cursor.get_string(7)[0].split(",")
def get_songs_with_album_match(name): name = Tracker.sparql_escape_string(name) query = """?album fts:match '"nie:title" : %(name)s*' . """.replace('\n', ' ').strip() % {'name': name} return Query.songs(query)