[libgdata] media: Switch GDataMediaThumbnail to a stream-based download API



commit ea6df1cfd4f7f4d2ccad733e20f9ee05ad618b88
Author: Philip Withnall <philip tecnocode co uk>
Date:   Fri Dec 10 15:45:33 2010 +0000

    media: Switch GDataMediaThumbnail to a stream-based download API
    
    Downloading to a file provided by its path was unnecessarily limiting,
    forcing downloads to be made directly to disk instead of (e.g.) another
    network stream, and subjecting clients to libgdata's idea of â??Replace this
    File?â?? logic. It's not much extra code for the clients to handle this
    themselves, and offers much greater flexibility. We also get async support
    for free, as creating the GDataDownloadStream is a cheap operation, and it
    already has async support.
    
    This updates the test suite appropriately. We now load thumbnails in the
    test suite directly into GdkPixbufs, which requires that we bump our
    gdk-pixbuf requirement to 2.14.
    
    This is an API break which affects:
     â?¢ gdata_media_thumbnail_download()
    
    Helps: bgo#633363

 configure.ac                                     |    2 +-
 gdata/media/gdata-media-thumbnail.c              |   49 ++-----
 gdata/media/gdata-media-thumbnail.h              |    8 +-
 gdata/services/picasaweb/gdata-picasaweb-album.c |   13 +-
 gdata/tests/picasaweb.c                          |  177 ++++++----------------
 5 files changed, 67 insertions(+), 182 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 6eff719..832bca6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -64,7 +64,7 @@ AC_SUBST(GDATA_CFLAGS)
 AC_SUBST(GDATA_LIBS)
 
 # Optional dependencies
-PKG_CHECK_MODULES(GDK_PIXBUF, gdk-pixbuf-2.0, have_gdk_pixbuf=yes, have_gdk_pixbuf=no)
+PKG_CHECK_MODULES(GDK_PIXBUF, gdk-pixbuf-2.0 >= 2.14, have_gdk_pixbuf=yes, have_gdk_pixbuf=no)
 if test "x$have_gdk_pixbuf" = "xyes"; then
 	AC_DEFINE(HAVE_GDK_PIXBUF, 1, [Defined if gdk-pixbuf is installed])
 fi
diff --git a/gdata/media/gdata-media-thumbnail.c b/gdata/media/gdata-media-thumbnail.c
index 1ce0b6f..6f59989 100644
--- a/gdata/media/gdata-media-thumbnail.c
+++ b/gdata/media/gdata-media-thumbnail.c
@@ -365,57 +365,30 @@ gdata_media_thumbnail_get_time (GDataMediaThumbnail *self)
  * gdata_media_thumbnail_download:
  * @self: a #GDataMediaThumbnail
  * @service: the #GDataService
- * @default_filename: a default filename used if the user selects a directory as the destination
- * @target_dest_file: the destination file or directory to download to
- * @replace_file_if_exists: whether to replace already existing files at the download location
- * @cancellable: optional #GCancellable object, or %NULL
  * @error: a #GError, or %NULL
  *
- * Downloads and returns the thumbnail represented by @self.
+ * Downloads and returns a #GDataDownloadStream allowing the thumbnail data represented by @self to be read.
  *
- * If @target_dest_file is a directory, then the file will be
- * downloaded into this directory with the default filename specified
- * in @default_filename.
+ * To get the content type of the downloaded data, gdata_download_stream_get_content_type() can be called on the returned #GDataDownloadStream.
+ * Calling gdata_download_stream_get_content_length() on the stream will not return a meaningful result, however, as the stream is encoded in chunks,
+ * rather than by content length.
  *
- * Return value: (transfer full): the thumbnail's data, or %NULL; unref with g_object_unref()
+ * Return value: (transfer full): a #GDataDownloadStream to download the thumbnail with, or %NULL; unref with g_object_unref()
  *
- * Since: 0.6.0
+ * Since: 0.8.0
  **/
-GFile *
-gdata_media_thumbnail_download (GDataMediaThumbnail *self, GDataService *service, const gchar *default_filename, GFile *target_dest_file,
-                                gboolean replace_file_if_exists, GCancellable *cancellable, GError **error)
+GDataDownloadStream *
+gdata_media_thumbnail_download (GDataMediaThumbnail *self, GDataService *service, GError **error)
 {
-	GFileOutputStream *dest_stream;
 	const gchar *src_uri;
-	GInputStream *src_stream;
-	GFile *actual_file = NULL;
-	GError *child_error = NULL;
 
 	g_return_val_if_fail (GDATA_IS_MEDIA_THUMBNAIL (self), NULL);
 	g_return_val_if_fail (GDATA_IS_SERVICE (service), NULL);
-	g_return_val_if_fail (default_filename != NULL, NULL);
-	g_return_val_if_fail (G_IS_FILE (target_dest_file), NULL);
-	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-	dest_stream = _gdata_download_stream_find_destination (default_filename, target_dest_file, &actual_file, replace_file_if_exists,
-	                                                       cancellable, error);
-	if (dest_stream == NULL)
-		return NULL;
+	/* We keep a GError in the argument list so that we can add authentication errors, etc., in future if necessary */
 
+	/* Get the download URI and create a stream for it */
 	src_uri = gdata_media_thumbnail_get_uri (self);
-
-	/* Synchronously splice the data from the download stream to the file stream (network -> disk) */
-	src_stream = gdata_download_stream_new (GDATA_SERVICE (service), src_uri);
-	g_output_stream_splice (G_OUTPUT_STREAM (dest_stream), src_stream,
-	                        G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, cancellable, &child_error);
-	g_object_unref (src_stream);
-	g_object_unref (dest_stream);
-	if (child_error != NULL) {
-		g_object_unref (actual_file);
-		g_propagate_error (error, child_error);
-		return NULL;
-	}
-
-	return actual_file;
+	return GDATA_DOWNLOAD_STREAM (gdata_download_stream_new (service, src_uri));
 }
diff --git a/gdata/media/gdata-media-thumbnail.h b/gdata/media/gdata-media-thumbnail.h
index c19d900..c79548c 100644
--- a/gdata/media/gdata-media-thumbnail.h
+++ b/gdata/media/gdata-media-thumbnail.h
@@ -22,8 +22,9 @@
 
 #include <glib.h>
 #include <glib-object.h>
-#include <gdata/gdata-service.h>
 
+#include <gdata/gdata-download-stream.h>
+#include <gdata/gdata-service.h>
 #include <gdata/gdata-parsable.h>
 
 G_BEGIN_DECLS
@@ -66,9 +67,8 @@ guint gdata_media_thumbnail_get_height (GDataMediaThumbnail *self) G_GNUC_PURE;
 guint gdata_media_thumbnail_get_width (GDataMediaThumbnail *self) G_GNUC_PURE;
 gint64 gdata_media_thumbnail_get_time (GDataMediaThumbnail *self) G_GNUC_PURE;
 
-GFile *gdata_media_thumbnail_download (GDataMediaThumbnail *self, GDataService *service, const gchar *default_filename, GFile *target_dest_file,
-                                       gboolean replace_file_if_exists,
-                                       GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+GDataDownloadStream *gdata_media_thumbnail_download (GDataMediaThumbnail *self, GDataService *service,
+                                                     GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
 
 G_END_DECLS
 
diff --git a/gdata/services/picasaweb/gdata-picasaweb-album.c b/gdata/services/picasaweb/gdata-picasaweb-album.c
index 132eba9..240e6c1 100644
--- a/gdata/services/picasaweb/gdata-picasaweb-album.c
+++ b/gdata/services/picasaweb/gdata-picasaweb-album.c
@@ -58,15 +58,18 @@
  *
  *		for (thumbnails = gdata_picasaweb_album_get_thumbnails (album); thumbnails != NULL; thumbnails = thumbnails->next) {
  *			GDataMediaThumbnail *thumbnail;
- *			GFile *new_file;
+ *			GDataDownloadStream *download_stream;
+ *			GdkPixbuf *pixbuf;
  *
  *			thumbnail = GDATA_MEDIA_THUMBNAIL (thumbnails->data);
- *			/<!-- -->* Do something fun with the thumbnails, like download and display them.
+ *			/<!-- -->* Do something fun with the thumbnails, like download and display them. We could just as easily download them into
+ *			 * files using g_file_create() and g_output_stream_splice(), rather than create GdkPixbuf<!-- -->s directly from them.
  *			 * Note that this is a blocking operation. *<!-- -->/
- *			new_file = gdata_media_thumbnail_download (thumbnail, GDATA_SERVICE (service), default_filename, target_file, FALSE,
- *			                                           NULL, NULL);
+ *			download_stream = gdata_media_thumbnail_download (thumbnail, GDATA_SERVICE (service), NULL);
+ *			pixbuf = gdk_pixbuf_new_from_stream (G_INPUT_STREAM (download_stream), NULL, NULL);
+ *			g_object_unref (download_stream);
  *			/<!-- -->* ... *<!-- -->/
- *			g_object_unref (new_file);
+ *			g_object_unref (pixbuf);
  *		}
  *
  *		/<!-- -->* Do something worthwhile with your album data *<!-- -->/
diff --git a/gdata/tests/picasaweb.c b/gdata/tests/picasaweb.c
index 5f6b3b6..9529035 100644
--- a/gdata/tests/picasaweb.c
+++ b/gdata/tests/picasaweb.c
@@ -37,38 +37,6 @@
 #define TEST_ALBUM_INDEX 2
 
 static void
-delete_directory (GFile *directory, GError **error)
-{
-	GFileEnumerator *enumerator;
-
-	enumerator = g_file_enumerate_children (directory, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error);
-	if (enumerator == NULL)
-		return;
-
-	/* Delete all the files in the directory */
-	while (TRUE) {
-		GFileInfo *file_info;
-		GFile *file;
-
-		file_info = g_file_enumerator_next_file (enumerator, NULL, NULL);
-		if (file_info == NULL)
-			break;
-
-		file = g_file_get_child (directory, g_file_info_get_name (file_info));
-		g_object_unref (file_info);
-
-		g_file_delete (file, NULL, NULL);
-		g_object_unref (file);
-	}
-
-	g_file_enumerator_close (enumerator, NULL, error);
-	g_object_unref (enumerator);
-
-	/* Delete the directory itself */
-	g_file_delete (directory, NULL, error);
-}
-
-static void
 test_authentication (void)
 {
 	gboolean retval;
@@ -144,9 +112,12 @@ test_download_thumbnails (gconstpointer _service)
 	GDataPicasaWebAlbum *album;
 	GDataPicasaWebFile *photo;
 	GDataPicasaWebQuery *query;
-	GFile *dest_dir, *dest_file, *actual_file;
 	GDataMediaThumbnail *thumbnail;
-	gchar *file_path, *basename;
+	GDataDownloadStream *download_stream;
+	gchar *destination_file_name, *destination_file_path;
+	GFile *destination_file;
+	GFileOutputStream *file_stream;
+	gssize transfer_size;
 	GError *error = NULL;
 
 	/* Acquire album, photo to test */
@@ -170,130 +141,68 @@ test_download_thumbnails (gconstpointer _service)
 	g_assert (photo_entries != NULL);
 
 	photo = GDATA_PICASAWEB_FILE (photo_entries->data);
-
-	dest_dir = g_file_new_for_path ("/tmp/gdata.picasaweb.test.dir/");
-	dest_file = g_file_new_for_path ("/tmp/gdata.picasaweb.test.dir/test.jpg");
-
-	/* clean up any pre-existing test output  */
-	if (g_file_query_exists (dest_dir, NULL)) {
-		delete_directory (dest_dir, &error);
-		g_assert_no_error (error);
-	}
-
 	thumbnails = gdata_picasaweb_file_get_thumbnails (photo);
 	thumbnail = GDATA_MEDIA_THUMBNAIL (thumbnails->data);
 
-	/* to a directory, non-existent, should succeed, file with "directory"'s name */
-	actual_file = gdata_media_thumbnail_download (thumbnail, service, "thumbnail.jpg", dest_dir, FALSE, NULL, &error);
+	/* Download a single thumbnail to a file for testing (in case we weren't compiled with GdkPixbuf support) */
+	download_stream = gdata_media_thumbnail_download (thumbnail, service, &error);
 	g_assert_no_error (error);
-	g_assert (g_file_query_exists (actual_file, NULL));
-	basename = g_file_get_basename (actual_file);
-	g_assert_cmpstr (basename, ==, "gdata.picasaweb.test.dir");
-	g_free (basename);
-	g_object_unref (actual_file);
-
-	/* to a "directory", which doesn't actually exist (as a directory), should fail */
-	actual_file = gdata_media_thumbnail_download (thumbnail, service, "thumbnail.jpg", dest_file, FALSE, NULL, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY);
-	g_clear_error (&error);
-	g_assert (actual_file == NULL);
+	g_assert (GDATA_IS_DOWNLOAD_STREAM (download_stream));
 
-	/* create the directory so we can test on it and in it */
-	g_file_delete (dest_dir, NULL, &error);
-	g_assert_no_error (error);
-	g_file_make_directory (dest_dir, NULL, &error);
-	g_assert_no_error (error);
+	/* Prepare a file to write the data to */
+	destination_file_name = g_strdup_printf ("%s_thumbnail_%ux%u.jpg", gdata_picasaweb_file_get_id (photo),
+	                                         gdata_media_thumbnail_get_width (thumbnail), gdata_media_thumbnail_get_height (thumbnail));
+	destination_file_path = g_build_filename (g_get_tmp_dir (), destination_file_name, NULL);
+	g_free (destination_file_name);
+	destination_file = g_file_new_for_path (destination_file_path);
+	g_free (destination_file_path);
 
-	/* to a directory, existent, should succeed, making use of the default filename provided */
-	actual_file = gdata_media_thumbnail_download (thumbnail, service, "thumbnail.jpg", dest_dir, FALSE, NULL, &error);
+	/* Download the file */
+	file_stream = g_file_replace (destination_file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, &error);
 	g_assert_no_error (error);
-	g_assert (actual_file != NULL);
-	basename = g_file_get_basename (actual_file);
-	g_assert_cmpstr (basename, ==, "thumbnail.jpg");
-	g_free (basename);
-	g_object_unref (actual_file);
-
-	/* to a directory, existent, with inferred file destination already existent, without replace, should fail */
-	actual_file = gdata_media_thumbnail_download (thumbnail, service, "thumbnail.jpg", dest_dir, FALSE, NULL, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS);
-	g_clear_error (&error);
-	g_assert (actual_file == NULL);
+	g_assert (G_IS_FILE_OUTPUT_STREAM (file_stream));
 
-	/* to a directory, existent, with inferred file destination already existent, with replace, should succeed */
-	actual_file = gdata_media_thumbnail_download (thumbnail, service, "thumbnail.jpg", dest_dir, TRUE, NULL, &error);
-	g_assert_no_error (error);
-	g_assert (g_file_query_exists (actual_file, NULL));
-	basename = g_file_get_basename (actual_file);
-	g_assert_cmpstr (basename, ==, "thumbnail.jpg");
-	g_free (basename);
-	g_object_unref (actual_file);
-
-	/* to a path, non-existent, should succeed */
-	g_assert (g_file_query_exists (dest_file, NULL) == FALSE);
-	actual_file = gdata_media_thumbnail_download (thumbnail, service, "thumbnail.jpg", dest_file, FALSE, NULL, &error);
+	transfer_size = g_output_stream_splice (G_OUTPUT_STREAM (file_stream), G_INPUT_STREAM (download_stream),
+			                        G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, &error);
 	g_assert_no_error (error);
-	g_assert (g_file_query_exists (actual_file, NULL));
-	basename = g_file_get_basename (actual_file);
-	g_assert_cmpstr (basename, ==, "test.jpg");
-	g_free (basename);
-	g_object_unref (actual_file);
-
-	/* to a path, existent, without replace, should fail */
-	actual_file = gdata_media_thumbnail_download (thumbnail, service, "thumbnail.jpg", dest_file, FALSE, NULL, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS);
-	g_clear_error (&error);
-	g_assert (actual_file == NULL);
+	g_assert_cmpint (transfer_size, >, 0);
 
-	/* to a path, existent, with replace, should succeed */
-	actual_file = gdata_media_thumbnail_download (thumbnail, service, "thumbnail.jpg", dest_file, TRUE, NULL, &error);
-	g_assert_no_error (error);
-	g_assert (g_file_query_exists (actual_file, NULL));
-	basename = g_file_get_basename (actual_file);
-	g_assert_cmpstr (basename, ==, "test.jpg");
-	g_free (basename);
-	g_object_unref (actual_file);
-
-	/* clean up test file and thumbnail*/
-	g_file_delete (dest_file, NULL, &error);
-	g_assert_no_error (error);
+	g_object_unref (file_stream);
+	g_object_unref (download_stream);
+
+	/* Delete the file (shouldn't cause the test to fail if this fails) */
+	g_file_delete (destination_file, NULL, NULL);
+	g_object_unref (destination_file);
 
-	/* test getting all thumbnails and that they're all the correct size */
-	for (node = thumbnails; node != NULL; node = node->next) {
 #ifdef HAVE_GDK_PIXBUF
+	/* Test downloading all thumbnails directly into GdkPixbufs, and check that they're all the correct size */
+	for (node = thumbnails; node != NULL; node = node->next) {
 		GdkPixbuf *pixbuf;
-#endif /* HAVE_GDK_PIXBUF */
 
 		thumbnail = GDATA_MEDIA_THUMBNAIL (node->data);
-		actual_file = gdata_media_thumbnail_download (thumbnail, service, "thumbnail.jpg", dest_file, FALSE, NULL, &error);
+
+		/* Prepare a download stream */
+		download_stream = gdata_media_thumbnail_download (thumbnail, service, &error);
 		g_assert_no_error (error);
-		g_assert (g_file_query_exists (actual_file, NULL));
+		g_assert (GDATA_IS_DOWNLOAD_STREAM (download_stream));
 
-#ifdef HAVE_GDK_PIXBUF
-		file_path = g_file_get_path (actual_file);
-		pixbuf = gdk_pixbuf_new_from_file (file_path, &error);
+		/* Download into a new GdkPixbuf */
+		pixbuf = gdk_pixbuf_new_from_stream (G_INPUT_STREAM (download_stream), NULL, &error);
 		g_assert_no_error (error);
-		g_free (file_path);
+		g_assert (GDK_IS_PIXBUF (pixbuf));
+
+		g_object_unref (download_stream);
 
 		/* PicasaWeb reported the height of a thumbnail as a pixel too large once, but otherwise correct */
-		g_assert_cmpint (abs (gdk_pixbuf_get_width (pixbuf) - (gint)gdata_media_thumbnail_get_width (thumbnail)) , <=, 1);
-		g_assert_cmpint (abs (gdk_pixbuf_get_height (pixbuf) - (gint)gdata_media_thumbnail_get_height (thumbnail)) , <=, 1);
-		g_object_unref (pixbuf);
-#endif /* HAVE_GDK_PIXBUF */
+		g_assert_cmpint (abs (gdk_pixbuf_get_width (pixbuf) - (gint) gdata_media_thumbnail_get_width (thumbnail)) , <=, 1);
+		g_assert_cmpint (abs (gdk_pixbuf_get_height (pixbuf) - (gint) gdata_media_thumbnail_get_height (thumbnail)) , <=, 1);
 
-		g_file_delete (actual_file, NULL, &error);
-		g_assert (g_file_query_exists (actual_file, NULL) == FALSE);
-		g_assert_no_error (error);
-		g_object_unref (actual_file);
+		g_object_unref (pixbuf);
 	}
-
-	/* clean up test directory again */
-	delete_directory (dest_dir, &error);
-	g_assert_no_error (error);
+#endif /* HAVE_GDK_PIXBUF */
 
 	g_object_unref (photo_feed);
 	g_object_unref (album_feed);
-	g_object_unref (dest_dir);
-	g_object_unref (dest_file);
 }
 
 static void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]