[tracker-miners/wip/carlosg/apps-in-miner-fs: 7/10] tracker-extract: Add desktop file extractor
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker-miners/wip/carlosg/apps-in-miner-fs: 7/10] tracker-extract: Add desktop file extractor
- Date: Tue, 13 Nov 2018 11:47:50 +0000 (UTC)
commit be2ba869e18064550958f5f63824e88cf661d4a2
Author: Carlos Garnacho <carlosg gnome org>
Date: Sat Nov 10 16:55:30 2018 +0100
tracker-extract: Add desktop file extractor
This is the first step towards folding tracker-miner-apps into
tracker-miner-fs, thus dropping one executable from a usual session.
Implement an extractor for .desktop files as per the desktop entry
specification [1]. This is basically a rip off of src/miner/apps,
just tuned down to ignore directory/link types, and made to use
TrackerResource instead of the deprecated TrackerSparqlBuilder.
[1] https://specifications.freedesktop.org/desktop-entry-spec/latest/
src/tracker-extract/10-desktop.rule | 4 +
src/tracker-extract/meson.build | 1 +
src/tracker-extract/tracker-extract-decorator.c | 1 +
src/tracker-extract/tracker-extract-desktop.c | 276 ++++++++++++++++++++++++
4 files changed, 282 insertions(+)
---
diff --git a/src/tracker-extract/10-desktop.rule b/src/tracker-extract/10-desktop.rule
new file mode 100644
index 000000000..851bd097a
--- /dev/null
+++ b/src/tracker-extract/10-desktop.rule
@@ -0,0 +1,4 @@
+[ExtractorRule]
+ModulePath=libextract-desktop.so
+MimeTypes=application/x-desktop
+FallbackRdfTypes=nfo:SoftwareApplication;
diff --git a/src/tracker-extract/meson.build b/src/tracker-extract/meson.build
index bf19f8bac..325f68329 100644
--- a/src/tracker-extract/meson.build
+++ b/src/tracker-extract/meson.build
@@ -6,6 +6,7 @@
modules = [
['extract-bmp', 'tracker-extract-bmp.c', ['10-bmp.rule'], [tracker_miners_common_dep]],
+ ['extract-desktop', 'tracker-extract-desktop.c', ['10-desktop.rule'], [tracker_miners_common_dep]],
['extract-dummy', 'tracker-extract-dummy.c', ['10-comics.rule', '10-ebooks.rule'],
[tracker_miners_common_dep]]
]
diff --git a/src/tracker-extract/tracker-extract-decorator.c b/src/tracker-extract/tracker-extract-decorator.c
index 516e1ee48..7c504363c 100644
--- a/src/tracker-extract/tracker-extract-decorator.c
+++ b/src/tracker-extract/tracker-extract-decorator.c
@@ -74,6 +74,7 @@ static const gchar *supported_classes[] = {
"nfo:Video",
"nfo:FilesystemImage",
"nmm:Playlist",
+ "nfo:SoftwareApplication",
NULL
};
diff --git a/src/tracker-extract/tracker-extract-desktop.c b/src/tracker-extract/tracker-extract-desktop.c
new file mode 100644
index 000000000..27e622127
--- /dev/null
+++ b/src/tracker-extract/tracker-extract-desktop.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018, Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "tracker-common.h"
+#include <libtracker-extract/tracker-extract.h>
+
+#include "tracker-main.h"
+
+#define GROUP_DESKTOP_ENTRY "Desktop Entry"
+
+#define SOFTWARE_CATEGORY_URN_PREFIX "urn:software-category:"
+#define THEME_ICON_URN_PREFIX "urn:theme-icon:"
+
+static GKeyFile *
+get_desktop_key_file (GFile *file,
+ gchar **type,
+ GError **error)
+{
+ GKeyFile *key_file;
+ gchar *path;
+ gchar *str;
+
+ path = g_file_get_path (file);
+ key_file = g_key_file_new ();
+ *type = NULL;
+
+ if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, error)) {
+ g_key_file_free (key_file);
+ g_free (path);
+ return NULL;
+ }
+
+ str = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "Type", NULL);
+
+ if (G_UNLIKELY (!str)) {
+ *type = NULL;
+
+ g_set_error_literal (error, G_KEY_FILE_ERROR,
+ G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+ "Desktop file doesn't contain type");
+ g_key_file_free (key_file);
+ g_free (path);
+ return NULL;
+ } else {
+ /* Sanitize type */
+ *type = g_strstrip (str);
+ }
+
+ g_free (path);
+
+ return key_file;
+}
+
+static void
+insert_data_from_desktop_file (TrackerResource *resource,
+ const gchar *metadata_key,
+ GKeyFile *desktop_file,
+ const gchar *key,
+ const gchar *locale)
+{
+ gchar *str;
+
+ if (locale) {
+ /* Try to get the key with our desired LANG locale... */
+ str = g_key_file_get_locale_string (desktop_file, GROUP_DESKTOP_ENTRY, key, locale, NULL);
+ /* If our desired locale failed, use the list of LANG locales prepared by GLib
+ * (will return untranslated string if none of the locales available) */
+ if (!str) {
+ str = g_key_file_get_locale_string (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL,
NULL);
+ }
+ } else {
+ str = g_key_file_get_string (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL);
+ }
+
+ if (str) {
+ tracker_resource_set_string (resource, metadata_key, str);
+ g_free (str);
+ }
+}
+
+static gboolean
+process_desktop_file (TrackerResource *resource,
+ GFile *file)
+{
+ GKeyFile *key_file;
+ GError *error = NULL;
+ gchar *name = NULL;
+ gchar *type;
+ GStrv cats;
+ gsize cats_len;
+ gboolean is_software = FALSE;
+ gchar *lang;
+
+ key_file = get_desktop_key_file (file, &type, &error);
+ if (!key_file) {
+ gchar *uri;
+
+ uri = g_file_get_uri (file);
+ g_warning ("Could not load desktop file '%s': %s",
+ uri,
+ error->message ? error->message : "no error given");
+
+ g_error_free (error);
+ g_free (uri);
+ return FALSE;
+ }
+
+ if (g_key_file_get_boolean (key_file, GROUP_DESKTOP_ENTRY, "Hidden", NULL)) {
+ g_debug ("Desktop file is hidden");
+ g_key_file_free (key_file);
+ return FALSE;
+ }
+
+ /* Retrieve LANG locale setup */
+ lang = tracker_locale_get (TRACKER_LOCALE_LANGUAGE);
+
+ /* Try to get the categories with our desired LANG locale... */
+ cats = g_key_file_get_locale_string_list (key_file, GROUP_DESKTOP_ENTRY, "Categories", lang,
&cats_len, NULL);
+ if (!cats) {
+ /* If our desired locale failed, use the list of LANG locales prepared by GLib
+ * (will return untranslated string if none of the locales available) */
+ cats = g_key_file_get_locale_string_list (key_file, GROUP_DESKTOP_ENTRY, "Categories", NULL,
&cats_len, NULL);
+ }
+
+ if (!name) {
+ /* Try to get the name with our desired LANG locale... */
+ name = g_key_file_get_locale_string (key_file, GROUP_DESKTOP_ENTRY, "Name", lang, NULL);
+ if (!name) {
+ /* If our desired locale failed, use the list of LANG locales prepared by GLib
+ * (will return untranslated string if none of the locales available) */
+ name = g_key_file_get_locale_string (key_file, GROUP_DESKTOP_ENTRY, "Name", NULL,
NULL);
+ }
+ }
+
+ /* Sanitize name */
+ if (name) {
+ g_strstrip (name);
+ }
+
+ if (name && g_ascii_strcasecmp (type, "Application") == 0) {
+ tracker_resource_add_uri (resource, "rdf:type", "nfo:SoftwareApplication");
+ is_software = TRUE;
+ } else {
+ /* Invalid type, all valid types are already listed above */
+ g_warning ("Unknown desktop entry type '%s'", type);
+ g_key_file_free (key_file);
+ g_strfreev (cats);
+ g_free (lang);
+ g_free (name);
+ return FALSE;
+ }
+
+ /* We should always always have a proper name if the desktop file is correct
+ * w.r.t to the Freedesktop specs, but sometimes this is not true, so
+ * instead of passing wrong stuff to the SPARQL builder, we avoid it.
+ * If we don't have a proper name, we already warned it before. */
+ if (name)
+ tracker_resource_set_string (resource, "nie:title", name);
+
+ if (is_software) {
+ gchar *icon;
+
+ tracker_resource_add_uri (resource, "rdf:type", "nfo:Executable");
+ insert_data_from_desktop_file (resource,
+ "nie:comment",
+ key_file,
+ "Comment",
+ lang);
+ insert_data_from_desktop_file (resource,
+ "nfo:softwareCmdLine",
+ key_file,
+ "Exec",
+ lang);
+
+ icon = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "Icon", NULL);
+
+ if (icon) {
+ TrackerResource *icon_resource;
+ gchar *escaped_icon;
+ gchar *icon_uri;
+
+ /* Sanitize icon */
+ g_strstrip (icon);
+
+ escaped_icon = g_uri_escape_string (icon, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
FALSE);
+
+ icon_uri = g_strdup_printf (THEME_ICON_URN_PREFIX "%s", escaped_icon);
+
+ icon_resource = tracker_resource_new (icon_uri);
+ tracker_resource_add_uri (icon_resource, "rdf:type", "nfo:Image");
+ tracker_resource_set_take_relation (resource, "nfo:softwareIcon", icon_resource);
+
+ g_free (icon_uri);
+ g_free (escaped_icon);
+ g_free (icon);
+ }
+ }
+
+ if (cats) {
+ gsize i;
+
+ for (i = 0 ; cats[i] && i < cats_len ; i++) {
+ TrackerResource *category;
+ gchar *cat_uri;
+ gchar *cat;
+
+ cat = cats[i];
+
+ if (!cat) {
+ continue;
+ }
+
+ /* Sanitize category */
+ g_strstrip (cat);
+
+ cat_uri = tracker_sparql_escape_uri_printf (SOFTWARE_CATEGORY_URN_PREFIX "%s", cat);
+
+ /* There are also .desktop
+ * files that describe these categories, but we can handle
+ * preemptively creating them if we visit a app .desktop
+ * file that mentions one that we don't yet know about */
+
+ category = tracker_resource_new (cat_uri);
+ tracker_resource_add_uri (category, "rdf:type", "nfo:SoftwareCategory");
+ tracker_resource_set_string (category, "nie:title", cat);
+ g_free (cat_uri);
+
+ tracker_resource_add_take_relation (resource, "nie:isLogicalPartOf", category);
+ }
+ }
+
+ g_strfreev (cats);
+
+ g_free (name);
+ g_free (lang);
+
+ return TRUE;
+}
+
+G_MODULE_EXPORT gboolean
+tracker_extract_get_metadata (TrackerExtractInfo *info)
+{
+ TrackerResource *metadata;
+
+ metadata = tracker_resource_new (NULL);
+
+ if (!process_desktop_file (metadata, tracker_extract_info_get_file (info))) {
+ g_object_unref (metadata);
+ return FALSE;
+ }
+
+ tracker_extract_info_set_resource (info, metadata);
+ g_object_unref (metadata);
+
+ return TRUE;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]