[Xfce4-commits] <tumbler:nick/cover-thumbnailer> Add config file system to control thumbnailing plugin.

Nick Schermer noreply at xfce.org
Sun Dec 16 15:32:03 CET 2012


Updating branch refs/heads/nick/cover-thumbnailer
         to cfb6c5ce6111d63c4e2a93ca6b16d39099e61230 (commit)
       from 466bd2294ee75ecfc23ad8d6eadc578a3f68f252 (commit)

commit cfb6c5ce6111d63c4e2a93ca6b16d39099e61230
Author: Nick Schermer <nick at xfce.org>
Date:   Sun Dec 16 15:30:09 2012 +0100

    Add config file system to control thumbnailing plugin.
    
    Settings that allow to change the plugin priority, max file
    size to act on, white-listed locations or completely disable
    them.

 configure.ac                                       |    4 +-
 .../cover-thumbnailer/cover-thumbnailer-provider.c |   81 ++-------
 plugins/cover-thumbnailer/cover-thumbnailer.c      |   92 +---------
 tumbler/tumbler-abstract-thumbnailer.c             |   48 +++++
 tumbler/tumbler-provider-factory.c                 |   28 +++-
 tumbler/tumbler-thumbnailer.c                      |   74 ++++++++-
 tumbler/tumbler-thumbnailer.h                      |    5 +
 tumbler/tumbler-util.c                             |   55 ++++++
 tumbler/tumbler-util.h                             |    2 +
 tumblerd/Makefile.am                               |    5 +
 tumblerd/main.c                                    |  191 ++++++++++++++++++++
 tumblerd/tumbler-registry.c                        |   81 +++++++--
 tumblerd/tumbler.rc                                |   95 ++++++++++
 13 files changed, 594 insertions(+), 167 deletions(-)

diff --git a/configure.ac b/configure.ac
index 80163e1..fea0484 100644
--- a/configure.ac
+++ b/configure.ac
@@ -108,13 +108,13 @@ dnl ***************************************
 AC_HEADER_STDC()
 AC_CHECK_HEADERS([fcntl.h linux/sched.h memory.h sched.h setjmp.h stdio.h \
                   stdlib.h string.h syscall.h sys/mman.h sys/types.h \
-                  sys/stat.h unistd.h sys/select.h])
+                  sys/stat.h unistd.h sys/select.h pwd.h])
 
 dnl ************************************
 dnl *** Check for standard functions ***
 dnl ************************************
 AC_FUNC_MMAP()
-AC_CHECK_FUNCS([sched_getparam sched_setscheduler])
+AC_CHECK_FUNCS([sched_getparam sched_setscheduler getpwnam])
 
 dnl ******************************
 dnl *** Check for i18n support ***
diff --git a/plugins/cover-thumbnailer/cover-thumbnailer-provider.c b/plugins/cover-thumbnailer/cover-thumbnailer-provider.c
index b29858c..d646dee 100644
--- a/plugins/cover-thumbnailer/cover-thumbnailer-provider.c
+++ b/plugins/cover-thumbnailer/cover-thumbnailer-provider.c
@@ -49,12 +49,6 @@ struct _CoverThumbnailerProviderClass
 struct _CoverThumbnailerProvider
 {
   GObject __parent__;
-
-  /* for themoviedb metadata */
-  gchar  *api_key;
-
-  /* allowed locations */
-  gchar **locations;
 };
 
 
@@ -105,22 +99,8 @@ cover_thumbnailer_provider_thumbnailer_provider_init (TumblerThumbnailerProvider
 static void
 cover_thumbnailer_provider_init (CoverThumbnailerProvider *provider)
 {
-  GKeyFile *keyfile;
-  gchar    *config_dir;
-
-  config_dir = g_build_filename (g_get_user_config_dir (), "tumbler", "cover.rc", NULL);
-  keyfile = g_key_file_new ();
-  if (g_key_file_load_from_file (keyfile, config_dir, G_KEY_FILE_NONE, NULL))
-    {
-      provider->api_key = g_key_file_get_string (keyfile, "TheMovieDB", "API-key", NULL);
-      provider->locations = g_key_file_get_string_list (keyfile, "General", "Locations", NULL, NULL);
-    }
-  g_key_file_free (keyfile);
-  g_free (config_dir);
-
   /* curl */
-  if (provider->locations != NULL)
-    curl_global_init (CURL_GLOBAL_ALL);
+  curl_global_init (CURL_GLOBAL_ALL);
 }
 
 
@@ -128,14 +108,8 @@ cover_thumbnailer_provider_init (CoverThumbnailerProvider *provider)
 static void
 cover_thumbnailer_provider_finalize (GObject *object)
 {
-  CoverThumbnailerProvider *provider = COVER_THUMBNAILER_PROVIDER (object);
-
-  g_free (provider->api_key);
-  g_strfreev (provider->locations);
-
   /* curl */
-  if (provider->locations != NULL)
-    curl_global_cleanup ();
+  curl_global_cleanup ();
 
   (*G_OBJECT_CLASS (cover_thumbnailer_provider_parent_class)->finalize) (object);
 }
@@ -145,11 +119,10 @@ cover_thumbnailer_provider_finalize (GObject *object)
 static GList *
 cover_thumbnailer_provider_get_thumbnailers (TumblerThumbnailerProvider *provider)
 {
-  CoverThumbnailerProvider *cover = COVER_THUMBNAILER_PROVIDER (provider);
-  CoverThumbnailer         *thumbnailer;
-  GList                    *thumbnailers = NULL;
-  GStrv                     uri_schemes;
-  static const gchar       *mime_types[] =
+  CoverThumbnailer   *thumbnailer;
+  GList              *thumbnailers = NULL;
+  GStrv               uri_schemes;
+  static const gchar *mime_types[] =
   {
     "video/divx",
     "video/jpeg",
@@ -167,34 +140,20 @@ cover_thumbnailer_provider_get_thumbnailers (TumblerThumbnailerProvider *provide
     NULL
   };
 
-  if (cover->locations != NULL)
-    {
-      /* determine the URI schemes supported by GIO */
-      uri_schemes = tumbler_util_get_supported_uri_schemes ();
-
-      /* create the pixbuf thumbnailer */
-      thumbnailer = g_object_new (TYPE_COVER_THUMBNAILER,
-                                  "uri-schemes", uri_schemes,
-                                  "mime-types", mime_types,
-                                  "locations", cover->locations,
-                                  "api-key", cover->api_key,
-                                  NULL);
-
-      /* add the thumbnailer to the list */
-      thumbnailers = g_list_append (thumbnailers, thumbnailer);
-
-      /* free URI schemes */
-      g_strfreev (uri_schemes);
-    }
-  else
-    {
-      g_print ("\n");
-      g_print (_("Cover thumbnailer is disabled because there are no "
-                 "locations white-listed in the config file. See "
-                 "%s for more information."),
-               "http://docs.xfce.org/xfce/thunar/tumbler");
-      g_print ("\n\n");
-    }
+  /* determine the URI schemes supported by GIO */
+  uri_schemes = tumbler_util_get_supported_uri_schemes ();
+
+  /* create the pixbuf thumbnailer */
+  thumbnailer = g_object_new (TYPE_COVER_THUMBNAILER,
+                              "uri-schemes", uri_schemes,
+                              "mime-types", mime_types,
+                              NULL);
+
+  /* add the thumbnailer to the list */
+  thumbnailers = g_list_append (thumbnailers, thumbnailer);
+
+  /* free URI schemes */
+  g_strfreev (uri_schemes);
 
   return thumbnailers;
 }
diff --git a/plugins/cover-thumbnailer/cover-thumbnailer.c b/plugins/cover-thumbnailer/cover-thumbnailer.c
index 14a70ff..cb3d708 100644
--- a/plugins/cover-thumbnailer/cover-thumbnailer.c
+++ b/plugins/cover-thumbnailer/cover-thumbnailer.c
@@ -54,10 +54,6 @@
 
 
 static void cover_thumbnailer_finalize     (GObject                    *object);
-static void cover_thumbnailer_set_property (GObject                    *object,
-                                            guint                       prop_id,
-                                            const GValue               *value,
-                                            GParamSpec                 *pspec);
 static void cover_thumbnailer_create       (TumblerAbstractThumbnailer *thumbnailer,
                                             GCancellable               *cancellable,
                                             TumblerFileInfo            *info);
@@ -76,9 +72,6 @@ struct _CoverThumbnailer
   /* themoviedb api key */
   gchar  *api_key;
 
-  /* whitelisted locations */
-  GSList *locations;
-
   /* precompiled */
   GRegex *series_regex;
   GRegex *abbrev_regex;
@@ -88,13 +81,6 @@ struct _CoverThumbnailer
   CURLM *curl_multi;
 };
 
-enum
-{
-  PROP_0,
-  PROP_LOCATIONS,
-  PROP_API_KEY
-};
-
 
 
 G_DEFINE_DYNAMIC_TYPE (CoverThumbnailer,
@@ -122,25 +108,6 @@ cover_thumbnailer_class_init (CoverThumbnailerClass *klass)
 
   gobject_class = G_OBJECT_CLASS (klass);
   gobject_class->finalize = cover_thumbnailer_finalize;
-  gobject_class->set_property = cover_thumbnailer_set_property;
-
-  g_object_class_install_property (gobject_class,
-                                   PROP_LOCATIONS,
-                                   g_param_spec_boxed ("locations",
-                                                       "locations",
-                                                       "locations",
-                                                       G_TYPE_STRV,
-                                                       G_PARAM_CONSTRUCT_ONLY |
-                                                       G_PARAM_WRITABLE));
-
-  g_object_class_install_property (gobject_class,
-                                   PROP_API_KEY,
-                                   g_param_spec_string ("api-key",
-                                                        "api-key",
-                                                        "api-key",
-                                                        NULL,
-                                                        G_PARAM_CONSTRUCT_ONLY |
-                                                        G_PARAM_WRITABLE));
 }
 
 
@@ -155,6 +122,8 @@ cover_thumbnailer_class_finalize (CoverThumbnailerClass *klass)
 static void
 cover_thumbnailer_init (CoverThumbnailer *thumbnailer)
 {
+  GKeyFile *rc;
+
   /* prepare the regular expressions */
   thumbnailer->series_regex = g_regex_new (SERIES_PATTERN, G_REGEX_CASELESS, 0, NULL);
   thumbnailer->abbrev_regex = g_regex_new (ABBREV_PATTERN, G_REGEX_CASELESS, 0, NULL);
@@ -162,41 +131,11 @@ cover_thumbnailer_init (CoverThumbnailer *thumbnailer)
 
   /* curl dns share */
   thumbnailer->curl_multi = curl_multi_init ();
-}
-
-
-
-static void
-cover_thumbnailer_set_property (GObject      *object,
-                                guint         prop_id,
-                                const GValue *value,
-                                GParamSpec   *pspec)
-{
-  CoverThumbnailer     *cover = COVER_THUMBNAILER (object);
-  guint                i;
-  const gchar * const *locations;
-  GFile               *gfile;
 
-  switch (prop_id)
-    {
-    case PROP_API_KEY:
-      cover->api_key = g_value_dup_string (value);
-      break;
-
-    case PROP_LOCATIONS:
-      locations = g_value_get_boxed (value);
-      g_assert (locations != NULL);
-      for (i = 0; locations[i] != NULL; i++)
-        {
-          gfile = g_file_new_for_commandline_arg (locations[i]);
-          cover->locations = g_slist_prepend (cover->locations, gfile);
-        }
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
+  /* read the api key */
+  rc = tumbler_util_get_settings ();
+  thumbnailer->api_key = g_key_file_get_string (rc, G_OBJECT_TYPE_NAME (thumbnailer), "APIKey", NULL);
+  g_key_file_free (rc);
 }
 
 
@@ -213,9 +152,6 @@ cover_thumbnailer_finalize (GObject *object)
 
   g_free (cover->api_key);
 
-  g_slist_foreach (cover->locations, (GFunc) g_object_unref, NULL);
-  g_slist_free (cover->locations);
-
   curl_multi_cleanup (cover->curl_multi);
 
   (*G_OBJECT_CLASS (cover_thumbnailer_parent_class)->finalize) (object);
@@ -718,27 +654,11 @@ cover_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
   TumblerImageData        data;
   TumblerThumbnailFlavor *flavor;
   GFile                  *gfile;
-  GSList                 *lp;
 
   /* source file */
   uri = tumbler_file_info_get_uri (info);
   gfile = g_file_new_for_uri (uri);
 
-  /* check if file is in allowed destinations */
-  for (lp = cover->locations; lp != NULL; lp = lp->next)
-    if (g_file_has_prefix (gfile, lp->data))
-      break;
-
-  if (lp == NULL)
-    {
-      /* location not white-listed */
-      g_object_unref (gfile);
-      g_signal_emit_by_name (thumbnailer, "error", uri,
-                             TUMBLER_ERROR_UNSUPPORTED,
-                             _("Location is not whitelisted in rc file"));
-      return;
-    }
-
   /* target data */
   thumbnail = tumbler_file_info_get_thumbnail (info);
   flavor = tumbler_thumbnail_get_flavor (thumbnail);
diff --git a/tumbler/tumbler-abstract-thumbnailer.c b/tumbler/tumbler-abstract-thumbnailer.c
index ea05c0f..0bb2649 100644
--- a/tumbler/tumbler-abstract-thumbnailer.c
+++ b/tumbler/tumbler-abstract-thumbnailer.c
@@ -42,6 +42,9 @@ enum
   PROP_URI_SCHEMES,
   PROP_MIME_TYPES,
   PROP_HASH_KEYS,
+  PROP_PRIORITY,
+  PROP_MAX_FILE_SIZE,
+  PROP_LOCATIONS
 };
 
 
@@ -68,6 +71,9 @@ struct _TumblerAbstractThumbnailerPrivate
   gchar **hash_keys;
   gchar **mime_types;
   gchar **uri_schemes;
+  gint    priority;
+  gint64  max_file_size;
+  GSList *locations;
 };
 
 
@@ -97,6 +103,9 @@ tumbler_abstract_thumbnailer_class_init (TumblerAbstractThumbnailerClass *klass)
   g_object_class_override_property (gobject_class, PROP_MIME_TYPES, "mime-types");
   g_object_class_override_property (gobject_class, PROP_URI_SCHEMES, "uri-schemes");
   g_object_class_override_property (gobject_class, PROP_HASH_KEYS, "hash-keys");
+  g_object_class_override_property (gobject_class, PROP_PRIORITY, "priority");
+  g_object_class_override_property (gobject_class, PROP_MAX_FILE_SIZE, "max-file-size");
+  g_object_class_override_property (gobject_class, PROP_LOCATIONS, "locations");
 }
 
 
@@ -173,6 +182,9 @@ tumbler_abstract_thumbnailer_finalize (GObject *object)
   g_strfreev (thumbnailer->priv->mime_types);
   g_strfreev (thumbnailer->priv->uri_schemes);
 
+  g_slist_foreach (thumbnailer->priv->locations, (GFunc) g_object_unref, NULL);
+  g_slist_free (thumbnailer->priv->locations);
+
   (*G_OBJECT_CLASS (tumbler_abstract_thumbnailer_parent_class)->finalize) (object);
 }
 
@@ -185,18 +197,36 @@ tumbler_abstract_thumbnailer_get_property (GObject    *object,
                                            GParamSpec *pspec)
 {
   TumblerAbstractThumbnailer *thumbnailer = TUMBLER_ABSTRACT_THUMBNAILER (object);
+  GSList                     *dup;
 
   switch (prop_id)
     {
     case PROP_MIME_TYPES:
       g_value_set_pointer (value, g_strdupv (thumbnailer->priv->mime_types));
       break;
+
     case PROP_URI_SCHEMES:
       g_value_set_pointer (value, g_strdupv (thumbnailer->priv->uri_schemes));
       break;
+
     case PROP_HASH_KEYS:
       g_value_set_pointer (value, g_strdupv (thumbnailer->priv->hash_keys));
       break;
+
+    case PROP_PRIORITY:
+      g_value_set_int (value, thumbnailer->priv->priority);
+      break;
+
+    case PROP_MAX_FILE_SIZE:
+      g_value_set_int64 (value, thumbnailer->priv->max_file_size);
+      break;
+
+    case PROP_LOCATIONS:
+      dup = g_slist_copy (thumbnailer->priv->locations);
+      g_slist_foreach (dup, (GFunc) g_object_ref, NULL);
+      g_value_set_pointer (value, dup);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -212,18 +242,36 @@ tumbler_abstract_thumbnailer_set_property (GObject      *object,
                                            GParamSpec   *pspec)
 {
   TumblerAbstractThumbnailer *thumbnailer = TUMBLER_ABSTRACT_THUMBNAILER (object);
+  GSList                     *dup;
 
   switch (prop_id)
     {
     case PROP_MIME_TYPES:
       thumbnailer->priv->mime_types = g_strdupv (g_value_get_pointer (value));
       break;
+
     case PROP_URI_SCHEMES:
       thumbnailer->priv->uri_schemes = g_strdupv (g_value_get_pointer (value));
       break;
+
     case PROP_HASH_KEYS:
       thumbnailer->priv->hash_keys = g_strdupv (g_value_get_pointer (value));
       break;
+
+    case PROP_PRIORITY:
+      thumbnailer->priv->priority = g_value_get_int (value);
+      break;
+
+    case PROP_MAX_FILE_SIZE:
+      thumbnailer->priv->max_file_size = g_value_get_int64 (value);
+      break;
+
+    case PROP_LOCATIONS:
+      dup = g_slist_copy (g_value_get_pointer (value));
+      g_slist_foreach (dup, (GFunc) g_object_ref, NULL);
+      thumbnailer->priv->locations = dup;
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
diff --git a/tumbler/tumbler-provider-factory.c b/tumbler/tumbler-provider-factory.c
index afa47e3..5c67147 100644
--- a/tumbler/tumbler-provider-factory.c
+++ b/tumbler/tumbler-provider-factory.c
@@ -22,11 +22,16 @@
 #include <config.h>
 #endif
 
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
 #include <glib.h>
 #include <glib-object.h>
 
 #include <tumbler/tumbler-provider-factory.h>
 #include <tumbler/tumbler-provider-plugin.h>
+#include <tumbler/tumbler-util.h>
 
 
 
@@ -253,17 +258,34 @@ tumbler_provider_factory_get_providers (TumblerProviderFactory *factory,
   GList               *plugins;
   GList               *providers = NULL;
   guint                n;
+  const gchar         *type_name;
+  gchar               *name;
+  gboolean             disabled;
+  GKeyFile            *rc;
 
   G_LOCK (factory_lock);
 
   /* load available plugins */
   plugins = tumbler_provider_factory_load_plugins (factory);
 
+  /* rc file */
+  rc = tumbler_util_get_settings ();
+
   /* iterate over all provider infos */
   for (n = 0; n < factory->provider_infos->len; ++n)
     {
       info = factory->provider_infos->pdata[n];
 
+      /* check if this plugin is disabled with the assumption
+       * the provider only provides 1 type */
+      type_name = g_type_name (info->type);
+      g_assert (g_str_has_suffix (type_name, "Provider"));
+      name = g_strndup (type_name, strlen (type_name) - 8);
+      disabled = g_key_file_get_boolean (rc, name, "Disabled", NULL);
+      g_free (name);
+      if (disabled)
+        continue;
+
       /* check if the provider type implements the given type */
       if (G_LIKELY (g_type_is_a (info->type, type)))
         {
@@ -271,8 +293,8 @@ tumbler_provider_factory_get_providers (TumblerProviderFactory *factory,
           if (info->provider == NULL)
             info->provider = g_object_new (info->type, NULL);
 
-          /* append the provider to the list */
-          providers = g_list_append (providers, g_object_ref (info->provider));
+          /* add the provider to the list */
+          providers = g_list_prepend (providers, g_object_ref (info->provider));
         }
     }
 
@@ -281,6 +303,8 @@ tumbler_provider_factory_get_providers (TumblerProviderFactory *factory,
     g_type_module_unuse (G_TYPE_MODULE (lp->data));
   g_list_free (plugins);
 
+  g_key_file_free (rc);
+
   G_UNLOCK (factory_lock);
 
   return providers;
diff --git a/tumbler/tumbler-thumbnailer.c b/tumbler/tumbler-thumbnailer.c
index 4c4d928..14e2b71 100644
--- a/tumbler/tumbler-thumbnailer.c
+++ b/tumbler/tumbler-thumbnailer.c
@@ -94,7 +94,28 @@ tumbler_thumbnailer_class_init (TumblerThumbnailerIface *klass)
                                        g_param_spec_pointer ("hash-keys",
                                                              "hash-keys",
                                                              "hash-keys",
-                                                             G_PARAM_READABLE));
+                                                             G_PARAM_CONSTRUCT_ONLY |
+                                                             G_PARAM_READWRITE));
+
+  g_object_interface_install_property (klass,
+                                       g_param_spec_int ("priority",
+                                                         "priority",
+                                                         "priority",
+                                                         0, G_MAXINT, 0,
+                                                         G_PARAM_READWRITE));
+
+  g_object_interface_install_property (klass,
+                                       g_param_spec_int64 ("max-file-size",
+                                                           "max-file-size",
+                                                           "max-file-size",
+                                                           0, G_MAXINT64, 0,
+                                                           G_PARAM_READWRITE));
+
+  g_object_interface_install_property (klass,
+                                       g_param_spec_pointer ("locations",
+                                                             "locations",
+                                                             "locations",
+                                                             G_PARAM_READWRITE));
 
   tumbler_thumbnailer_signals[SIGNAL_READY] = 
     g_signal_new ("ready",
@@ -191,6 +212,57 @@ tumbler_thumbnailer_get_uri_schemes (TumblerThumbnailer *thumbnailer)
 
 
 
+gint
+tumbler_thumbnailer_get_priority (TumblerThumbnailer *thumbnailer)
+{
+  gint priority;
+
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAILER (thumbnailer), 0);
+
+  g_object_get (thumbnailer, "priority", &priority, NULL);
+  return priority;
+}
+
+
+
+gint64
+tumbler_thumbnailer_get_max_file_size (TumblerThumbnailer *thumbnailer)
+{
+  gint max_file_size;
+
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAILER (thumbnailer), 0);
+
+  g_object_get (thumbnailer, "max-file-size", &max_file_size, NULL);
+  return max_file_size;
+}
+
+
+
+gboolean
+tumbler_thumbnailer_supports_location (TumblerThumbnailer *thumbnailer,
+                                       GFile              *file)
+{
+  GSList   *locations, *lp;
+  gboolean  supported = FALSE;
+
+  /* we're cool if no locations are set */
+  g_object_get (thumbnailer, "locations", &locations, NULL);
+  if (locations == NULL)
+    return TRUE;
+
+  /*check if the prefix is supported */
+  for (lp = locations; !supported && lp != NULL; lp = lp->next)
+    if (g_file_has_prefix (file, G_FILE (lp->data)))
+      supported = TRUE;
+
+  g_slist_foreach (locations, (GFunc) g_object_unref, NULL);
+  g_slist_free (locations);
+
+  return supported;
+}
+
+
+
 gboolean
 tumbler_thumbnailer_supports_hash_key (TumblerThumbnailer *thumbnailer,
                                        const gchar        *hash_key)
diff --git a/tumbler/tumbler-thumbnailer.h b/tumbler/tumbler-thumbnailer.h
index 00abbbb..313e404 100644
--- a/tumbler/tumbler-thumbnailer.h
+++ b/tumbler/tumbler-thumbnailer.h
@@ -68,6 +68,11 @@ void                 tumbler_thumbnailer_create            (TumblerThumbnailer
 gchar              **tumbler_thumbnailer_get_hash_keys     (TumblerThumbnailer  *thumbnailer);
 gchar              **tumbler_thumbnailer_get_mime_types    (TumblerThumbnailer  *thumbnailer);
 gchar              **tumbler_thumbnailer_get_uri_schemes   (TumblerThumbnailer  *thumbnailer);
+gint                 tumbler_thumbnailer_get_priority      (TumblerThumbnailer  *thumbnailer);
+gint64               tumbler_thumbnailer_get_max_file_size (TumblerThumbnailer  *thumbnailer);
+
+gboolean             tumbler_thumbnailer_supports_location (TumblerThumbnailer  *thumbnailer,
+                                                            GFile               *file);
 gboolean             tumbler_thumbnailer_supports_hash_key (TumblerThumbnailer  *thumbnailer,
                                                             const gchar         *hash_key);
 
diff --git a/tumbler/tumbler-util.c b/tumbler/tumbler-util.c
index bb36be4..9d656d5 100644
--- a/tumbler/tumbler-util.c
+++ b/tumbler/tumbler-util.c
@@ -75,3 +75,58 @@ tumbler_util_get_supported_uri_schemes (void)
 
   return uri_schemes;
 }
+
+
+static gchar *
+tumbler_util_get_settings_filename (void)
+{
+  gchar               *path;
+  const gchar          filename[] = "tumbler" G_DIR_SEPARATOR_S "tumbler.rc";
+  const gchar * const *dirs;
+  guint                n;
+
+  /* check user directory */
+  path = g_build_filename (g_get_user_config_dir (), filename, NULL);
+  if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
+    return path;
+  g_free (path);
+
+  dirs = g_get_system_config_dirs ();
+  if (G_UNLIKELY (dirs == NULL))
+    return FALSE;
+
+  /* look in system config dirs */
+  for (n = 0; dirs[n] != NULL; n++)
+    {
+      path = g_build_filename (dirs[n], filename, NULL);
+      if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
+        return path;
+      g_free (path);
+    }
+
+  return NULL;
+}
+
+
+
+GKeyFile *
+tumbler_util_get_settings (void)
+{
+  GKeyFile *settings;
+  GError   *err = NULL;
+  gchar    *filename;
+
+  settings = g_key_file_new ();
+  filename = tumbler_util_get_settings_filename ();
+
+  if (filename != NULL
+      && !g_key_file_load_from_file (settings, filename, 0, &err))
+    {
+      g_critical ("Unable to load settings from \"%s\": %s", filename, err->message);
+      g_error_free (err);
+    }
+
+  g_free (filename);
+
+  return settings;
+}
diff --git a/tumbler/tumbler-util.h b/tumbler/tumbler-util.h
index eeba4d1..b68db0a 100644
--- a/tumbler/tumbler-util.h
+++ b/tumbler/tumbler-util.h
@@ -27,6 +27,8 @@ G_BEGIN_DECLS
 
 gchar **tumbler_util_get_supported_uri_schemes (void) G_GNUC_MALLOC;
 
+GKeyFile *tumbler_util_get_settings (void) G_GNUC_MALLOC;
+
 G_END_DECLS
 
 #endif /* !__TUMBLER_UTIL_H__ */
diff --git a/tumblerd/Makefile.am b/tumblerd/Makefile.am
index e25a34d..6dfd249 100644
--- a/tumblerd/Makefile.am
+++ b/tumblerd/Makefile.am
@@ -94,11 +94,16 @@ service_DATA = $(service_in_files:.service.in=.service)
 	sed -e "s,\@libdir\@,$(libdir),g"				\
 	    -e "s,\@TUMBLER_VERSION_API\@,$(TUMBLER_VERSION_API),g" < $< > $@
 
+confdir = $(sysconfdir)/xdg/tumbler
+conf_DATA = \
+	tumbler.rc
+
 CLEANFILES =								\
 	$(service_DATA)
 
 EXTRA_DIST =								\
 	$(service_in_files)						\
+	tumbler.rc							\
 	tumbler-cache-service-dbus.xml					\
 	tumbler-manager-dbus.xml					\
 	tumbler-service-dbus.xml
diff --git a/tumblerd/main.c b/tumblerd/main.c
index 046c13f..1a7face 100644
--- a/tumblerd/main.c
+++ b/tumblerd/main.c
@@ -22,6 +22,13 @@
 #include <config.h>
 #endif
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
 #include <stdlib.h>
 
 #include <glib.h>
@@ -53,6 +60,159 @@ shutdown_tumbler (TumblerLifecycleManager *lifecycle_manager,
 
 
 
+static inline gboolean
+xfce_is_valid_tilde_prefix (const gchar *p)
+{
+  if (g_ascii_isspace (*p) /* thunar ~/music */
+      || *p == '=' /* terminal --working-directory=~/ */
+      || *p == '\'' || *p == '"') /* terminal --working-directory '~my music' */
+    return TRUE;
+
+  return FALSE;
+}
+
+
+/* from libxfce4util */
+static gchar *
+xfce_expand_variables (const gchar *command,
+                       gchar      **envp)
+{
+  GString        *buf;
+  const gchar    *start;
+  gchar          *variable;
+  const gchar    *p;
+  const gchar    *value;
+  gchar         **ep;
+  guint           len;
+#ifdef HAVE_GETPWNAM
+  struct passwd  *pw;
+  gchar          *username;
+#endif
+
+  if (G_UNLIKELY (command == NULL))
+    return NULL;
+
+  buf = g_string_sized_new (strlen (command));
+
+  for (p = command; *p != '\0'; ++p)
+    {
+      continue_without_increase:
+
+      if (*p == '~'
+          && (p == command
+              || xfce_is_valid_tilde_prefix (p - 1)))
+        {
+          /* walk to the end of the string or to a directory separator */
+          for (start = ++p; *p != '\0' && *p != G_DIR_SEPARATOR; ++p);
+
+          if (G_LIKELY (start == p))
+            {
+              /* add the current user directory */
+              buf = g_string_append (buf, g_get_home_dir ());
+            }
+          else
+            {
+#ifdef HAVE_GETPWNAM
+              username = g_strndup (start, p - start);
+              pw = getpwnam (username);
+              g_free (username);
+
+              /* add the users' home directory if found, fallback to the
+               * not-expanded string */
+              if (pw != NULL && pw->pw_dir != NULL)
+                buf = g_string_append (buf, pw->pw_dir);
+              else
+#endif
+                buf = g_string_append_len (buf, start - 1, p - start + 1);
+            }
+
+          /* we are either at the end of the string or *p is a separator,
+           * so continue to add it to the result buffer */
+        }
+      else if (*p == '$')
+        {
+          /* walk to the end of a valid variable name */
+          for (start = ++p; *p != '\0' && (g_ascii_isalnum (*p) || *p == '_'); ++p);
+
+          if (start < p)
+            {
+              value = NULL;
+              len = p - start;
+
+              /* lookup the variable in the environment supplied by the user */
+              if (envp != NULL)
+                {
+                  /* format is NAME=VALUE */
+                  for (ep = envp; *ep != NULL; ++ep)
+                    if (strncmp (*ep, start, len) == 0
+                        && (*ep)[len] == '=')
+                      {
+                        value = (*ep) + len + 1;
+                        break;
+                      }
+                }
+
+              /* fallback to the environment */
+              if (value == NULL)
+                {
+                  variable = g_strndup (start, len);
+                  value = g_getenv (variable);
+                  g_free (variable);
+                }
+
+              if (G_LIKELY (value != NULL))
+                {
+                  buf = g_string_append (buf, value);
+                }
+              else
+                {
+                  /* the variable name was valid, but no value was
+                   * found, insert nothing and continue */
+                }
+
+              /* *p is at the start of the charater after the variable,
+               * so continue scanning without advancing the string offset
+               * so two variables are replaced properly */
+              goto continue_without_increase;
+            }
+          else
+            {
+              /* invalid variable format, add the
+               * $ character and continue */
+              --p;
+            }
+        }
+
+      buf = g_string_append_c (buf, *p);
+    }
+
+  return g_string_free (buf, FALSE);
+}
+
+
+
+static GSList *
+locations_from_strv (gchar **array)
+{
+  GSList *locations = NULL;
+  guint   n;
+  gchar  *path;
+
+  if (array == NULL)
+    return NULL;
+
+  for (n = 0; array[n] != NULL; n++)
+    {
+      path = xfce_expand_variables (array[n], NULL);
+      locations = g_slist_prepend (locations, g_file_new_for_commandline_arg (path));
+      g_free (path);
+    }
+
+  return locations;
+}
+
+
+
 int
 main (int    argc,
       char **argv)
@@ -71,6 +231,12 @@ main (int    argc,
   GList                   *lp;
   GList                   *tp;
   gint                     retval = EXIT_SUCCESS;
+  GKeyFile                *rc;
+  gint64                   file_size;
+  gint                     priority;
+  const gchar             *type_name;
+  gchar                  **paths;
+  GSList                  *locations;
 
   /* set the program name */
   g_set_prgname (G_LOG_DOMAIN);
@@ -120,6 +286,9 @@ main (int    argc,
   providers = tumbler_provider_factory_get_providers (provider_factory,
                                                       TUMBLER_TYPE_THUMBNAILER_PROVIDER);
 
+  /* settings */
+  rc = tumbler_util_get_settings ();
+
   /* iterate over all providers */
   for (lp = providers; lp != NULL; lp = lp->next)
     {
@@ -129,14 +298,36 @@ main (int    argc,
       /* add all thumbnailers to the registry */
       for (tp = thumbnailers; tp != NULL; tp = tp->next)
         {
+          /* set settings from rc file */
+          type_name = G_OBJECT_TYPE_NAME (tp->data);
+          priority = g_key_file_get_integer (rc, type_name, "Priority", NULL);
+          file_size = g_key_file_get_int64 (rc, type_name, "MaxFileSize", NULL);
+
+          paths = g_key_file_get_string_list (rc, type_name, "Locations", NULL, NULL);
+          locations = locations_from_strv (paths);
+          g_strfreev (paths);
+
+          g_object_set (G_OBJECT (tp->data),
+                        "priority", priority,
+                        "max-file-size", file_size,
+                        "locations", locations,
+                        NULL);
+
+          /* ready for usage */
           tumbler_registry_add (registry, tp->data);
+
+          /* cleanup */
           g_object_unref (tp->data);
+          g_slist_foreach (locations, (GFunc) g_object_unref, NULL);
+          g_slist_free (locations);
         }
 
       /* free the thumbnailer list */
       g_list_free (thumbnailers);
     }
 
+  g_key_file_free (rc);
+
   /* release all providers and free the provider list */
   g_list_foreach (providers, (GFunc) g_object_unref, NULL);
   g_list_free (providers);
diff --git a/tumblerd/tumbler-registry.c b/tumblerd/tumbler-registry.c
index c75ca61..61739ac 100644
--- a/tumblerd/tumbler-registry.c
+++ b/tumblerd/tumbler-registry.c
@@ -41,7 +41,7 @@ static void                tumbler_registry_list_free                 (gpointer
 static GList              *tumbler_registry_get_thumbnailers_internal (TumblerRegistry    *registry);
 static gint                tumbler_registry_compare                   (TumblerThumbnailer *a,
                                                                        TumblerThumbnailer *b);
-static TumblerThumbnailer *tumbler_registry_lookup                    (TumblerRegistry    *registry,
+static GList              *tumbler_registry_lookup                    (TumblerRegistry    *registry,
                                                                        const gchar        *hash_key);
 
 
@@ -175,8 +175,8 @@ tumbler_registry_compare (TumblerThumbnailer *a,
 
   if (!TUMBLER_IS_SPECIALIZED_THUMBNAILER (a) || !TUMBLER_IS_SPECIALIZED_THUMBNAILER (b))
     {
-      /* plugin thumbnailers are always overriden */
-      insert_a_before_b = TRUE;
+      /* sort by priority */
+      insert_a_before_b = tumbler_thumbnailer_get_priority (a) >= tumbler_thumbnailer_get_priority (b);
     }
   else if (TUMBLER_IS_SPECIALIZED_THUMBNAILER (b))
     {
@@ -260,32 +260,50 @@ tumbler_registry_get_thumbnailers_internal (TumblerRegistry *registry)
 
 
 
-static TumblerThumbnailer *
+static GList *
 tumbler_registry_lookup (TumblerRegistry *registry,
                          const gchar     *hash_key)
 {
   TumblerThumbnailer *thumbnailer = NULL;
   GList             **list;
-  GList              *first;
+  GList              *available = NULL;
+  GList              *lp;
 
   g_return_val_if_fail (TUMBLER_IS_REGISTRY (registry), NULL);
   g_return_val_if_fail (hash_key != NULL, NULL);
 
   thumbnailer = g_hash_table_lookup (registry->preferred_thumbnailers, hash_key);
   if (thumbnailer != NULL)
-    return g_object_ref (thumbnailer);
+    available = g_list_prepend (available, g_object_ref (thumbnailer));
 
   list = g_hash_table_lookup (registry->thumbnailers, hash_key);
-
   if (list != NULL)
     {
-      first = g_list_first (*list);
+      for (lp = *list; lp != NULL; lp = lp->next)
+        available = g_list_prepend (available, g_object_ref (lp->data));
+    }
+
+  return g_list_reverse (available);
+}
 
-      if (first != NULL)
-        thumbnailer = g_object_ref (first->data);
+
+
+static gint64
+tumbler_registry_get_file_size (GFile *gfile)
+{
+  GFileInfo *file_info;
+  gint64     size = 0;
+
+  file_info = g_file_query_info (gfile,
+                                 G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                 G_FILE_QUERY_INFO_NONE, NULL, NULL);
+  if (file_info != NULL)
+    {
+      size = g_file_info_get_size (file_info);
+      g_object_unref (file_info);
     }
 
-  return thumbnailer;
+  return size;
 }
 
 
@@ -419,6 +437,10 @@ tumbler_registry_get_thumbnailer_array (TumblerRegistry    *registry,
   gchar               *hash_key;
   gchar               *scheme;
   guint                n;
+  GList               *list, *lp;
+  GFile               *gfile;
+  gint64               file_size;
+  gint64               max_file_size;
 
   g_return_val_if_fail (TUMBLER_IS_REGISTRY (registry), NULL);
   g_return_val_if_fail (infos != NULL, NULL);
@@ -433,17 +455,46 @@ tumbler_registry_get_thumbnailer_array (TumblerRegistry    *registry,
     {
       tumbler_mutex_lock (registry->mutex);
 
+      /* reset */
+      file_size = 0;
+
       /* determine the URI scheme and generate a hash key */
-      scheme = g_uri_parse_scheme (tumbler_file_info_get_uri (infos[n]));
+      gfile = g_file_new_for_uri (tumbler_file_info_get_uri (infos[n]));
+      scheme = g_file_get_uri_scheme (gfile);
       hash_key = g_strdup_printf ("%s-%s", scheme, 
                                   tumbler_file_info_get_mime_type (infos[n]));
 
-      /* see if we can find a thumbnailer to handle this URI/MIME type pair */
-      thumbnailers[n] = tumbler_registry_lookup (registry, hash_key);
+      /* get list of thumbnailer that can handle this URI/MIME type pair */
+      list = tumbler_registry_lookup (registry, hash_key);
+      for (lp = list; lp != NULL; lp = lp->next)
+        {
+          /* check if the file size is a limitation */
+          max_file_size = tumbler_thumbnailer_get_max_file_size (lp->data);
+          if (max_file_size > 0)
+            {
+              /* load the source's size on demand */
+              if (file_size == 0)
+                file_size = tumbler_registry_get_file_size (gfile);
+              if (file_size > max_file_size)
+                continue;
+            }
+
+          /* check if the location is supported */
+          if (!tumbler_thumbnailer_supports_location (lp->data, gfile))
+            continue;
+
+          /* found a usable thumbnailer */
+          thumbnailers[n] = g_object_ref (lp->data);
+
+          break;
+        }
 
-      /* free strings */
+      /* cleanup */
       g_free (hash_key);
       g_free (scheme);
+      g_object_unref (gfile);
+      g_list_foreach (list, (GFunc) g_object_unref, NULL);
+      g_list_free (list);
 
       tumbler_mutex_unlock (registry->mutex);
     }
diff --git a/tumblerd/tumbler.rc b/tumblerd/tumbler.rc
new file mode 100644
index 0000000..52e73f3
--- /dev/null
+++ b/tumblerd/tumbler.rc
@@ -0,0 +1,95 @@
+###
+# [TypeNameOfPlugin]
+# Disabled:    Set to true to avoid loading the plugin. By default all
+#              plugins are loaded.
+# Priority:    Priority of the plugin if more plugins support the same
+#              uri-scheme / mime-type combination.
+# Locations:   ;-separated path list the plugin will be used in. If the
+#              source file is not a child of one of the locations, the
+#              plugin won't be used and another plugin with a lower
+#              priority will be tried.
+#              Absolute paths, environement variables, ~/ and ~username/
+#              are allowed. Leave empty to allow all locations.
+# MaxFileSize: Maximum size of the source file the plugin will still
+#              try to generate a plugin for. The size is in bytes,
+#              0 disabled the check.
+###
+
+###
+# Image Thumbnailers
+###
+
+# Jpeg thumbnailer (from exif data if possible)
+[JPEGThumbnailer]
+Disabled=false
+Priority=3
+Locations=
+MaxFileSize=0
+
+# Supports all type GdkPixbuf supports
+[PixbufThumbnailer]
+Disabled=false
+Priority=2
+Locations=
+MaxFileSize=0
+
+# RAW image files using libopenraw
+[RawThumbnailer]
+Disabled=false
+Priority=1
+Locations=
+MaxFileSize=0
+
+###
+# Video Thumbnailers
+###
+
+# Download cover from omdbapi.com or themoviedb.org if an
+# API key is given. This plugin is disabled because it
+# sends your (private) movie names over the internet.
+[CoverThumbnailer]
+Disabled=true
+Priority=3
+Locations=~/movies
+MaxFileSize=0
+#APIKey=your-api-key-from-themoviedb.org
+
+# ffmpegthumbnailer plugin
+[FfmegThumbnailer]
+Disabled=false
+Priority=2
+Locations=
+MaxFileSize=0
+
+# GStreamer plugin
+[GstThumbnailer]
+Disabled=false
+Priority=1
+Locations=
+MaxFileSize=0
+
+###
+# Other Thumbnailers
+###
+
+# FreeType thumbnailer
+[FontThumbnailer]
+Disabled=false
+Priority=1
+Locations=
+MaxFileSize=0
+
+
+# PDF/PS thumbnailer
+[PopplerThumbnailer]
+Disabled=false
+Priority=1
+Locations=
+MaxFileSize=0
+
+# Open document thumbnailer (ODF)
+[OdfThumbnailer]
+Disabled=false
+Priority=1
+Locations=
+MaxFileSize=0


More information about the Xfce4-commits mailing list