From efb7babcd0c49c793df526fef7288cddbd8fe93b Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 7 Jul 2015 18:31:52 +0100 Subject: [PATCH 3/4] xdg-cache: Support loading thumbnails from shared repositories The thumbnail specification defines shared repositories as optional read-only repositories stored relative to the media files, which can therefore be shared between multiple users and computers. This could be useful for removable media, for example. http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#SHARED Add read-only support for shared repositories, preferring to load the thumbnail from the local repository ($XDG_CACHE_HOME/thumbnails), but using the shared repository if that fails. Based on a patch by Jeremy Whiting . https://bugzilla.xfce.org/show_bug.cgi?id=12036 --- plugins/xdg-cache/xdg-cache-cache.c | 186 ++++++++++++++++++++++---------- plugins/xdg-cache/xdg-cache-cache.h | 6 +- plugins/xdg-cache/xdg-cache-thumbnail.c | 53 ++++++++- 3 files changed, 181 insertions(+), 64 deletions(-) diff --git a/plugins/xdg-cache/xdg-cache-cache.c b/plugins/xdg-cache/xdg-cache-cache.c index 313cf8a..29efdbd 100644 --- a/plugins/xdg-cache/xdg-cache-cache.c +++ b/plugins/xdg-cache/xdg-cache-cache.c @@ -193,7 +193,7 @@ xdg_cache_cache_cleanup (TumblerCache *cache, for (iter = xdg_cache->flavors; iter != NULL; iter = iter->next) { /* compute the flavor directory filename */ - dummy_file = xdg_cache_cache_get_file ("foo", iter->data); + dummy_file = xdg_cache_cache_get_file ("foo", iter->data, FALSE); parent = g_file_get_parent (dummy_file); dirname = g_file_get_path (parent); g_object_unref (parent); @@ -278,11 +278,13 @@ xdg_cache_cache_delete (TumblerCache *cache, g_return_if_fail (XDG_CACHE_IS_CACHE (cache)); g_return_if_fail (uris != NULL); + /* Delete all flavours of the given URIs, but only from the local repository, + * not any shared repository — they are read-only. */ for (iter = xdg_cache->flavors; iter != NULL; iter = iter->next) { for (n = 0; uris[n] != NULL; ++n) { - file = xdg_cache_cache_get_file (uris[n], iter->data); + file = xdg_cache_cache_get_file (uris[n], iter->data, FALSE); g_file_delete (file, NULL, NULL); g_object_unref (file); } @@ -291,13 +293,14 @@ xdg_cache_cache_delete (TumblerCache *cache, -static void -xdg_cache_cache_copy_or_move_file (TumblerCache *cache, - TumblerThumbnailFlavor *flavor, - gboolean do_copy, - const gchar *from_uri, - const gchar *to_uri, - guint64 mtime) +static gboolean +_xdg_cache_cache_copy_or_move_file (TumblerCache *cache, + TumblerThumbnailFlavor *flavor, + gboolean do_copy, + const gchar *from_uri, + const gchar *to_uri, + guint64 mtime, + gboolean shared) { GFile *from_file; GFile *temp_file; @@ -307,8 +310,8 @@ xdg_cache_cache_copy_or_move_file (TumblerCache *cache, gboolean result; GFile *dest_file; - from_file = xdg_cache_cache_get_file (from_uri, flavor); - temp_file = xdg_cache_cache_get_temp_file (to_uri, flavor); + from_file = xdg_cache_cache_get_file (from_uri, flavor, shared); + temp_file = xdg_cache_cache_get_temp_file (to_uri, flavor, shared); if (do_copy) { @@ -328,7 +331,9 @@ xdg_cache_cache_copy_or_move_file (TumblerCache *cache, if (xdg_cache_cache_write_thumbnail_info (temp_path, to_uri, mtime, NULL, NULL)) { - dest_file = xdg_cache_cache_get_file (to_uri, flavor); + /* Always write to the local repository, as shared repositories are + * read-only. */ + dest_file = xdg_cache_cache_get_file (to_uri, flavor, FALSE); dest_path = g_file_get_path (dest_file); if (g_rename (temp_path, dest_path) != 0) @@ -354,6 +359,26 @@ xdg_cache_cache_copy_or_move_file (TumblerCache *cache, g_object_unref (temp_file); g_object_unref (from_file); + + return result; +} + +static void +xdg_cache_cache_copy_or_move_file (TumblerCache *cache, + TumblerThumbnailFlavor *flavor, + gboolean do_copy, + const gchar *from_uri, + const gchar *to_uri, + guint64 mtime) +{ + /* Try copying from the local repository, then from the shared repository if + * that fails. */ + if (!_xdg_cache_cache_copy_or_move_file (cache, flavor, do_copy, from_uri, + to_uri, mtime, FALSE)) + { + _xdg_cache_cache_copy_or_move_file (cache, flavor, do_copy, from_uri, + to_uri, mtime, TRUE); + } } static void @@ -375,8 +400,10 @@ xdg_cache_cache_copy_or_move_directory (TumblerCache *cache, gchar *file_to_uri; guint64 mtime; - /* compute the flavor directory filename */ - dummy_file = xdg_cache_cache_get_file ("foo", flavor); + /* Compute the flavor directory filename. Only use the local repository — any + * shared repository for a directory which has been copied or moved will have + * been copied or moved with the directory. */ + dummy_file = xdg_cache_cache_get_file ("foo", flavor, FALSE); parent = g_file_get_parent (dummy_file); dirname = g_file_get_path (parent); g_object_unref (parent); @@ -567,72 +594,115 @@ xdg_cache_cache_get_flavors (TumblerCache *cache) return flavors; } - - -GFile * -xdg_cache_cache_get_file (const gchar *uri, - TumblerThumbnailFlavor *flavor) +static gchar * +build_cache_directory (const gchar *uri, + TumblerThumbnailFlavor *flavor, + gboolean shared) { - const gchar *cachedir; const gchar *dirname; - GFile *file; - gchar *filename; - gchar *md5_hash; - gchar *path; + gchar *path = NULL; - g_return_val_if_fail (uri != NULL && *uri != '\0', NULL); - g_return_val_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor), NULL); - - cachedir = g_get_user_cache_dir (); dirname = tumbler_thumbnail_flavor_get_name (flavor); - md5_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1); - filename = g_strdup_printf ("%s.png", md5_hash); - path = g_build_filename (cachedir, "thumbnails", dirname, filename, NULL); + if (shared) { + GFile *file = NULL; + GFile *parent_file = NULL; + gchar *parent_path = NULL; - file = g_file_new_for_path (path); + /* Shared repository. + * See: http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#SHARED */ + file = g_file_new_for_uri (uri); + parent_file = g_file_get_parent (file); + parent_path = g_file_get_path (parent_file); + g_object_unref (parent_file); + g_object_unref (file); + + path = g_build_filename (parent_path, ".sh_thumbnails", dirname, NULL); + + g_free (parent_path); + } else { + const gchar *cachedir; + + /* Local repository. + * See: http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#DIRECTORY */ + cachedir = g_get_user_cache_dir (); + path = g_build_filename (cachedir, "thumbnails", dirname, NULL); + } + + return path; +} + +static GFile * +build_cache_file_path (const gchar *uri, + TumblerThumbnailFlavor *flavor, + gboolean shared, + const GTimeVal *current_time) +{ + gchar *directory = NULL; + gchar *path = NULL; + GFile *file = NULL; + gchar *cache_filename = NULL; + gchar *md5_hash = NULL; + + /* Work out the MD5 sum. For shared repositories, this is calculated from the + * basename of the URI, because the shared repository could move. + * + * See: http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#SHARED */ + if (shared) { + gchar *uri_basename = g_path_get_basename (uri); + md5_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri_basename, -1); + g_free (uri_basename); + } else { + md5_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1); + } + + if (current_time != NULL) { + cache_filename = g_strdup_printf ("%s-%ld-%ld.png", md5_hash, + current_time->tv_sec, + current_time->tv_usec); + } else { + cache_filename = g_strdup_printf ("%s.png", md5_hash); + } - g_free (path); - g_free (filename); g_free (md5_hash); + directory = build_cache_directory (uri, flavor, shared); + path = g_build_filename (directory, cache_filename, NULL); + g_free (directory); + + g_debug ("%s: Built path ‘%s’ for URI ‘%s’, %s.", G_STRFUNC, path, uri, + shared ? "shared" : "not shared"); + file = g_file_new_for_path (path); + g_free (path); + + g_free (cache_filename); + return file; } +GFile * +xdg_cache_cache_get_file (const gchar *uri, + TumblerThumbnailFlavor *flavor, + gboolean shared) +{ + g_return_val_if_fail (uri != NULL && *uri != '\0', NULL); + g_return_val_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor), NULL); + return build_cache_file_path (uri, flavor, shared, NULL); +} GFile * -xdg_cache_cache_get_temp_file (const gchar *uri, - TumblerThumbnailFlavor *flavor) +xdg_cache_cache_get_temp_file (const gchar *uri, + TumblerThumbnailFlavor *flavor, + gboolean shared) { - const gchar *cachedir; - const gchar *dirname; GTimeVal current_time = { 0, 0 }; - GFile *file; - gchar *filename; - gchar *md5_hash; - gchar *path; g_return_val_if_fail (uri != NULL && *uri != '\0', NULL); g_return_val_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor), NULL); - cachedir = g_get_user_cache_dir (); - dirname = tumbler_thumbnail_flavor_get_name (flavor); - g_get_current_time (¤t_time); - - md5_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1); - filename = g_strdup_printf ("%s-%ld-%ld.png", md5_hash, - current_time.tv_sec, current_time.tv_usec); - path = g_build_filename (cachedir, "thumbnails", dirname, filename, NULL); - - file = g_file_new_for_path (path); - - g_free (path); - g_free (filename); - g_free (md5_hash); - - return file; + return build_cache_file_path (uri, flavor, shared, ¤t_time); } diff --git a/plugins/xdg-cache/xdg-cache-cache.h b/plugins/xdg-cache/xdg-cache-cache.h index b2037e9..7965cda 100644 --- a/plugins/xdg-cache/xdg-cache-cache.h +++ b/plugins/xdg-cache/xdg-cache-cache.h @@ -41,9 +41,11 @@ GType xdg_cache_cache_get_type (void) G_GNUC_CONST; void xdg_cache_cache_register (TumblerCachePlugin *plugin); GFile *xdg_cache_cache_get_file (const gchar *uri, - TumblerThumbnailFlavor *flavor) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; + TumblerThumbnailFlavor *flavor, + gboolean shared) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; GFile *xdg_cache_cache_get_temp_file (const gchar *uri, - TumblerThumbnailFlavor *flavor) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; + TumblerThumbnailFlavor *flavor, + gboolean shared) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; gboolean xdg_cache_cache_read_thumbnail_info (const gchar *filename, gchar **uri, guint64 *mtime, diff --git a/plugins/xdg-cache/xdg-cache-thumbnail.c b/plugins/xdg-cache/xdg-cache-thumbnail.c index ea6a0d8..8d5cf5a 100644 --- a/plugins/xdg-cache/xdg-cache-thumbnail.c +++ b/plugins/xdg-cache/xdg-cache-thumbnail.c @@ -235,8 +235,10 @@ xdg_cache_thumbnail_load (TumblerThumbnail *thumbnail, g_return_val_if_fail (cache_thumbnail->uri != NULL, FALSE); g_return_val_if_fail (XDG_CACHE_IS_CACHE (cache_thumbnail->cache), FALSE); + /* Always try the local repository first. */ file = xdg_cache_cache_get_file (cache_thumbnail->uri, - cache_thumbnail->flavor); + cache_thumbnail->flavor, + FALSE); path = g_file_get_path (file); g_object_unref (file); @@ -249,6 +251,29 @@ xdg_cache_thumbnail_load (TumblerThumbnail *thumbnail, &cache_thumbnail->cached_mtime, cancellable, &err); + /* If that thumbnail isn’t valid or is corrupt, try the shared repository, + * if appropriate. */ + if (err != NULL || + cache_thumbnail->cached_uri == NULL || + cache_thumbnail->cached_mtime == 0) { + g_free (path); + g_clear_error (&err); + g_free (cache_thumbnail->cached_uri); + cache_thumbnail->cached_uri = NULL; + cache_thumbnail->cached_mtime = 0; + + file = xdg_cache_cache_get_file (cache_thumbnail->uri, + cache_thumbnail->flavor, + TRUE); + path = g_file_get_path (file); + g_object_unref (file); + + xdg_cache_cache_read_thumbnail_info (path, + &cache_thumbnail->cached_uri, + &cache_thumbnail->cached_mtime, + cancellable, &err); + } + /* free the filename */ g_free (path); @@ -308,6 +333,8 @@ xdg_cache_thumbnail_save_image_data (TumblerThumbnail *thumbnail, gchar *mtime_str; gint width; gint height; + gboolean shared; + gchar *temp_file_uri = NULL; g_return_val_if_fail (XDG_CACHE_IS_THUMBNAIL (thumbnail), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); @@ -317,6 +344,16 @@ xdg_cache_thumbnail_save_image_data (TumblerThumbnail *thumbnail, if (g_cancellable_set_error_if_cancelled (cancellable, error)) return FALSE; + /* Determine whether to save the thumbnail to a local or shared repository. + * + * By default, the policy is to always save to a local repository; but + * distributions might want to override this to, for example, store thumbnails + * in a shared repository on removable drives. + * + * See: http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#SHARED + */ + shared = FALSE; + /* determine dimensions of the thumbnail pixbuf */ width = data->width; height = data->height; @@ -338,8 +375,15 @@ xdg_cache_thumbnail_save_image_data (TumblerThumbnail *thumbnail, /* determine the URI of the temporary file to write to */ temp_file = xdg_cache_cache_get_temp_file (cache_thumbnail->uri, - cache_thumbnail->flavor); - + cache_thumbnail->flavor, + shared); + + temp_file_uri = g_file_get_uri (temp_file); + g_debug ("%s: Writing thumbnail for URI ‘%s’ to temp file ‘%s’, %s.", + G_STRFUNC, cache_thumbnail->uri, temp_file_uri, + shared ? "shared" : "not shared"); + g_free (temp_file_uri); + /* determine the flavor directory and its path */ flavor_dir = g_file_get_parent (temp_file); flavor_dir_path = g_file_get_path (flavor_dir); @@ -369,7 +413,8 @@ xdg_cache_thumbnail_save_image_data (TumblerThumbnail *thumbnail, { /* saving succeeded, termine the final destination of the thumbnail */ dest_file = xdg_cache_cache_get_file (cache_thumbnail->uri, - cache_thumbnail->flavor); + cache_thumbnail->flavor, + shared); /* determine temp and destination paths */ temp_path = g_file_get_path (temp_file); -- 2.4.3