RFC: preview files
- From: David Zeuthen <david fubar dk>
- To: gvfs-list gnome org
- Subject: RFC: preview files
- Date: Wed, 15 Oct 2008 22:04:15 -0400
Hey,
So one interesting feature of digital cameras is that they're capable of
generating thumbnails on the device side. This is actually pretty useful
when importing photos; instead of downloading hundreds images (each
5-10MB on my Canon EOS 5D DSLR) for the import dialog, one can download
the previews (typically ~10KB of size 160x120).
I've written a patch for the gphoto2 backend to do this.
First, there's a GIO patch to introduce two new properties preview::uri
and preview::uri-original that links the original file with the preview
file. See [1] for gvfs-info output.
Second there's the gphoto2 patch. It's not too complicated, we just have
to synthesize GFileInfo objects in enumerate() and then handle them in
query_file_info() and do_open_for_read().
(There's one catch, there doesn't seem to be any way to get the size of
the preview file (or maybe I'm just lost in the libgphoto2 API again);
not a huge problem. Most apps can open the files just fine.)
Turns out the performance is par on using the gphoto2(1) command-line
tool; in fact, we're slightly faster, see [2] for details, though that
may just be statistical noise.
If these patches get in, we should probably teach Nautilus's thumbnailer
about preview::uri and use that instead of the regular file. That will
speed up thumbnailing a lot.
Not sure what other backends can benefit from this but preview::* seems
like a pretty useful feature in general.
OK to commit?
David
[1] :
$ gvfs-info gphoto2://[usb:001,012]/DCIM/100EOS5D/IMG_2125.JPG
display name: IMG_2125.JPG
name: IMG_2125.JPG
type: regular
size: 5221629
attributes:
standard::name: IMG_2125.JPG
standard::display-name: IMG_2125.JPG
standard::type: 1
standard::size: 5221629
standard::content-type: image/jpeg
standard::icon: GThemedIcon:0x1eaf520
standard::is-hidden: FALSE
time::modified: 1215970398
time::modified-usec: 0
access::can-read: TRUE
access::can-write: TRUE
access::can-delete: TRUE
access::can-execute: FALSE
access::can-trash: FALSE
access::can-rename: TRUE
preview::uri: gphoto2://[usb:001,012]/DCIM/100EOS5D/.gvfs-gphoto2-preview-IMG_2125.JPG
id::filesystem: host='[usb:001,012]',type='gphoto2',mount_prefix='/'
$ gvfs-info gphoto2://[usb:001,012]/DCIM/100EOS5D/.gvfs-gphoto2-preview-IMG_2125.JPG
display name: .gvfs-gphoto2-preview-IMG_2125.JPG
name: .gvfs-gphoto2-preview-IMG_2125.JPG
type: regular
size: 0
hidden
attributes:
standard::name: .gvfs-gphoto2-preview-IMG_2125.JPG
standard::display-name: .gvfs-gphoto2-preview-IMG_2125.JPG
standard::type: 1
standard::content-type: image/jpeg
standard::icon: GThemedIcon:0x862520
standard::is-hidden: TRUE
time::modified: 1215970398
time::modified-usec: 0
access::can-read: TRUE
access::can-write: FALSE
access::can-delete: FALSE
access::can-execute: FALSE
access::can-trash: FALSE
access::can-rename: TRUE
preview::uri-original: gphoto2://[usb:001,012]/DCIM/100EOS5D/IMG_2125.JPG
id::filesystem: host='[usb:001,012]',type='gphoto2',mount_prefix='/'
[2] :
$ time for i in `gvfs-ls -h gphoto2://[usb:001,012]/DCIM/100EOS5D|grep ".gvfs-gphoto2"`; do gvfs-copy gphoto2://[usb:001,012]/DCIM/100EOS5D/$i ~/Desktop/foo; done
real 0m43.094s
user 0m2.807s
sys 0m3.178s
$ time gphoto2 -T
Detected a 'Canon:EOS 5D (normal mode)'.
Downloading 'IMG_2125.JPG' from folder '/DCIM/100EOS5D'...
Downloading 'IMG_2125.JPG' from folder '/DCIM/100EOS5D'...
Saving file as thumb_IMG_2125.jpg
[...]
Downloading '_MG_2124.JPG' from folder '/DCIM/100EOS5D'...
Downloading '_MG_2124.JPG' from folder '/DCIM/100EOS5D'...
Saving file as thumb__MG_2124.jpg
real 0m43.829s
user 0m2.439s
sys 0m0.270s
Index: gfileinfo.h
===================================================================
--- gfileinfo.h (revision 7593)
+++ gfileinfo.h (working copy)
@@ -608,6 +608,32 @@ typedef struct _GFileInfoClass GFileInfoClass;
**/
#define G_FILE_ATTRIBUTE_THUMBNAILING_FAILED "thumbnail::failed" /* boolean */
+/* Preview */
+
+/**
+ * G_FILE_ATTRIBUTE_PREVIEW_URI:
+ *
+ * A key in the "preview" namespace for getting the URI to a preview
+ * file. A preview file is typically a smaller size, lower quality
+ * version of a given file; typically a lower resultion thumbnail for
+ * images. Corresponding #GFileAttributeType is
+ * %G_FILE_ATTRIBUTE_TYPE_BYTE_STRING.
+ *
+ * Since: 2.20
+ **/
+#define G_FILE_ATTRIBUTE_PREVIEW_URI "preview::uri" /* bytestring */
+
+/**
+ * G_FILE_ATTRIBUTE_PREVIEW_URI_ORIGINAL:
+ *
+ * A key in the "preview" namespace for getting the URI to the
+ * original file that the given file is a preview for. Corresponding
+ * #GFileAttributeType is %G_FILE_ATTRIBUTE_TYPE_BYTE_STRING.
+ *
+ * Since: 2.20
+ **/
+#define G_FILE_ATTRIBUTE_PREVIEW_URI_ORIGINAL "preview::uri-original" /* bytestring */
+
/* File system info (for g_file_get_filesystem_info) */
/**
Index: gvfsbackendgphoto2.c
===================================================================
--- gvfsbackendgphoto2.c (revision 2049)
+++ gvfsbackendgphoto2.c (working copy)
@@ -68,6 +68,17 @@
#define DEBUG_NO_CACHING 1
#endif
+/* the prefix used for synthesized preview files */
+#define PREVIEW_PREFIX ".gvfs-gphoto2-preview-"
+
+static gboolean file_info_has_preview (GVfsBackendGphoto2 *gphoto2_backend,
+ GFileInfo *file_info,
+ const char *directory_name);
+
+static void modify_info_for_preview (GVfsBackendGphoto2 *gphoto2_backend,
+ GFileInfo *file_info,
+ const char *directory_name);
+
/*--------------------------------------------------------------------------------------------------------------*/
/* TODO:
@@ -976,8 +987,10 @@ file_get_info (GVfsBackendGphoto2 *gphoto2_backend
char *mime_type;
GIcon *icon;
unsigned int n;
+ gboolean is_preview;
ret = FALSE;
+ is_preview = FALSE;
full_path = g_build_filename (dir, name, NULL);
DEBUG ("file_get_info() try_cache_only=%d dir='%s', name='%s'\n"
@@ -1033,6 +1046,12 @@ file_get_info (GVfsBackendGphoto2 *gphoto2_backend
goto add_to_cache;
}
+ if (g_str_has_prefix (name, PREVIEW_PREFIX))
+ {
+ name = name + sizeof (PREVIEW_PREFIX) - 1;
+ is_preview = TRUE;
+ }
+
rc = gp_camera_file_get_info (gphoto2_backend->camera,
dir,
name,
@@ -1141,7 +1160,6 @@ file_get_info (GVfsBackendGphoto2 *gphoto2_backend
}
g_free (mime_type);
-
if (gp_info.file.fields & GP_FILE_INFO_MTIME)
mtime.tv_sec = gp_info.file.mtime;
else
@@ -1167,7 +1185,17 @@ file_get_info (GVfsBackendGphoto2 *gphoto2_backend
}
ret = TRUE;
- DEBUG (" Generating info (file) for '%s'", full_path);
+ if (is_preview)
+ {
+ modify_info_for_preview (gphoto2_backend, info, dir);
+ DEBUG (" Generated info (preview) for '%s'", full_path);
+ }
+ else
+ {
+ /* sets G_FILE_ATTRIBUTE_PREVIEW_URI if appropriate */
+ file_info_has_preview (gphoto2_backend, info, dir);
+ DEBUG (" Generated info (file) for '%s'", full_path);
+ }
add_to_cache:
/* add this sucker to the cache */
@@ -1687,6 +1715,7 @@ do_open_for_read (GVfsBackend *backend,
GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
char *dir;
char *name;
+ gboolean is_preview;
DEBUG ("open_for_read (%s)", filename);
@@ -1721,10 +1750,20 @@ do_open_for_read (GVfsBackend *backend,
goto out;
}
+ is_preview = FALSE;
+ if (g_str_has_prefix (name, PREVIEW_PREFIX))
+ {
+ char *s;
+ s = name;
+ name = g_strdup (name + sizeof (PREVIEW_PREFIX) - 1);
+ g_free (s);
+ is_preview = TRUE;
+ }
+
rc = gp_camera_file_get (gphoto2_backend->camera,
dir,
name,
- GP_FILE_TYPE_NORMAL,
+ is_preview ? GP_FILE_TYPE_PREVIEW : GP_FILE_TYPE_NORMAL,
read_handle->file,
gphoto2_backend->context);
if (rc != 0)
@@ -1940,7 +1979,97 @@ try_query_info (GVfsBackend *backend,
/* ------------------------------------------------------------------------------------------------- */
+static gboolean
+file_info_has_preview (GVfsBackendGphoto2 *gphoto2_backend,
+ GFileInfo *file_info,
+ const char *directory_name)
+{
+ gboolean ret;
+ char *preview_uri;
+ const char *name;
+ const char *content_type;
+
+ ret = FALSE;
+ preview_uri = NULL;
+
+ name = g_file_info_get_name (file_info);
+ if (name == NULL)
+ goto out;
+
+ content_type = g_file_info_get_content_type (file_info);
+ if (content_type == NULL)
+ goto out;
+
+ /* for now we assume only that all JPG files has a preview file */
+ if (strcmp (content_type, "image/jpg") != 0 &&
+ strcmp (content_type, "image/jpeg") != 0)
+ goto out;
+
+ ret = TRUE;
+
+ preview_uri = g_strdup_printf ("gphoto2://[%s]/%s/%s%s",
+ gphoto2_backend->gphoto2_port,
+ directory_name + strlen (gphoto2_backend->ignore_prefix),
+ PREVIEW_PREFIX,
+ name);
+ g_file_info_set_attribute_string (file_info, G_FILE_ATTRIBUTE_PREVIEW_URI, preview_uri);
+
+ out:
+ g_free (preview_uri);
+ return ret;
+}
+
static void
+modify_info_for_preview (GVfsBackendGphoto2 *gphoto2_backend,
+ GFileInfo *file_info,
+ const char *directory_name)
+{
+ char *name;
+ char *preview_name;
+ char *preview_original_uri;
+
+ name = g_strdup (g_file_info_get_name (file_info));
+ preview_name = g_strdup_printf (PREVIEW_PREFIX "%s", name);
+
+ g_file_info_set_display_name (file_info, preview_name);
+ g_file_info_set_name (file_info, preview_name);
+ g_file_info_set_is_hidden (file_info, TRUE);
+ g_file_info_set_attribute_boolean (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
+ g_file_info_set_attribute_boolean (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
+
+ /* TODO: how do we get the size? */
+ g_file_info_remove_attribute (file_info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
+
+ preview_original_uri = g_strdup_printf ("gphoto2://[%s]/%s/%s",
+ gphoto2_backend->gphoto2_port,
+ directory_name + strlen (gphoto2_backend->ignore_prefix),
+ name);
+ g_file_info_set_attribute_string (file_info, G_FILE_ATTRIBUTE_PREVIEW_URI_ORIGINAL, preview_original_uri);
+
+ g_free (preview_original_uri);
+ g_free (preview_name);
+ g_free (name);
+}
+
+static void
+maybe_add_preview_file (GVfsBackendGphoto2 *gphoto2_backend,
+ GList **file_info_list,
+ GFileInfo *file_info,
+ const char *directory_name)
+{
+ if (file_info_has_preview (gphoto2_backend,
+ file_info,
+ directory_name))
+ {
+ GFileInfo *preview_info;
+
+ preview_info = g_file_info_dup (file_info);
+ modify_info_for_preview (gphoto2_backend, preview_info, directory_name);
+ *file_info_list = g_list_append (*file_info_list, preview_info);
+ }
+}
+
+static void
do_enumerate (GVfsBackend *backend,
GVfsJobEnumerate *job,
const char *given_filename,
@@ -2106,6 +2235,8 @@ do_enumerate (GVfsBackend *backend,
return;
}
l = g_list_append (l, info);
+
+ maybe_add_preview_file (gphoto2_backend, &l, info, as_dir);
}
if (!using_cached_file_list)
{
@@ -2208,6 +2339,8 @@ try_enumerate (GVfsBackend *backend,
goto error_not_cached;
}
l = g_list_append (l, info);
+
+ maybe_add_preview_file (gphoto2_backend, &l, info, filename);
}
g_mutex_lock (gphoto2_backend->lock);
gp_list_unref (list);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]