Skip to content

Instantly share code, notes, and snippets.

@ahodesuka
Last active October 28, 2021 04:36
Show Gist options
  • Save ahodesuka/49c1d0eea4b64f24c4c7 to your computer and use it in GitHub Desktop.
Save ahodesuka/49c1d0eea4b64f24c4c7 to your computer and use it in GitHub Desktop.
diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c
index f75a0d2..bb4c8a6 100644
--- a/gio/glocalfileinfo.c
+++ b/gio/glocalfileinfo.c
@@ -64,6 +64,12 @@
#endif
#include "thumbnail-verify.h"
+#ifdef HAVE_DBUS1
+#define FREEDESKTOP_THUMBNAILER
+#include <gio/gio.h>
+#include <gio/gdbusproxy.h>
+#include <glib-object.h>
+#endif /* HAVE_DBUS1 */
#ifdef G_OS_WIN32
#include <windows.h>
@@ -101,6 +107,16 @@ struct ThumbMD5Context {
unsigned char in[64];
};
+#ifdef FREEDESKTOP_THUMBNAILER
+typedef struct
+{
+ GMainLoop *mainloop;
+ guint32 handle;
+ gboolean success;
+ const char *error_message;
+} ThumbnailerState;
+#endif /* FREEDESKTOP_THUMBNAILER */
+
#ifndef G_OS_WIN32
typedef struct {
@@ -1277,16 +1293,132 @@ get_content_type (const char *basename,
}
+#ifdef FREEDESKTOP_THUMBNAILER
+static void
+thumbnailer_signal_cb (GDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ GVariant *parameters,
+ gpointer thumbnailer_state)
+{
+ ThumbnailerState *state = (ThumbnailerState *) thumbnailer_state;
+ guint32 signal_handle;
+ const gchar **uris;
+
+
+ if (g_strcmp0 (signal_name, "Error") == 0)
+ {
+ g_variant_get (parameters, "(uasis)", &signal_handle, NULL, NULL, &state->error_message);
+ //g_assert (signal_handle == state->handle);
+ state->success = FALSE;
+ }
+ else if (g_strcmp0 (signal_name, "Ready") == 0)
+ {
+ g_variant_get (parameters, "(u^as)", &signal_handle, &uris);
+ //g_assert (signal_handle == state->handle);
+ state->success = TRUE;
+ }
+ else if (g_strcmp0 (signal_name, "Finished") == 0)
+ {
+ g_main_loop_quit (state->mainloop);
+ }
+
+}
+
+static gboolean
+generate_thumbnail(const char *uri, const char *mime_type)
+{
+ GMainContext *thread_context;
+ GDBusConnection *connection;
+ GDBusProxy *proxy;
+ GVariant *result = NULL;
+ GError *error = NULL;
+ ThumbnailerState state;
+ const gchar *uris[2] = { uri, NULL };
+ const gchar *mime_types[2] = { mime_type, NULL };
+
+ thread_context = g_main_context_new ();
+ state.mainloop = g_main_loop_new (thread_context, FALSE);
+ state.success = FALSE;
+
+ connection = g_dbus_connection_new_for_address_sync (
+ g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, NULL),
+ G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+
+ proxy = g_dbus_proxy_new_sync (connection,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ "org.freedesktop.thumbnails.Thumbnailer1",
+ "/org/freedesktop/thumbnails/Thumbnailer1",
+ "org.freedesktop.thumbnails.Thumbnailer1",
+ NULL, /* TODO: cancellable */
+ &error);
+ if (!proxy)
+ {
+ g_warning ("generate_thumbnail (): g_dbus_proxy_new_sync failed");
+ g_main_loop_unref (state.mainloop);
+ return FALSE;
+ }
+ else
+ g_debug("generate_thumbnail (): connected to D-Bus");
+
+ g_signal_connect (G_OBJECT (proxy), "g-signal",
+ G_CALLBACK (thumbnailer_signal_cb), &state);
+
+ g_main_context_push_thread_default (thread_context);
+ result = g_dbus_proxy_call_sync (proxy,
+ "Queue",
+ g_variant_new("(^as^asssu)",
+ uris,
+ mime_types,
+ "normal",
+ "default",
+ 0),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (!result || error)
+ {
+ g_warning ("generate_thumbnail (): g_dbus_proxy_call_sync() failed: %s", error->message);
+ return FALSE;
+ }
+ g_variant_get (result, "(u)", &(state.handle));
+ g_variant_unref (result);
+ // block until the loop is terminated in thumbnailer_signal_cb ()
+ g_main_loop_run (state.mainloop);
+ g_object_unref (proxy);
+ g_object_unref (connection);
+ g_main_loop_unref (state.mainloop);
+ g_main_context_pop_thread_default (thread_context);
+ g_main_context_unref (thread_context);
+
+ if (state.success)
+ {
+ g_debug ("generate_thumbnail (): Thumbnail generated for %s", uris[0]);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+#endif /* FREEDESKTOP_THUMBNAILER */
+
/* @stat_buf is the pre-calculated result of stat(path), or %NULL if that failed. */
static void
get_thumbnail_attributes (const char *path,
GFileInfo *info,
- const GLocalFileStat *stat_buf)
+ const GLocalFileStat *stat_buf,
+ gboolean generate)
{
GChecksum *checksum;
char *uri;
char *filename;
char *basename;
+ const char *content_type;
uri = g_filename_to_uri (path, NULL, NULL);
@@ -1305,6 +1437,7 @@ get_thumbnail_attributes (const char *path,
_g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
thumbnail_verify (filename, uri, stat_buf));
+ generate = FALSE;
}
else
{
@@ -1318,6 +1451,7 @@ get_thumbnail_attributes (const char *path,
_g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
thumbnail_verify (filename, uri, stat_buf));
+ generate = FALSE;
}
else
{
@@ -1333,9 +1467,32 @@ get_thumbnail_attributes (const char *path,
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED, TRUE);
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
thumbnail_verify (filename, uri, stat_buf));
+ generate = FALSE;
}
}
}
+
+ if (generate)
+ {
+#ifdef FREEDESKTOP_THUMBNAILER
+ content_type = g_file_info_get_content_type (info);
+ if (content_type)
+ {
+ g_debug ("invoking Freedesktop Thumbnailer for %s (%s)", uri, content_type);
+ if(generate_thumbnail (uri, content_type))
+ {
+ /* Now that the thumbnail is generated, find it. */
+ get_thumbnail_attributes (path, info, stat_buf, FALSE);
+ }
+ else
+ {
+ _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED, TRUE);
+ _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID, FALSE);
+ }
+ }
+#endif /* FREEDESKTOP_THUMBNAILER */
+ }
+
g_free (basename);
g_free (filename);
g_free (uri);
@@ -1695,6 +1852,18 @@ _g_local_file_info_get (const char *basename,
info = g_file_info_new ();
+ /* Thumbnail generation requires a content-type.
+ * TODO: implement g_file_attribute_matcher_add () in gfileinfo.c */
+ if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH)
+ && !_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE))
+ {
+ char *attributes = g_file_attribute_matcher_to_string (attribute_matcher);
+ char *_attributes = g_strdup_printf ("%s,standard::content-type", attributes);
+ attribute_matcher = g_file_attribute_matcher_new (_attributes);
+ g_free (attributes);
+ g_free (_attributes);
+ }
+
/* Make sure we don't set any unwanted attributes */
g_file_info_set_attribute_mask (info, attribute_matcher);
@@ -1835,7 +2004,9 @@ _g_local_file_info_get (const char *basename,
_g_file_attribute_matcher_matches_id (attribute_matcher,
G_FILE_ATTRIBUTE_ID_STANDARD_ICON) ||
_g_file_attribute_matcher_matches_id (attribute_matcher,
- G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
+ G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON) ||
+ _g_file_attribute_matcher_matches_id (attribute_matcher,
+ G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH))
{
char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, FALSE);
@@ -1948,9 +2119,9 @@ _g_local_file_info_get (const char *basename,
G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH))
{
if (stat_ok)
- get_thumbnail_attributes (path, info, &statbuf);
+ get_thumbnail_attributes (path, info, &statbuf, TRUE);
else
- get_thumbnail_attributes (path, info, NULL);
+ get_thumbnail_attributes (path, info, NULL, TRUE);
}
vfs = g_vfs_get_default ();
@CamilleScholtz
Copy link

Should I use tumbler as my thumbnailer for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment