[libgdata] youtube: Add gdata_youtube_service_upload_video_async()
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgdata] youtube: Add gdata_youtube_service_upload_video_async()
- Date: Thu, 28 Oct 2010 09:10:45 +0000 (UTC)
commit 85a249181981af3333673d9e1ea4e76c2b691c73
Author: Philip Withnall <philip tecnocode co uk>
Date: Thu Oct 28 10:10:17 2010 +0100
youtube: Add gdata_youtube_service_upload_video_async()
This includes tests and documentation.
docs/reference/gdata-sections.txt | 2 +
gdata/gdata.symbols | 2 +
gdata/services/youtube/gdata-youtube-service.c | 213 +++++++++++++++++++++---
gdata/services/youtube/gdata-youtube-service.h | 4 +
gdata/tests/youtube.c | 53 ++++++
5 files changed, 252 insertions(+), 22 deletions(-)
---
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 68691c4..7e8a956 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -188,6 +188,8 @@ 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_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 5cee491..5ab8320 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -852,3 +852,5 @@ 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
diff --git a/gdata/services/youtube/gdata-youtube-service.c b/gdata/services/youtube/gdata-youtube-service.c
index d285b69..8fcc79e 100644
--- a/gdata/services/youtube/gdata-youtube-service.c
+++ b/gdata/services/youtube/gdata-youtube-service.c
@@ -651,6 +651,46 @@ 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
@@ -672,13 +712,9 @@ gdata_youtube_service_query_related_async (GDataYouTubeService *self, GDataYouTu
GDataYouTubeVideo *
gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo *video, GFile *video_file, GCancellable *cancellable, GError **error)
{
- /* TODO: Async variant */
GDataYouTubeVideo *new_entry;
GOutputStream *output_stream;
GInputStream *input_stream;
- const gchar *slug, *content_type, *response_body;
- gssize response_length;
- GFileInfo *file_info;
GError *child_error = NULL;
g_return_val_if_fail (GDATA_IS_YOUTUBE_SERVICE (self), NULL);
@@ -699,19 +735,7 @@ gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo
return NULL;
}
- file_info = g_file_query_info (video_file, "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), slug, content_type);
-
- g_object_unref (file_info);
+ output_stream = get_file_output_stream (self, video, video_file, error);
if (output_stream == NULL)
return NULL;
@@ -733,17 +757,162 @@ gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo
return NULL;
}
- /* Get and parse 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);
-
- new_entry = GDATA_YOUTUBE_VIDEO (gdata_parsable_new_from_xml (GDATA_TYPE_YOUTUBE_VIDEO, response_body, (gint) response_length, error));
+ new_entry = parse_spliced_stream (output_stream, error);
g_object_unref (output_stream);
return new_entry;
}
/**
+ * gdata_youtube_service_upload_video_finish:
+ * @self: a #GDataYouTubeService
+ * @result: a #GSimpleAsyncResult
+ * @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.
+ *
+ * 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 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()
+ *
+ * Since: 0.8.0
+ */
+GDataYouTubeVideo *
+gdata_youtube_service_upload_video_finish (GDataYouTubeService *self, GAsyncResult *result, GError **error)
+{
+ 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 (error == NULL || *error == NULL, NULL);
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ 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);
+}
+
+/**
* gdata_youtube_service_get_developer_key:
* @self: a #GDataYouTubeService
*
diff --git a/gdata/services/youtube/gdata-youtube-service.h b/gdata/services/youtube/gdata-youtube-service.h
index 19566c9..67c1427 100644
--- a/gdata/services/youtube/gdata-youtube-service.h
+++ b/gdata/services/youtube/gdata-youtube-service.h
@@ -133,6 +133,10 @@ void gdata_youtube_service_query_related_async (GDataYouTubeService *self, GData
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,
+ GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
const gchar *gdata_youtube_service_get_developer_key (GDataYouTubeService *self) G_GNUC_PURE;
const gchar *gdata_youtube_service_get_youtube_user (GDataYouTubeService *self) G_GNUC_PURE;
diff --git a/gdata/tests/youtube.c b/gdata/tests/youtube.c
index d86a852..d8082a1 100644
--- a/gdata/tests/youtube.c
+++ b/gdata/tests/youtube.c
@@ -320,6 +320,58 @@ test_upload_simple (gconstpointer service)
}
static void
+test_upload_async_cb (GDataService *service, GAsyncResult *async_result, GMainLoop *main_loop)
+{
+ GDataYouTubeVideo *new_video;
+ GError *error = NULL;
+
+ new_video = gdata_youtube_service_upload_video_finish (GDATA_YOUTUBE_SERVICE (service), async_result, &error);
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_YOUTUBE_VIDEO (new_video));
+ g_clear_error (&error);
+
+ g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (new_video)), ==, "Bad Wedding Toast");
+
+ g_main_loop_quit (main_loop);
+ g_object_unref (new_video);
+}
+
+static void
+test_upload_async (gconstpointer service)
+{
+ GDataYouTubeVideo *video;
+ GDataMediaCategory *category;
+ GFile *video_file;
+ const gchar * const tags[] = { "toast", "wedding", NULL };
+ GMainLoop *main_loop;
+
+ main_loop = g_main_loop_new (NULL, TRUE);
+
+ video = gdata_youtube_video_new (NULL);
+
+ gdata_entry_set_title (GDATA_ENTRY (video), "Bad Wedding Toast");
+ gdata_youtube_video_set_description (video, "I gave a bad toast at my friend's wedding.");
+ category = gdata_media_category_new ("People", "http://gdata.youtube.com/schemas/2007/categories.cat", NULL);
+ gdata_youtube_video_set_category (video, category);
+ g_object_unref (category);
+ gdata_youtube_video_set_keywords (video, tags);
+
+ /* TODO: fix the path */
+ video_file = g_file_new_for_path (TEST_FILE_DIR "sample.ogg");
+
+ /* Upload the video */
+ gdata_youtube_service_upload_video_async (GDATA_YOUTUBE_SERVICE (service), video, video_file, NULL,
+ (GAsyncReadyCallback) test_upload_async_cb, main_loop);
+
+ g_object_unref (video);
+ g_object_unref (video_file);
+
+ g_main_loop_run (main_loop);
+ g_main_loop_unref (main_loop);
+}
+
+
+static void
test_parsing_app_control (void)
{
GDataYouTubeVideo *video;
@@ -1092,6 +1144,7 @@ main (int argc, char *argv[])
g_test_add_data_func ("/youtube/query/related_async", service, test_query_related_async);
g_test_add_data_func ("/youtube/upload/simple", service, test_upload_simple);
+ g_test_add_data_func ("/youtube/upload/async", service, test_upload_async);
g_test_add_data_func ("/youtube/query/single", service, test_query_single);
g_test_add_data_func ("/youtube/query/single_async", service, test_query_single_async);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]