[libgdata] [youtube] Convert YouTube video uploading to use GDataUploadStream
- From: Philip Withnall <pwithnall src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [libgdata] [youtube] Convert YouTube video uploading to use GDataUploadStream
- Date: Wed, 20 Jan 2010 20:55:54 +0000 (UTC)
commit a61f3c360cb0fcd4340cb468109b6ab212245690
Author: Philip Withnall <philip tecnocode co uk>
Date: Wed Jan 20 20:54:46 2010 +0000
[youtube] Convert YouTube video uploading to use GDataUploadStream
Convert gdata_youtube_service_upload_video() to use GDataUploadStream, which
should lower memory consumption (by using streaming I/O) and ease maintenance.
gdata/services/documents/gdata-documents-service.c | 6 +-
gdata/services/youtube/gdata-youtube-service.c | 145 ++++++-------------
2 files changed, 50 insertions(+), 101 deletions(-)
---
diff --git a/gdata/services/documents/gdata-documents-service.c b/gdata/services/documents/gdata-documents-service.c
index 4f38b7c..f73947f 100644
--- a/gdata/services/documents/gdata-documents-service.c
+++ b/gdata/services/documents/gdata-documents-service.c
@@ -354,6 +354,7 @@ upload_update_document (GDataDocumentsService *self, GDataDocumentsEntry *docume
gssize response_length;
GFileInfo *file_info = NULL;
GType new_document_type;
+ GError *child_error = NULL;
/* Get some information about the file we're uploading */
if (document_file != NULL) {
@@ -418,11 +419,12 @@ upload_update_document (GDataDocumentsService *self, GDataDocumentsEntry *docume
}
g_output_stream_splice (output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
- cancellable, error);
+ cancellable, &child_error);
g_object_unref (input_stream);
- if (error != NULL && *error != NULL) {
+ if (child_error != NULL) {
g_object_unref (output_stream);
+ g_propagate_error (error, child_error);
return NULL;
}
diff --git a/gdata/services/youtube/gdata-youtube-service.c b/gdata/services/youtube/gdata-youtube-service.c
index 108c453..e973572 100644
--- a/gdata/services/youtube/gdata-youtube-service.c
+++ b/gdata/services/youtube/gdata-youtube-service.c
@@ -41,6 +41,7 @@
#include "gdata-private.h"
#include "gdata-parser.h"
#include "atom/gdata-link.h"
+#include "gdata-upload-stream.h"
/* Standards reference here: http://code.google.com/apis/youtube/2.0/reference.html */
@@ -763,137 +764,83 @@ gdata_youtube_service_query_related_async (GDataYouTubeService *self, GDataYouTu
* Return value: the inserted #GDataYouTubeVideo with updated properties from @video; unref with g_object_unref()
**/
GDataYouTubeVideo *
-gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo *video, GFile *video_file,
- GCancellable *cancellable, GError **error)
+gdata_youtube_service_upload_video (GDataYouTubeService *self, GDataYouTubeVideo *video, GFile *video_file, GCancellable *cancellable, GError **error)
{
/* TODO: Async variant */
- #define BOUNDARY_STRING "0xdeadbeef6e0808d5e6ed8bc168390bcc"
-
- GDataServiceClass *klass;
- SoupMessage *message;
- gchar *entry_xml, *second_chunk_header, *upload_data, *video_contents, *i;
- const gchar *first_chunk_header, *footer;
- guint status;
- GFileInfo *video_file_info;
- gsize content_length, first_chunk_header_length, second_chunk_header_length, entry_xml_length, video_length, footer_length;
+ GDataYouTubeVideo *new_entry;
+ GDataCategory *category;
+ 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);
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 (error == NULL || *error == NULL, NULL);
if (gdata_entry_is_inserted (GDATA_ENTRY (video)) == TRUE) {
g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED,
- _("The entry has already been inserted."));
+ _("The entry has already been inserted."));
return NULL;
}
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."));
+ _("You must be authenticated to upload a video."));
return NULL;
}
- message = soup_message_new (SOUP_METHOD_POST, "http://uploads.gdata.youtube.com/feeds/api/users/default/uploads");
-
- /* Make sure subclasses set their headers */
- klass = GDATA_SERVICE_GET_CLASS (self);
- if (klass->append_query_headers != NULL)
- klass->append_query_headers (GDATA_SERVICE (self), message);
+ /* Add the "video" kind if the entry is missing it. If it already has the kind category, no duplicate is added. */
+ category = gdata_category_new ("http://gdata.youtube.com/schemas/2007#video", "http://schemas.google.com/g/2005#kind", NULL);
+ gdata_entry_add_category (GDATA_ENTRY (video), category);
+ g_object_unref (category);
- /* Get the data early so we can calculate the content length */
- if (g_file_load_contents (video_file, NULL, &video_contents, &video_length, NULL, error) == FALSE) {
- g_object_unref (message);
+ 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;
- }
- entry_xml = gdata_parsable_get_xml (GDATA_PARSABLE (video));
+ slug = g_file_info_get_display_name (file_info);
+ content_type = g_file_info_get_content_type (file_info);
- /* Check for cancellation */
- if (g_cancellable_set_error_if_cancelled (cancellable, error) == TRUE) {
- g_object_unref (message);
- g_free (entry_xml);
- return NULL;
- }
+ /* 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);
- video_file_info = g_file_query_info (video_file, "standard::display-name,standard::content-type", G_FILE_QUERY_INFO_NONE, NULL, error);
- if (video_file_info == NULL) {
- g_object_unref (message);
- g_free (entry_xml);
+ g_object_unref (file_info);
+ if (output_stream == NULL)
return NULL;
- }
- /* Check for cancellation */
- if (g_cancellable_set_error_if_cancelled (cancellable, error) == TRUE) {
- g_object_unref (message);
- g_free (entry_xml);
- g_object_unref (video_file_info);
+ /* 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;
}
- /* Add video-upload--specific headers */
- soup_message_headers_append (message->request_headers, "Slug", g_file_info_get_display_name (video_file_info));
-
- first_chunk_header = "--" BOUNDARY_STRING "\nContent-Type: application/atom+xml; charset=UTF-8\n\n<?xml version='1.0'?>";
- second_chunk_header = g_strdup_printf ("\n--" BOUNDARY_STRING "\nContent-Type: %s\nContent-Transfer-Encoding: binary\n\n",
- g_file_info_get_content_type (video_file_info));
- footer = "\n--" BOUNDARY_STRING "--";
-
- g_object_unref (video_file_info);
-
- first_chunk_header_length = strlen (first_chunk_header);
- second_chunk_header_length = strlen (second_chunk_header);
- footer_length = strlen (footer);
- entry_xml_length = strlen (entry_xml);
-
- content_length = first_chunk_header_length + entry_xml_length + second_chunk_header_length + video_length + footer_length;
-
- /* Build the upload data */
- upload_data = i = g_malloc (content_length);
+ /* 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);
- memcpy (upload_data, first_chunk_header, first_chunk_header_length);
- i += first_chunk_header_length;
-
- memcpy (i, entry_xml, entry_xml_length);
- i += entry_xml_length;
- g_free (entry_xml);
-
- memcpy (i, second_chunk_header, second_chunk_header_length);
- g_free (second_chunk_header);
- i += second_chunk_header_length;
-
- memcpy (i, video_contents, video_length);
- g_free (video_contents);
- i += video_length;
-
- memcpy (i, footer, footer_length);
-
- /* Append the data */
- soup_message_set_request (message, "multipart/related; boundary=" BOUNDARY_STRING, SOUP_MEMORY_TAKE, upload_data, content_length);
-
- /* Send the message */
- status = _gdata_service_send_message (GDATA_SERVICE (self), message, error);
- if (status == SOUP_STATUS_NONE) {
- g_object_unref (message);
+ g_object_unref (input_stream);
+ if (child_error != NULL) {
+ g_object_unref (output_stream);
+ g_propagate_error (error, child_error);
return NULL;
}
- /* Check for cancellation */
- if (g_cancellable_set_error_if_cancelled (cancellable, error) == TRUE) {
- g_object_unref (message);
- return NULL;
- }
-
- if (status != 201) {
- /* Error */
- parse_error_response (GDATA_SERVICE (self), GDATA_SERVICE_ERROR_WITH_INSERTION, status, message->reason_phrase,
- message->response_body->data, message->response_body->length, error);
- g_object_unref (message);
- 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);
- g_assert (message->response_body->data != NULL);
+ new_entry = GDATA_YOUTUBE_VIDEO (gdata_parsable_new_from_xml (GDATA_TYPE_YOUTUBE_VIDEO, response_body, (gint) response_length, error));
+ g_object_unref (output_stream);
- return GDATA_YOUTUBE_VIDEO (gdata_parsable_new_from_xml (GDATA_TYPE_YOUTUBE_VIDEO, message->response_body->data,
- (gint) message->response_body->length, error));
+ return new_entry;
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]