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



commit 8c6c28dca3cbd84759f7933e8fc29aec0a5eb4ac
Author: Philip Withnall <philip tecnocode co uk>
Date:   Fri Dec 10 15:29:58 2010 +0000

    media: Switch GDataMediaContent 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.
    
    This is an API break which affects:
     â?¢ gdata_media_content_download()
    
    Helps: bgo#633363

 gdata/media/gdata-media-content.c               |   50 +++--------
 gdata/media/gdata-media-content.h               |    6 +-
 gdata/services/picasaweb/gdata-picasaweb-file.c |   12 ++-
 gdata/tests/picasaweb.c                         |  107 ++++++-----------------
 4 files changed, 50 insertions(+), 125 deletions(-)
---
diff --git a/gdata/media/gdata-media-content.c b/gdata/media/gdata-media-content.c
index 89c336f..7f720ee 100644
--- a/gdata/media/gdata-media-content.c
+++ b/gdata/media/gdata-media-content.c
@@ -538,58 +538,30 @@ gdata_media_content_get_width (GDataMediaContent *self)
  * gdata_media_content_download:
  * @self: a #GDataMediaContent
  * @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 a #GFile of the content represented by @self.
+ * Downloads and returns a #GDataDownloadStream allowing the content 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 content's data, or %NULL; unref with g_object_unref()
+ * Return value: (transfer full): a #GDataDownloadStream to download the content with, or %NULL; unref with g_object_unref()
  *
- * Since: 0.6.0
+ * Since: 0.8.0
  **/
-GFile *
-gdata_media_content_download (GDataMediaContent *self, GDataService *service, const gchar *default_filename, GFile *target_dest_file,
-                              gboolean replace_file_if_exists, GCancellable *cancellable, GError **error)
+GDataDownloadStream *
+gdata_media_content_download (GDataMediaContent *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_CONTENT (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_content_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-content.h b/gdata/media/gdata-media-content.h
index e8a1ad1..da634e3 100644
--- a/gdata/media/gdata-media-content.h
+++ b/gdata/media/gdata-media-content.h
@@ -23,6 +23,7 @@
 #include <glib.h>
 #include <glib-object.h>
 
+#include <gdata/gdata-download-stream.h>
 #include <gdata/gdata-parsable.h>
 #include <gdata/gdata-service.h>
 
@@ -107,9 +108,8 @@ gint64 gdata_media_content_get_duration (GDataMediaContent *self) G_GNUC_PURE;
 guint gdata_media_content_get_height (GDataMediaContent *self) G_GNUC_PURE;
 guint gdata_media_content_get_width (GDataMediaContent *self) G_GNUC_PURE;
 
-GFile *gdata_media_content_download (GDataMediaContent *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_content_download (GDataMediaContent *self, GDataService *service,
+                                                   GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
 
 G_END_DECLS
 
diff --git a/gdata/services/picasaweb/gdata-picasaweb-file.c b/gdata/services/picasaweb/gdata-picasaweb-file.c
index a447519..ddbf932 100644
--- a/gdata/services/picasaweb/gdata-picasaweb-file.c
+++ b/gdata/services/picasaweb/gdata-picasaweb-file.c
@@ -60,12 +60,20 @@
  *		/<!-- -->* Obtain the image data at various sizes *<!-- -->/
  *		for (contents = gdata_picasaweb_file_get_contents (photo); contents != NULL; contents = contents->next) {
  *			GDataMediaContent *content;
+ *			GDataDownloadStream *download_stream;
+ *			GFileOutputStream *file_stream;
  *			GFile *new_file;
  *
  *			content = GDATA_MEDIA_CONTENT (contents->data);
- *			/<!-- -->* Do something fun with the actual images, like download and display them.
+ *			/<!-- -->* Do something fun with the actual images, like download them to a file.
  *			 * Note that this is a blocking operation. *<!-- -->/
- *			new_file = gdata_media_content_download (content, GDATA_SERVICE (service), default_filename, target_file, FALSE, NULL, NULL);
+ *			download_stream = gdata_media_content_download (content, GDATA_SERVICE (service), NULL);
+ *			new_file = g_file_new_for_path (file_path);
+ *			file_stream = g_file_create (new_file, G_FILE_CREATE_NONE, NULL, NULL);
+ *			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, NULL);
+ *			g_object_unref (file_stream);
+ *			g_object_unref (download_stream);
  *			/<!-- -->* ... *<!-- -->/
  *			g_object_unref (new_file);
  *		}
diff --git a/gdata/tests/picasaweb.c b/gdata/tests/picasaweb.c
index 061f21b..5f6b3b6 100644
--- a/gdata/tests/picasaweb.c
+++ b/gdata/tests/picasaweb.c
@@ -305,9 +305,12 @@ test_download (gconstpointer _service)
 	GDataPicasaWebAlbum *album;
 	GDataPicasaWebFile *photo;
 	GDataPicasaWebQuery *query;
-	GDataMediaContent* content;
-	GFile *dest_dir, *dest_file, *actual_file;
-	gchar *basename;
+	GDataMediaContent *content;
+	GDataDownloadStream *download_stream;
+	gchar *destination_file_name, *destination_file_path;
+	GFile *destination_file;
+	GFileOutputStream *file_stream;
+	gssize transfer_size;
 	GError *error = NULL;
 
 	/*** Acquire a photo to test ***/
@@ -331,99 +334,41 @@ test_download (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);
-	}
-
 	media_contents = gdata_picasaweb_file_get_contents (photo);
 	g_assert_cmpint (g_list_length (media_contents), ==, 1);
 	content = GDATA_MEDIA_CONTENT (media_contents->data);
 
-	/* to a directory, non-existent, should succeed, file with "directory"'s name */
-	actual_file = gdata_media_content_download (content, service, "default.jpg", dest_dir, FALSE, NULL, &error);
+	/* Prepare a download stream */
+	download_stream = gdata_media_content_download (content, 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);
+	g_assert (GDATA_IS_DOWNLOAD_STREAM (download_stream));
 
-	/* to a file in a "directory", which already exists as a file, should fail */
-	actual_file = gdata_media_content_download (content, service, "default.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);
+	/* Prepare a file to write the data to */
+	destination_file_name = g_strdup_printf ("%s.jpg", gdata_picasaweb_file_get_id (photo));
+	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);
 
-	/* 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);
+	/* 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 (G_IS_FILE_OUTPUT_STREAM (file_stream));
 
-	/* to a directory, existent, should succeed, using default filename */
-	actual_file = gdata_media_content_download (content, service, "default.jpg", dest_dir, FALSE, NULL, &error);
-	g_assert_no_error (error);
-	g_assert (actual_file != NULL);
-	basename = g_file_get_basename (actual_file);
-	g_assert_cmpstr (basename, ==, "default.jpg");
-	g_free (basename);
-	g_object_unref (actual_file);
-	/* TODO: test that it exists with default filename? */
-
-	/* to a directory, existent, should fail trying to use the default filename, which already exists */
-	actual_file = gdata_media_content_download (content, service, "default.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);
-
-	/* to a directory, existent, should succeed with default filename, replacing what already exists */
-	actual_file = gdata_media_content_download (content, service, "default.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, ==, "default.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_content_download (content, service, "default.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_content_download (content, service, "default.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_content_download (content, service, "default.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);
+	g_object_unref (file_stream);
+	g_object_unref (download_stream);
 
-	/* clean up test directory */
-	delete_directory (dest_dir, &error);
-	g_assert_no_error (error);
+	/* 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);
 
 	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]