Commit 48c353ae authored by Debarshi Ray's avatar Debarshi Ray

Add PhotosThumbnailFactory

This is the application-facing interface to the thumbnailer process.
It is modelled on gnome_desktop_thumbnail_factory_generate_thumbnail.
It adds error-handling and the generated thumbnail is implicitly saved
without handing the pixels back to the application.

There is a placeholder for cancelling running jobs, but it isn't
implemented at the moment.

https://bugzilla.gnome.org/show_bug.cgi?id=690255
parent e62947cd
......@@ -23,6 +23,8 @@ gnome_photos_built_sources = \
photos-resources.h \
photos-shell-search-provider2.c \
photos-shell-search-provider2.h \
photos-thumbnailer-dbus.c \
photos-thumbnailer-dbus.h \
photos-tracker-extract-priority.c \
photos-tracker-extract-priority.h \
photos-tracker-resources.c \
......@@ -230,6 +232,8 @@ gnome_photos_SOURCES = \
photos-source-manager.h \
photos-spinner-box.c \
photos-spinner-box.h \
photos-thumbnail-factory.c \
photos-thumbnail-factory.h \
photos-tool.c \
photos-tool.h \
photos-tool-colors.c \
......@@ -354,6 +358,7 @@ EXTRA_DIST = \
$(NULL)
AM_CPPFLAGS = \
-DPACKAGE_LIBEXEC_DIR=\""${libexecdir}"\" \
-DPACKAGE_LOCALE_DIR=\""${datadir}/locale"\" \
$(BABL_CFLAGS) \
$(CAIRO_CFLAGS) \
......
/*
* Photos - access, organize and share your photos on GNOME
* Copyright © 2017 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "config.h"
#include "photos-debug.h"
#include "photos-error.h"
#include "photos-quarks.h"
#include "photos-thumbnail-factory.h"
#include "photos-thumbnailer-dbus.h"
#include "photos-utils.h"
struct _PhotosThumbnailFactory
{
GObject parent_instance;
GCond cond_thumbnailer;
GDBusConnection *connection;
GDBusServer *dbus_server;
GError *initialization_error;
GError *thumbnailer_error;
GMutex mutex_connection;
GMutex mutex_thumbnailer;
PhotosThumbnailerDBus *thumbnailer;
gboolean is_initialized;
};
static void photos_thumbnail_factory_initable_iface_init (GInitableIface *iface);
G_DEFINE_TYPE_WITH_CODE (PhotosThumbnailFactory, photos_thumbnail_factory, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, photos_thumbnail_factory_initable_iface_init));
G_LOCK_DEFINE_STATIC (init_lock);
static const gchar *THUMBNAILER_PATH = "/org/gnome/Photos/Thumbnailer";
static gboolean
photos_thumbnail_factory_authorize_authenticated_peer (PhotosThumbnailFactory *self,
GIOStream *iostream,
GCredentials *credentials)
{
GCredentials *own_credentials = NULL;
GError *error;
gboolean ret_val = FALSE;
gchar *str = NULL;
g_mutex_lock (&self->mutex_connection);
str = g_credentials_to_string (credentials);
photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Received authorization request: %s", str);
if (self->connection != NULL)
{
g_warning ("Unable to authorize peer: Connection exists");
goto out;
}
if (credentials == NULL)
{
g_warning ("Unable to authorize peer: Credentials not found");
goto out;
}
own_credentials = g_credentials_new ();
error = NULL;
if (!g_credentials_is_same_user (credentials, own_credentials, &error))
{
g_warning ("Unable to authorize peer: %s", error->message);
g_error_free (error);
goto out;
}
ret_val = TRUE;
out:
g_mutex_unlock (&self->mutex_connection);
g_clear_object (&own_credentials);
g_free (str);
return ret_val;
}
static void
photos_thumbnail_factory_connection_closed (PhotosThumbnailFactory *self,
gboolean remote_peer_vanished,
GError *error)
{
g_mutex_lock (&self->mutex_connection);
if (error != NULL)
photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Lost connection to the thumbnailer: %s", error->message);
else
photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Lost connection to the thumbnailer");
g_signal_handlers_disconnect_by_func (self->connection, photos_thumbnail_factory_connection_closed, self);
g_clear_object (&self->connection);
g_mutex_unlock (&self->mutex_connection);
}
static GdkPixbuf *
photos_thumbnail_factory_get_preview (PhotosThumbnailFactory *self,
GFile *file,
gint size,
GCancellable *cancellable,
GError **error)
{
GFileInfo *info = NULL;
GIcon *icon;
GInputStream *stream = NULL;
GdkPixbuf *pixbuf = NULL;
GdkPixbuf *ret_val = NULL;
info = g_file_query_info (file, G_FILE_ATTRIBUTE_PREVIEW_ICON, G_FILE_QUERY_INFO_NONE, cancellable, error);
if (info == NULL)
goto out;
icon = G_ICON (g_file_info_get_attribute_object (info, G_FILE_ATTRIBUTE_PREVIEW_ICON));
if (!G_IS_LOADABLE_ICON (icon))
{
g_set_error (error, PHOTOS_ERROR, 0, "Preview icon is not loadable");
goto out;
}
stream = g_loadable_icon_load (G_LOADABLE_ICON (icon), 0, NULL, cancellable, error);
if (stream == NULL)
goto out;
pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, size, size, TRUE, cancellable, error);
if (pixbuf == NULL)
goto out;
ret_val = g_object_ref (pixbuf);
out:
g_clear_object (&info);
g_clear_object (&pixbuf);
g_clear_object (&stream);
return ret_val;
}
static gboolean
photos_thumbnail_factory_new_connection (PhotosThumbnailFactory *self, GDBusConnection *connection)
{
GError *error;
g_mutex_lock (&self->mutex_connection);
g_mutex_lock (&self->mutex_thumbnailer);
g_assert_null (self->connection);
g_clear_error (&self->thumbnailer_error);
g_clear_object (&self->thumbnailer);
photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Received new connection");
self->connection = g_object_ref (connection);
g_signal_connect_swapped (self->connection,
"closed",
G_CALLBACK (photos_thumbnail_factory_connection_closed),
self);
error = NULL;
self->thumbnailer = photos_thumbnailer_dbus_proxy_new_sync (self->connection,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
| G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
NULL,
THUMBNAILER_PATH,
NULL,
&error);
if (error != NULL)
{
self->thumbnailer_error = g_error_copy (error);
g_error_free (error);
}
g_mutex_unlock (&self->mutex_connection);
g_cond_signal (&self->cond_thumbnailer);
g_mutex_unlock (&self->mutex_thumbnailer);
return TRUE;
}
static GObject *
photos_thumbnail_factory_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params)
{
static GObject *self = NULL;
if (self == NULL)
{
self = G_OBJECT_CLASS (photos_thumbnail_factory_parent_class)->constructor (type,
n_construct_params,
construct_params);
g_object_add_weak_pointer (self, (gpointer) &self);
return self;
}
return g_object_ref (self);
}
static void
photos_thumbnail_factory_dispose (GObject *object)
{
PhotosThumbnailFactory *self = PHOTOS_THUMBNAIL_FACTORY (object);
if (self->dbus_server != NULL)
{
g_dbus_server_stop (self->dbus_server);
g_clear_object (&self->dbus_server);
}
g_clear_object (&self->connection);
g_clear_object (&self->thumbnailer);
G_OBJECT_CLASS (photos_thumbnail_factory_parent_class)->dispose (object);
}
static void
photos_thumbnail_factory_finalize (GObject *object)
{
PhotosThumbnailFactory *self = PHOTOS_THUMBNAIL_FACTORY (object);
g_cond_clear (&self->cond_thumbnailer);
g_clear_error (&self->initialization_error);
g_clear_error (&self->thumbnailer_error);
g_mutex_clear (&self->mutex_connection);
g_mutex_clear (&self->mutex_thumbnailer);
G_OBJECT_CLASS (photos_thumbnail_factory_parent_class)->finalize (object);
}
static void
photos_thumbnail_factory_init (PhotosThumbnailFactory *self)
{
g_cond_init (&self->cond_thumbnailer);
g_mutex_init (&self->mutex_connection);
g_mutex_init (&self->mutex_thumbnailer);
}
static void
photos_thumbnail_factory_class_init (PhotosThumbnailFactoryClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->constructor = photos_thumbnail_factory_constructor;
object_class->dispose = photos_thumbnail_factory_dispose;
object_class->finalize = photos_thumbnail_factory_finalize;
}
static gboolean
photos_thumbnail_factory_initable_init (GInitable *initable, GCancellable *cancellable, GError **error)
{
PhotosThumbnailFactory *self = PHOTOS_THUMBNAIL_FACTORY (initable);
GDBusAuthObserver *observer = NULL;
gboolean ret_val = FALSE;
const gchar *tmp_dir;
gchar *address = NULL;
gchar *guid = NULL;
G_LOCK (init_lock);
if (self->is_initialized)
{
if (self->dbus_server != NULL)
ret_val = TRUE;
else
g_assert_nonnull (self->initialization_error);
goto out;
}
g_assert_no_error (self->initialization_error);
tmp_dir = g_get_tmp_dir ();
address = g_strdup_printf ("unix:tmpdir=%s", tmp_dir);
guid = g_dbus_generate_guid ();
observer = g_dbus_auth_observer_new ();
g_signal_connect_swapped (observer,
"authorize-authenticated-peer",
G_CALLBACK (photos_thumbnail_factory_authorize_authenticated_peer),
self);
self->dbus_server = g_dbus_server_new_sync (address, G_DBUS_SERVER_FLAGS_NONE,
guid,
observer,
cancellable,
&self->initialization_error);
if (G_UNLIKELY (self->initialization_error != NULL))
goto out;
g_signal_connect_swapped (self->dbus_server,
"new-connection",
G_CALLBACK (photos_thumbnail_factory_new_connection),
self);
g_dbus_server_start (self->dbus_server);
ret_val = TRUE;
out:
self->is_initialized = TRUE;
if (!ret_val)
{
g_assert_nonnull (self->initialization_error);
g_propagate_error (error, g_error_copy (self->initialization_error));
}
G_UNLOCK (init_lock);
g_clear_object (&observer);
g_free (address);
g_free (guid);
return ret_val;
}
static void
photos_thumbnail_factory_initable_iface_init (GInitableIface *iface)
{
iface->init = photos_thumbnail_factory_initable_init;
}
PhotosThumbnailFactory *
photos_thumbnail_factory_dup_singleton (GCancellable *cancellable, GError **error)
{
return g_initable_new (PHOTOS_TYPE_THUMBNAIL_FACTORY, cancellable, error, NULL);
}
gboolean
photos_thumbnail_factory_generate_thumbnail (PhotosThumbnailFactory *self,
GFile *file,
const gchar *mime_type,
GQuark orientation,
gint64 original_height,
gint64 original_width,
const gchar *pipeline_uri,
GCancellable *cancellable,
GError **error)
{
GError *local_error;
GdkPixbuf *pixbuf = NULL;
gboolean mutex_connection_unlocked = FALSE;
gboolean ret_val = FALSE;
gint thumbnail_size;
g_return_val_if_fail (PHOTOS_IS_THUMBNAIL_FACTORY (self), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (g_file_is_native (file), FALSE);
g_return_val_if_fail (mime_type != NULL && mime_type != '\0', FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_mutex_lock (&self->mutex_connection);
if (orientation == 0)
orientation = PHOTOS_ORIENTATION_TOP;
thumbnail_size = photos_utils_get_icon_size ();
local_error = NULL;
pixbuf = photos_thumbnail_factory_get_preview (self, file, thumbnail_size, cancellable, &local_error);
if (local_error != NULL)
{
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
goto out;
else
g_clear_error (&local_error);
}
if (pixbuf != NULL)
{
ret_val = TRUE;
goto out;
}
if (pixbuf == NULL)
{
if (self->connection == NULL)
{
GSubprocess *subprocess;
const gchar *address;
gchar *thumbnailer_path;
g_mutex_lock (&self->mutex_thumbnailer);
g_clear_error (&self->thumbnailer_error);
g_clear_object (&self->thumbnailer);
g_mutex_unlock (&self->mutex_thumbnailer);
address = g_dbus_server_get_client_address (self->dbus_server);
thumbnailer_path = g_strconcat (PACKAGE_LIBEXEC_DIR,
G_DIR_SEPARATOR_S,
PACKAGE_TARNAME,
"-thumbnailer",
NULL);
photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Spawning “%s --address %s”", thumbnailer_path, address);
local_error = NULL;
subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_NONE,
&local_error,
thumbnailer_path,
"--address",
address,
NULL);
if (local_error != NULL)
goto out;
g_mutex_unlock (&self->mutex_connection);
mutex_connection_unlocked = TRUE;
g_free (thumbnailer_path);
g_object_unref (subprocess);
}
photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Waiting for org.gnome.Photos.Thumbnailer proxy");
g_mutex_lock (&self->mutex_thumbnailer);
while (self->thumbnailer == NULL && self->thumbnailer_error == NULL)
g_cond_wait (&self->cond_thumbnailer, &self->mutex_thumbnailer);
if (self->thumbnailer_error != NULL)
{
g_assert_null (self->thumbnailer);
photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Error creating org.gnome.Photos.Thumbnailer proxy");
local_error = self->thumbnailer_error;
self->thumbnailer_error = NULL;
}
else
{
const gchar *orientation_str;
gchar *thumbnail_path = NULL;
gchar *uri = NULL;
g_assert_true (PHOTOS_IS_THUMBNAILER_DBUS (self->thumbnailer));
uri = g_file_get_uri (file);
orientation_str = g_quark_to_string (orientation);
thumbnail_path = photos_utils_get_thumbnail_path_for_file (file);
photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Calling GenerateThumbnail for %s", uri);
ret_val = photos_thumbnailer_dbus_call_generate_thumbnail_sync (self->thumbnailer,
uri,
mime_type,
orientation_str,
original_height,
original_width,
pipeline_uri,
thumbnail_path,
thumbnail_size,
cancellable,
&local_error);
g_free (thumbnail_path);
g_free (uri);
}
g_mutex_unlock (&self->mutex_thumbnailer);
}
out:
if (!mutex_connection_unlocked)
g_mutex_unlock (&self->mutex_connection);
if (!ret_val)
{
g_assert_nonnull (local_error);
g_propagate_error (error, local_error);
}
g_clear_object (&pixbuf);
return ret_val;
}
/*
* Photos - access, organize and share your photos on GNOME
* Copyright © 2017 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef PHOTOS_THUMBNAIL_FACTORY_H
#define PHOTOS_THUMBNAIL_FACTORY_H
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gio/gio.h>
G_BEGIN_DECLS
#define PHOTOS_TYPE_THUMBNAIL_FACTORY (photos_thumbnail_factory_get_type ())
G_DECLARE_FINAL_TYPE (PhotosThumbnailFactory, photos_thumbnail_factory, PHOTOS, THUMBNAIL_FACTORY, GObject);
PhotosThumbnailFactory *photos_thumbnail_factory_dup_singleton (GCancellable *cancellable,
GError **error);
gboolean photos_thumbnail_factory_generate_thumbnail (PhotosThumbnailFactory *self,
GFile *file,
const gchar *mime_type,
GQuark orientation,
gint64 original_height,
gint64 original_width,
const gchar *pipeline_uri,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* PHOTOS_THUMBNAIL_FACTORY_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment