[libgdata] youtube: Switch to a stream-based upload API



commit 5a6637eb4ba66265a7f698d8bbae4a3f5b6a30a5
Author: Philip Withnall <philip tecnocode co uk>
Date:   Fri Dec 10 17:13:33 2010 +0000

    youtube: Switch to a stream-based upload API
    
    For similar reasons as the Documents service's switch to a stream-based
    upload and download API, it makes sense to switch the YouTube service to a
    stream-based upload API. This gives us more flexibility as to the source of
    uploaded data.
    
    The test suite has been updated appropriately.
    
    The following API has been changed:
     â?¢ gdata_youtube_service_upload_video()
    
    The following API has been removed:
     â?¢ gdata_youtube_service_upload_video_async()
     â?¢ gdata_youtube_service_upload_video_finish()
    
    The following API has been added:
     â?¢ gdata_youtube_service_finish_video_upload()

 docs/reference/gdata-sections.txt              |    3 +-
 gdata/gdata.symbols                            |    5 +-
 gdata/services/youtube/gdata-youtube-service.c |  248 ++++--------------------
 gdata/services/youtube/gdata-youtube-service.h |    9 +-
 gdata/tests/youtube.c                          |  100 +++++++++-
 5 files changed, 136 insertions(+), 229 deletions(-)
---
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 071218a..3b4ed92 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -188,8 +188,7 @@ gdata_youtube_service_query_related_async
 gdata_youtube_service_query_standard_feed
 gdata_youtube_service_query_standard_feed_async
 gdata_youtube_service_upload_video
-gdata_youtube_service_upload_video_async
-gdata_youtube_service_upload_video_finish
+gdata_youtube_service_finish_video_upload
 gdata_youtube_service_get_categories
 gdata_youtube_service_get_categories_async
 gdata_youtube_service_get_categories_finish
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index 7d65b16..5c12768 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -109,7 +109,6 @@ gdata_youtube_service_query_videos
 gdata_youtube_service_query_videos_async
 gdata_youtube_service_query_related
 gdata_youtube_service_query_related_async
-gdata_youtube_service_upload_video
 gdata_youtube_service_get_developer_key
 gdata_youtube_video_get_type
 gdata_youtube_video_new
@@ -842,8 +841,6 @@ gdata_contacts_service_query_groups
 gdata_contacts_service_query_groups_async
 gdata_contacts_service_insert_group
 gdata_contacts_service_insert_group_async
-gdata_youtube_service_upload_video_async
-gdata_youtube_service_upload_video_finish
 gdata_picasaweb_service_insert_album_async
 gdata_picasaweb_service_query_files_async
 gdata_documents_service_finish_upload
@@ -855,3 +852,5 @@ gdata_documents_service_remove_entry_from_folder_async
 gdata_documents_service_remove_entry_from_folder_finish
 gdata_picasaweb_service_upload_file
 gdata_picasaweb_service_finish_file_upload
+gdata_youtube_service_upload_video
+gdata_youtube_service_finish_video_upload
diff --git a/gdata/services/youtube/gdata-youtube-service.c b/gdata/services/youtube/gdata-youtube-service.c
index ec59191..bd75b23 100644
--- a/gdata/services/youtube/gdata-youtube-service.c
+++ b/gdata/services/youtube/gdata-youtube-service.c
@@ -596,76 +596,36 @@ gdata_youtube_service_query_related_async (GDataYouTubeService *self, GDataYouTu
 	                           GDATA_TYPE_YOUTUBE_VIDEO, cancellable, progress_callback, progress_user_data, callback, user_data);
 }
 
-static GOutputStream *
-get_file_output_stream (GDataYouTubeService *self, GDataYouTubeVideo *video_entry, GFile *video_data, GError **error)
-{
-	GFileInfo *file_info = NULL;
-	const gchar *slug = NULL, *content_type = NULL;
-	GOutputStream *output_stream;
-
-	file_info = g_file_query_info (video_data, "standard::display-name,standard::content-type", G_FILE_QUERY_INFO_NONE, NULL, error);
-	if (file_info == NULL)
-		return NULL;
-
-	slug = g_file_info_get_display_name (file_info);
-	content_type = g_file_info_get_content_type (file_info);
-
-	/* Streaming upload support using GDataUploadStream; automatically handles the XML and multipart stuff for us */
-	output_stream = gdata_upload_stream_new (GDATA_SERVICE (self), SOUP_METHOD_POST,
-	                                         "http://uploads.gdata.youtube.com/feeds/api/users/default/uploads";,
-	                                         GDATA_ENTRY (video_entry), slug, content_type);
-	g_object_unref (file_info);
-
-	return output_stream;
-}
-
-static GDataYouTubeVideo *
-parse_spliced_stream (GOutputStream *output_stream, GError **error)
-{
-	const gchar *response_body;
-	gssize response_length;
-	GDataYouTubeVideo *new_entry;
-
-	/* Get the response from the server */
-	response_body = gdata_upload_stream_get_response (GDATA_UPLOAD_STREAM (output_stream), &response_length);
-	g_assert (response_body != NULL && response_length > 0);
-
-	/* Parse the response to produce a GDataPicasaWebFile */
-	new_entry = GDATA_YOUTUBE_VIDEO (gdata_parsable_new_from_xml (GDATA_TYPE_YOUTUBE_VIDEO, response_body, (gint) response_length, error));
-
-	return new_entry;
-}
-
 /**
  * gdata_youtube_service_upload_video:
  * @self: a #GDataYouTubeService
  * @video: a #GDataYouTubeVideo to insert
- * @video_file: the video file to upload
- * @cancellable: optional #GCancellable object, or %NULL
+ * @slug: the filename to give to the uploaded file
+ * @content_type: the content type of the uploaded data
  * @error: a #GError, or %NULL
  *
- * Uploads a video to YouTube, using the properties from @video and the video file pointed to by @video_file.
+ * Uploads a video to YouTube, using the properties from @video and the file data written to the resulting #GDataUploadStream.
  *
  * If @video has already been inserted, a %GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED error will be returned. If no user is authenticated
  * with the service, %GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED will be returned.
  *
- * If there is a problem reading @video_file, an error from g_file_load_contents() or g_file_query_info() will be returned. Other errors from
- * #GDataServiceError can be returned for other exceptional conditions, as determined by the server.
+ * The stream returned by this function should be written to using the standard #GOutputStream methods, asychronously or synchronously. Once the stream
+ * is closed (using g_output_stream_close()), gdata_youtube_service_finish_video_upload() should be called on it to parse and return the updated
+ * #GDataYouTubeVideo for the uploaded video. This must be done, as @video isn't updated in-place.
+ *
+ * Any upload errors will be thrown by the stream methods, and may come from the #GDataServiceError domain.
+ *
+ * Return value: (transfer full): a #GDataUploadStream to write the video data to, or %NULL; unref with g_object_unref()
  *
- * Return value: (transfer full): the inserted #GDataYouTubeVideo with updated properties from @video; unref with g_object_unref()
+ * Since: 0.8.0
  **/
-GDataYouTubeVideo *
-gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo *video, GFile *video_file, GCancellable *cancellable, GError **error)
+GDataUploadStream *
+gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo *video, const gchar *slug, const gchar *content_type, GError **error)
 {
-	GDataYouTubeVideo *new_entry;
-	GOutputStream *output_stream;
-	GInputStream *input_stream;
-	GError *child_error = NULL;
-
 	g_return_val_if_fail (GDATA_IS_YOUTUBE_SERVICE (self), NULL);
 	g_return_val_if_fail (GDATA_IS_YOUTUBE_VIDEO (video), NULL);
-	g_return_val_if_fail (G_IS_FILE (video_file), NULL);
-	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+	g_return_val_if_fail (slug != NULL && *slug != '\0', NULL);
+	g_return_val_if_fail (content_type != NULL && *content_type != '\0', NULL);
 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
 	if (gdata_entry_is_inserted (GDATA_ENTRY (video)) == TRUE) {
@@ -680,181 +640,45 @@ gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo
 		return NULL;
 	}
 
-	output_stream = get_file_output_stream (self, video, video_file, error);
-	if (output_stream == NULL)
-		return NULL;
-
-	/* Open the video file for reading */
-	input_stream = G_INPUT_STREAM (g_file_read (video_file, cancellable, error));
-	if (input_stream == NULL) {
-		g_object_unref (output_stream);
-		return NULL;
-	}
-
-	/* Splice the streams to upload the file and metadata */
-	g_output_stream_splice (output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
-	                        cancellable, &child_error);
-
-	g_object_unref (input_stream);
-	if (child_error != NULL) {
-		g_object_unref (output_stream);
-		g_propagate_error (error, child_error);
-		return NULL;
-	}
-
-	new_entry = parse_spliced_stream (output_stream, error);
-	g_object_unref (output_stream);
-
-	return new_entry;
+	/* Streaming upload support using GDataUploadStream; automatically handles the XML and multipart stuff for us */
+	return GDATA_UPLOAD_STREAM (gdata_upload_stream_new (GDATA_SERVICE (self), SOUP_METHOD_POST,
+	                                                     "http://uploads.gdata.youtube.com/feeds/api/users/default/uploads";,
+	                                                      GDATA_ENTRY (video), slug, content_type));
 }
 
 /**
- * gdata_youtube_service_upload_video_finish:
+ * gdata_youtube_service_finish_video_upload:
  * @self: a #GDataYouTubeService
- * @result: a #GSimpleAsyncResult
+ * @upload_stream: the #GDataUploadStream from the operation
  * @error: a #GError, or %NULL
  *
- * This should be called to obtain the result of a call to gdata_youtube_service_upload_video_async() and to check for errors.
+ * Finish off a video upload operation started by gdata_youtube_service_upload_video(), parsing the result and returning the new #GDataYouTubeVideo.
  *
- * If there is a problem reading the subect file's data, an error from g_output_stream_splice() or g_file_query_info() will be returned. Other errors
- * from #GDataServiceError can be returned for other exceptional conditions, as determined by the server.
+ * If an error occurred during the upload operation, it will have been returned during the operation (e.g. by g_output_stream_splice() or one
+ * of the other stream methods). In such a case, %NULL will be returned but @error will remain unset. @error is only set in the case that the server
+ * indicates that the operation was successful, but an error is encountered in parsing the result sent by the server.
  *
- * If the video to upload has already been inserted, a %GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED error will be set. If no user is authenticated with
- * the service when trying to upload it, %GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED will be set.
- *
- * Return value: (transfer full): the inserted #GDataYouTubeVideo; unref with g_object_unref()
+ * Return value: (transfer full): the new #GDataYouTubeVideo, or %NULL; unref with g_object_unref()
  *
  * Since: 0.8.0
  */
 GDataYouTubeVideo *
-gdata_youtube_service_upload_video_finish (GDataYouTubeService *self, GAsyncResult *result, GError **error)
+gdata_youtube_service_finish_video_upload (GDataYouTubeService *self, GDataUploadStream *upload_stream, GError **error)
 {
+	const gchar *response_body;
+	gssize response_length;
+
 	g_return_val_if_fail (GDATA_IS_YOUTUBE_SERVICE (self), NULL);
-	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+	g_return_val_if_fail (GDATA_IS_UPLOAD_STREAM (upload_stream), NULL);
 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+	/* Get the response from the server */
+	response_body = gdata_upload_stream_get_response (upload_stream, &response_length);
+	if (response_body == NULL || response_length == 0)
 		return NULL;
 
-	g_assert (gdata_youtube_service_upload_video_async == g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (result)));
-
-	return GDATA_YOUTUBE_VIDEO (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
-}
-
-typedef struct {
-	GDataYouTubeService *service;
-	GAsyncReadyCallback callback;
-	gpointer user_data;
-} UploadVideoAsyncData;
-
-static void
-upload_video_async_data_free (UploadVideoAsyncData *data)
-{
-	g_object_unref (data->service);
-	g_slice_free (UploadVideoAsyncData, data);
-}
-
-static void
-upload_video_async_cb (GOutputStream *output_stream, GAsyncResult *result, UploadVideoAsyncData *data)
-{
-	GError *error = NULL;
-	GDataYouTubeVideo *video = NULL;
-	GSimpleAsyncResult *async_result;
-
-	g_output_stream_splice_finish (output_stream, result, &error);
-
-	/* If we're error free, parse the file from the stream */
-	if (error == NULL)
-		video = parse_spliced_stream (output_stream, &error);
-
-	if (error == NULL && video != NULL) {
-		async_result = g_simple_async_result_new (G_OBJECT (data->service), (GAsyncReadyCallback) data->callback,
-		                                          data->user_data, gdata_youtube_service_upload_video_async);
-	} else {
-		async_result = g_simple_async_result_new_from_error (G_OBJECT (data->service), (GAsyncReadyCallback) data->callback,
-		                                                     data->user_data, error);
-	}
-
-	g_simple_async_result_set_op_res_gpointer (async_result, video, NULL);
-
-	g_simple_async_result_complete (async_result);
-
-	upload_video_async_data_free (data);
-}
-
-/**
- * gdata_youtube_service_upload_video_async:
- * @self: a #GDataYouTubeService
- * @video_entry: a #GDataYouTubeVideo to insert
- * @video_data: the actual file to upload
- * @cancellable: optional #GCancellable object, or %NULL
- * @callback: a #GAsyncReadyCallback to call when the upload is finished
- * @user_data: (closure): data to pass to the @callback function
- *
- * Uploads a video to YouTube asynchronously, using the @video_data from disk and the metadata from @video_entry. A user must be authenticated to use
- * this function. Note that uploaded videos aren't publicly visible on YouTube immediately; they need to go through a moderation queue first.
- *
- * @callback should call gdata_youtube_service_upload_video_finish() to obtain a #GDataYouTubeVideo representing the uploaded video and check for
- * possible errors.
- *
- * Since: 0.8.0
- **/
-void
-gdata_youtube_service_upload_video_async (GDataYouTubeService *self, GDataYouTubeVideo *video_entry, GFile *video_data, GCancellable *cancellable,
-                                          GAsyncReadyCallback callback, gpointer user_data)
-{
-	GOutputStream *output_stream;
-	GInputStream *input_stream;
-	UploadVideoAsyncData *data;
-	GSimpleAsyncResult *result;
-	GError *error = NULL;
-
-	g_return_if_fail (GDATA_IS_YOUTUBE_SERVICE (self));
-	g_return_if_fail (GDATA_IS_YOUTUBE_VIDEO (video_entry));
-	g_return_if_fail (G_IS_FILE (video_data));
-	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
-
-	if (gdata_entry_is_inserted (GDATA_ENTRY (video_entry)) == TRUE) {
-		g_set_error_literal (&error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED,
-		                     _("The entry has already been inserted."));
-		goto error;
-	}
-
-	if (gdata_service_is_authenticated (GDATA_SERVICE (self)) == FALSE) {
-		g_set_error_literal (&error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
-		                     _("You must be authenticated to upload a video."));
-		goto error;
-	}
-
-	/* Prepare and retrieve a #GDataOutputStream for the file and its data */
-	output_stream = get_file_output_stream (self, video_entry, video_data, &error);
-	if (output_stream == NULL)
-		goto error;
-
-	/* Pipe the input file to the upload stream */
-	input_stream = G_INPUT_STREAM (g_file_read (video_data, cancellable, &error));
-	if (input_stream == NULL) {
-		g_object_unref (output_stream);
-		goto error;
-	}
-
-	data = g_slice_new (UploadVideoAsyncData);
-	data->service = g_object_ref (self);
-	data->callback = callback;
-	data->user_data = user_data;
-
-	/* Actually transfer the data */
-	g_output_stream_splice_async (output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
-	                              0, cancellable, (GAsyncReadyCallback) upload_video_async_cb, data);
-
-	g_object_unref (input_stream);
-	g_object_unref (output_stream);
-
-	return;
-
-error:
-	result = g_simple_async_result_new_from_error (G_OBJECT (self), callback, user_data, error);
-	g_simple_async_result_complete (result);
+	/* Parse the response to produce a GDataYouTubeVideo */
+	return GDATA_YOUTUBE_VIDEO (gdata_parsable_new_from_xml (GDATA_TYPE_YOUTUBE_VIDEO, response_body, (gint) response_length, error));
 }
 
 /**
diff --git a/gdata/services/youtube/gdata-youtube-service.h b/gdata/services/youtube/gdata-youtube-service.h
index 5a7ba58..5268640 100644
--- a/gdata/services/youtube/gdata-youtube-service.h
+++ b/gdata/services/youtube/gdata-youtube-service.h
@@ -25,6 +25,7 @@
 #include <libsoup/soup.h>
 
 #include <gdata/gdata-service.h>
+#include <gdata/gdata-upload-stream.h>
 #include <gdata/services/youtube/gdata-youtube-video.h>
 #include <gdata/app/gdata-app-categories.h>
 
@@ -131,11 +132,9 @@ void gdata_youtube_service_query_related_async (GDataYouTubeService *self, GData
                                                 GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
                                                 GAsyncReadyCallback callback, gpointer user_data);
 
-GDataYouTubeVideo *gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo *video, GFile *video_file,
-                                                       GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
-void gdata_youtube_service_upload_video_async (GDataYouTubeService *self, GDataYouTubeVideo *video_entry, GFile *video_data,
-                                               GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
-GDataYouTubeVideo *gdata_youtube_service_upload_video_finish (GDataYouTubeService *self, GAsyncResult *result,
+GDataUploadStream *gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo *video, const gchar *slug,
+                                                       const gchar *content_type, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+GDataYouTubeVideo *gdata_youtube_service_finish_video_upload (GDataYouTubeService *self, GDataUploadStream *upload_stream,
                                                               GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
 
 const gchar *gdata_youtube_service_get_developer_key (GDataYouTubeService *self) G_GNUC_PURE;
diff --git a/gdata/tests/youtube.c b/gdata/tests/youtube.c
index daa5bc5..43c2ec0 100644
--- a/gdata/tests/youtube.c
+++ b/gdata/tests/youtube.c
@@ -259,16 +259,24 @@ test_query_related_async (gconstpointer service)
 }
 
 typedef struct {
+	GDataYouTubeService *service;
 	GDataYouTubeVideo *video;
 	GDataYouTubeVideo *updated_video;
 	GFile *video_file;
+	gchar *slug;
+	gchar *content_type;
+	GFileInputStream *file_stream;
 } UploadData;
 
 static void
 setup_upload (UploadData *data, gconstpointer service)
 {
 	GDataMediaCategory *category;
+	GFileInfo *file_info;
 	const gchar * const tags[] = { "toast", "wedding", NULL };
+	GError *error = NULL;
+
+	data->service = g_object_ref ((gpointer) service);
 
 	/* Create the metadata for the video being uploaded */
 	data->video = gdata_youtube_video_new (NULL);
@@ -283,6 +291,22 @@ setup_upload (UploadData *data, gconstpointer service)
 	/* Get a file to upload */
 	/* TODO: fix the path */
 	data->video_file = g_file_new_for_path (TEST_FILE_DIR "sample.ogg");
+
+	/* Get the file's info */
+	file_info = g_file_query_info (data->video_file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+	                               G_FILE_QUERY_INFO_NONE, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (G_IS_FILE_INFO (file_info));
+
+	data->slug = g_strdup (g_file_info_get_display_name (file_info));
+	data->content_type = g_strdup (g_file_info_get_content_type (file_info));
+
+	g_object_unref (file_info);
+
+	/* Get an input stream for the file */
+	data->file_stream = g_file_read (data->video_file, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (G_IS_FILE_INPUT_STREAM (data->file_stream));
 }
 
 static void
@@ -296,19 +320,49 @@ teardown_upload (UploadData *data, gconstpointer service)
 
 	g_object_unref (data->video);
 	g_object_unref (data->video_file);
+	g_free (data->slug);
+	g_free (data->content_type);
+	g_object_unref (data->file_stream);
+	g_object_unref (data->service);
 }
 
 static void
 test_upload_simple (UploadData *data, gconstpointer service)
 {
+	GDataUploadStream *upload_stream;
+	const gchar * const *tags, * const *tags2;
+	gssize transfer_size;
 	GError *error = NULL;
 
+	/* Prepare the upload stream */
+	upload_stream = gdata_youtube_service_upload_video (GDATA_YOUTUBE_SERVICE (service), data->video, data->slug, data->content_type, &error);
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_UPLOAD_STREAM (upload_stream));
+
 	/* Upload the video */
-	data->updated_video = gdata_youtube_service_upload_video (GDATA_YOUTUBE_SERVICE (service), data->video, data->video_file, NULL, &error);
+	transfer_size = g_output_stream_splice (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (data->file_stream),
+	                                        G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, &error);
+	g_assert_no_error (error);
+	g_assert_cmpint (transfer_size, >, 0);
+
+	/* Finish off the upload */
+	data->updated_video = gdata_youtube_service_finish_video_upload (GDATA_YOUTUBE_SERVICE (service), upload_stream, &error);
 	g_assert_no_error (error);
 	g_assert (GDATA_IS_YOUTUBE_VIDEO (data->updated_video));
 
-	/* TODO: check entries and feed properties */
+	/* Check the video's properties */
+	g_assert (gdata_entry_is_inserted (GDATA_ENTRY (data->updated_video)));
+	g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (data->updated_video)), ==, gdata_entry_get_title (GDATA_ENTRY (data->video)));
+	g_assert_cmpstr (gdata_youtube_video_get_description (data->updated_video), ==, gdata_youtube_video_get_description (data->video));
+	g_assert_cmpstr (gdata_media_category_get_category (gdata_youtube_video_get_category (data->updated_video)), ==,
+	                 gdata_media_category_get_category (gdata_youtube_video_get_category (data->video)));
+
+	tags = gdata_youtube_video_get_keywords (data->video);
+	tags2 = gdata_youtube_video_get_keywords (data->updated_video);
+	g_assert_cmpuint (g_strv_length ((gchar**) tags2), ==, g_strv_length ((gchar**) tags));
+	g_assert_cmpstr (tags2[0], ==, tags[0]);
+	g_assert_cmpstr (tags2[1], ==, tags[1]);
+	g_assert_cmpstr (tags2[2], ==, tags[2]);
 }
 
 typedef struct {
@@ -331,15 +385,35 @@ teardown_upload_async (UploadAsyncData *data, gconstpointer service)
 }
 
 static void
-test_upload_async_cb (GDataService *service, GAsyncResult *async_result, UploadAsyncData *data)
+test_upload_async_cb (GOutputStream *stream, GAsyncResult *result, UploadAsyncData *data)
 {
+	const gchar * const *tags, * const *tags2;
+	gssize transfer_size;
 	GError *error = NULL;
 
-	data->data.updated_video = gdata_youtube_service_upload_video_finish (GDATA_YOUTUBE_SERVICE (service), async_result, &error);
+	/* Finish off the transfer */
+	transfer_size = g_output_stream_splice_finish (stream, result, &error);
+	g_assert_no_error (error);
+	g_assert_cmpint (transfer_size, >, 0);
+
+	/* Finish off the upload */
+	data->data.updated_video = gdata_youtube_service_finish_video_upload (data->data.service, GDATA_UPLOAD_STREAM (stream), &error);
 	g_assert_no_error (error);
 	g_assert (GDATA_IS_YOUTUBE_VIDEO (data->data.updated_video));
 
+	/* Check the video's properties */
+	g_assert (gdata_entry_is_inserted (GDATA_ENTRY (data->data.updated_video)));
 	g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (data->data.updated_video)), ==, gdata_entry_get_title (GDATA_ENTRY (data->data.video)));
+	g_assert_cmpstr (gdata_youtube_video_get_description (data->data.updated_video), ==, gdata_youtube_video_get_description (data->data.video));
+	g_assert_cmpstr (gdata_media_category_get_category (gdata_youtube_video_get_category (data->data.updated_video)), ==,
+	                 gdata_media_category_get_category (gdata_youtube_video_get_category (data->data.video)));
+
+	tags = gdata_youtube_video_get_keywords (data->data.video);
+	tags2 = gdata_youtube_video_get_keywords (data->data.updated_video);
+	g_assert_cmpuint (g_strv_length ((gchar**) tags2), ==, g_strv_length ((gchar**) tags));
+	g_assert_cmpstr (tags2[0], ==, tags[0]);
+	g_assert_cmpstr (tags2[1], ==, tags[1]);
+	g_assert_cmpstr (tags2[2], ==, tags[2]);
 
 	g_main_loop_quit (data->main_loop);
 }
@@ -347,10 +421,22 @@ test_upload_async_cb (GDataService *service, GAsyncResult *async_result, UploadA
 static void
 test_upload_async (UploadAsyncData *data, gconstpointer service)
 {
-	/* Upload the video */
-	gdata_youtube_service_upload_video_async (GDATA_YOUTUBE_SERVICE (service), data->data.video, data->data.video_file, NULL,
-	                                          (GAsyncReadyCallback) test_upload_async_cb, data);
+	GDataUploadStream *upload_stream;
+	GError *error = NULL;
+
+	/* Prepare the upload stream */
+	upload_stream = gdata_youtube_service_upload_video (GDATA_YOUTUBE_SERVICE (service), data->data.video, data->data.slug,
+	                                                    data->data.content_type, &error);
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_UPLOAD_STREAM (upload_stream));
+
+	/* Upload the video asynchronously */
+	g_output_stream_splice_async (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (data->data.file_stream),
+	                              G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL,
+	                              (GAsyncReadyCallback) test_upload_async_cb, data);
 	g_main_loop_run (data->main_loop);
+
+	g_object_unref (upload_stream);
 }
 
 static void



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