From 37364525ace31d05626c081df50b401bb0ecb328 Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Sun, 20 Mar 2011 23:47:37 -0700 Subject: Added Youtube proxy for uploading videos. Fixes: BMC#14749 --- rest-extras/Makefile.am | 7 +- rest-extras/youtube-proxy-private.h | 31 +++ rest-extras/youtube-proxy.c | 381 ++++++++++++++++++++++++++++++++++++ rest-extras/youtube-proxy.h | 91 +++++++++ 4 files changed, 508 insertions(+), 2 deletions(-) create mode 100644 rest-extras/youtube-proxy-private.h create mode 100644 rest-extras/youtube-proxy.c create mode 100644 rest-extras/youtube-proxy.h (limited to 'rest-extras') diff --git a/rest-extras/Makefile.am b/rest-extras/Makefile.am index 57b7cfc..2db330a 100644 --- a/rest-extras/Makefile.am +++ b/rest-extras/Makefile.am @@ -6,12 +6,15 @@ lib_sources = \ flickr-proxy-private.h \ lastfm-proxy.c \ lastfm-proxy-call.c \ - lastfm-proxy-private.h + lastfm-proxy-private.h \ + youtube-proxy.c \ + youtube-proxy-private.h lib_headers = \ flickr-proxy.h \ flickr-proxy-call.h \ lastfm-proxy.h \ - lastfm-proxy-call.h + lastfm-proxy-call.h \ + youtube-proxy.h lib_LTLIBRARIES = librest-extras-@API_VERSION@.la diff --git a/rest-extras/youtube-proxy-private.h b/rest-extras/youtube-proxy-private.h new file mode 100644 index 0000000..83b7bc5 --- /dev/null +++ b/rest-extras/youtube-proxy-private.h @@ -0,0 +1,31 @@ +/* + * librest - RESTful web services access + * Copyright (c) 2011 Collabora Ltd. + * + * Authors: Eitan Isaacson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "youtube-proxy.h" + +#define YOUTUBE_PROXY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), YOUTUBE_TYPE_PROXY, YoutubeProxyPrivate)) + +struct _YoutubeProxyPrivate { + char *developer_key; + char *user_auth; +}; + diff --git a/rest-extras/youtube-proxy.c b/rest-extras/youtube-proxy.c new file mode 100644 index 0000000..fc824a2 --- /dev/null +++ b/rest-extras/youtube-proxy.c @@ -0,0 +1,381 @@ +/* + * librest - RESTful web services access + * Copyright (c) 2008, 2009, Intel Corporation. + * + * Authors: Rob Bradford + * Ross Burton + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "rest/rest-private.h" +#include "youtube-proxy.h" +#include "youtube-proxy-private.h" + +G_DEFINE_TYPE (YoutubeProxy, youtube_proxy, REST_TYPE_PROXY) + +#define UPLOAD_URL \ + "http://uploads.gdata.youtube.com/feeds/api/users/default/uploads" + +enum { + PROP_0, + PROP_DEVELOPER_KEY, + PROP_USER_AUTH, +}; + +GQuark +youtube_proxy_error_quark (void) +{ + return g_quark_from_static_string ("rest-youtube-proxy"); +} + +static void +youtube_proxy_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + YoutubeProxyPrivate *priv = YOUTUBE_PROXY_GET_PRIVATE (object); + + switch (property_id) { + case PROP_DEVELOPER_KEY: + g_value_set_string (value, priv->developer_key); + break; + case PROP_USER_AUTH: + g_value_set_string (value, priv->user_auth); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +youtube_proxy_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + YoutubeProxyPrivate *priv = YOUTUBE_PROXY_GET_PRIVATE (object); + + switch (property_id) { + case PROP_DEVELOPER_KEY: + g_free (priv->developer_key); + priv->developer_key = g_value_dup_string (value); + break; + case PROP_USER_AUTH: + g_free (priv->user_auth); + priv->user_auth = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +youtube_proxy_finalize (GObject *object) +{ + YoutubeProxyPrivate *priv = YOUTUBE_PROXY_GET_PRIVATE (object); + + g_free (priv->developer_key); + g_free (priv->user_auth); + + G_OBJECT_CLASS (youtube_proxy_parent_class)->finalize (object); +} + +static void +youtube_proxy_class_init (YoutubeProxyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (YoutubeProxyPrivate)); + + object_class->get_property = youtube_proxy_get_property; + object_class->set_property = youtube_proxy_set_property; + object_class->finalize = youtube_proxy_finalize; + + pspec = g_param_spec_string ("developer-key", "developer-key", + "The developer API key", NULL, + G_PARAM_READWRITE| + G_PARAM_CONSTRUCT_ONLY| + G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, + PROP_DEVELOPER_KEY, + pspec); + + pspec = g_param_spec_string ("user-auth", "user-auth", + "The ClientLogin token", NULL, + G_PARAM_READWRITE| + G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, + PROP_USER_AUTH, + pspec); +} + +static void +youtube_proxy_init (YoutubeProxy *self) +{ + self->priv = YOUTUBE_PROXY_GET_PRIVATE (self); +} + +RestProxy * +youtube_proxy_new (const char *developer_key) +{ + return youtube_proxy_new_with_auth (developer_key, + NULL); +} + +RestProxy * +youtube_proxy_new_with_auth (const char *developer_key, + const char *user_auth) +{ + return g_object_new (YOUTUBE_TYPE_PROXY, + "developer-key", developer_key, + "user-auth", user_auth, + NULL); +} + +static gchar * +_construct_upload_atom_xml (GHashTable *fields) +{ + GHashTableIter iter; + gpointer key, value; + RestXmlNode *entry = rest_xml_node_add_child (NULL, "entry"); + RestXmlNode *group = rest_xml_node_add_child (entry, "media:group"); + gchar *bare_xml; + gchar *full_xml; + + rest_xml_node_add_attr (entry, "xmlns", "http://www.w3.org/2005/Atom"); + rest_xml_node_add_attr (entry, "xmlns:media", + "http://search.yahoo.com/mrss/"); + rest_xml_node_add_attr (entry, "xmlns:yt", + "http://gdata.youtube.com/schemas/2007"); + + + g_hash_table_iter_init (&iter, fields); + + while (g_hash_table_iter_next (&iter, &key, &value)) { + RestXmlNode *node; + gchar *tag_name; + const gchar* field_value = value; + const gchar* field_key = key; + + tag_name = g_strdup_printf ("media:%s", field_key); + + node = rest_xml_node_add_child (group, tag_name); + + if (g_strcmp0 (field_key, "title") == 0 || + g_strcmp0 (field_key, "description") == 0) + rest_xml_node_add_attr (node, "type", "plain"); + + if (g_strcmp0 (field_key, "category") == 0) + rest_xml_node_add_attr (node, "scheme", "http://gdata.youtube.com/" + "schemas/2007/categories.cat"); + + rest_xml_node_set_content (node, field_value); + } + + bare_xml = rest_xml_node_print (entry); + full_xml = g_strdup_printf ("\n%s", bare_xml); + + rest_xml_node_unref (entry); + g_free (bare_xml); + + return full_xml; +} + +static void +_set_upload_headers (YoutubeProxy *self, + SoupMessageHeaders *headers, + const gchar *filename) +{ + YoutubeProxyPrivate *priv = self->priv; + gchar *user_auth_header; + gchar *devkey_header; + gchar *basename; + const gchar *user_agent; + + /* Set the user agent, if one was set in the proxy */ + user_agent = rest_proxy_get_user_agent (REST_PROXY (self)); + if (user_agent) + soup_message_headers_append (headers, "User-Agent", user_agent); + + user_auth_header = g_strdup_printf ("GoogleLogin auth=%s", priv->user_auth); + soup_message_headers_append (headers, "Authorization", user_auth_header); + devkey_header = g_strdup_printf ("key=%s", priv->developer_key); + soup_message_headers_append (headers, "X-GData-Key", devkey_header); + basename = g_path_get_basename (filename); + soup_message_headers_append (headers, "Slug", basename); + + g_free (user_auth_header); + g_free (devkey_header); +} + +typedef struct { + YoutubeProxy *proxy; + YoutubeProxyUploadCallback callback; + SoupMessage *message; + GObject *weak_object; + gpointer user_data; +} YoutubeProxyUploadClosure; + +static void +_upload_async_weak_notify_cb (gpointer *data, + GObject *dead_object) +{ + YoutubeProxyUploadClosure *closure = + (YoutubeProxyUploadClosure *) data; + + _rest_proxy_cancel_message (REST_PROXY (closure->proxy), closure->message); +} + +static void +_upload_async_closure_free (YoutubeProxyUploadClosure *closure) +{ + if (closure->weak_object != NULL) + g_object_weak_unref (closure->weak_object, + (GWeakNotify) _upload_async_weak_notify_cb, + closure); + + g_object_unref (closure->proxy); + + g_slice_free (YoutubeProxyUploadClosure, closure); +} + +static YoutubeProxyUploadClosure * +_upload_async_closure_new (YoutubeProxy *self, + YoutubeProxyUploadCallback callback, + SoupMessage *message, + GObject *weak_object, + gpointer user_data) +{ + YoutubeProxyUploadClosure *closure = + g_slice_new0 (YoutubeProxyUploadClosure); + + closure->proxy = g_object_ref (self); + closure->callback = callback; + closure->message = message; + closure->weak_object = weak_object; + closure->user_data = user_data; + + if (weak_object != NULL) + g_object_weak_ref (weak_object, + (GWeakNotify) _upload_async_weak_notify_cb, + closure); + return closure; +} + +static void +_upload_completed_cb (SoupSession *session, + SoupMessage *message, + gpointer user_data) +{ + YoutubeProxyUploadClosure *closure = + (YoutubeProxyUploadClosure *) user_data; + GError *error = NULL; + + if (closure->callback == NULL) + return; + + if (message->status_code < 200 || message->status_code >= 300) + error = g_error_new_literal (REST_PROXY_ERROR, + message->status_code, + message->reason_phrase); + + closure->callback (closure->proxy, message->response_body->data, error, + closure->weak_object, closure->user_data); + + _upload_async_closure_free (closure); +} + +gboolean +youtube_proxy_upload_async (YoutubeProxy *self, + const gchar *filename, + GHashTable *fields, + YoutubeProxyUploadCallback callback, + GObject *weak_object, + gpointer userdata, + GError **error) +{ + SoupMultipart *mp; + SoupMessage *message; + SoupMessageHeaders *part_headers; + SoupBuffer *sb; + gchar *content_type; + gchar *atom_xml; + GMappedFile *map; + YoutubeProxyUploadClosure *closure; + + map = g_mapped_file_new (filename, FALSE, error); + if (*error != NULL) { + g_warning ("Error opening file %s: %s", filename, (*error)->message); + return FALSE; + } + + mp = soup_multipart_new ("multipart/related"); + + atom_xml = _construct_upload_atom_xml (fields); + + sb = soup_buffer_new_with_owner (atom_xml, + strlen(atom_xml), + atom_xml, + (GDestroyNotify) g_free); + + part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); + + soup_message_headers_append (part_headers, "Content-Type", + "application/atom+xml; charset=UTF-8"); + + + soup_multipart_append_part (mp, part_headers, sb); + + soup_buffer_free (sb); + + content_type = g_content_type_guess ( + filename, + (const guchar*) g_mapped_file_get_contents (map), + g_mapped_file_get_length (map), + NULL); + + sb = soup_buffer_new_with_owner (g_mapped_file_get_contents (map), + g_mapped_file_get_length (map), + map, + (GDestroyNotify) g_mapped_file_unref); + + soup_message_headers_replace (part_headers, "Content-Type", content_type); + + soup_multipart_append_part (mp, part_headers, sb); + + soup_buffer_free (sb); + + soup_message_headers_free (part_headers); + + message = soup_form_request_new_from_multipart (UPLOAD_URL, mp); + + soup_multipart_free (mp); + + _set_upload_headers (self, message->request_headers, filename); + + closure = _upload_async_closure_new (self, callback, message, weak_object, + userdata); + + _rest_proxy_queue_message (REST_PROXY (self), message, _upload_completed_cb, + closure); + + return TRUE; +} diff --git a/rest-extras/youtube-proxy.h b/rest-extras/youtube-proxy.h new file mode 100644 index 0000000..2b4673a --- /dev/null +++ b/rest-extras/youtube-proxy.h @@ -0,0 +1,91 @@ +/* + * librest - RESTful web services access + * Copyright (c) 2008, 2009, Intel Corporation. + * + * Authors: Rob Bradford + * Ross Burton + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _YOUTUBE_PROXY +#define _YOUTUBE_PROXY + +#include + +G_BEGIN_DECLS + +#define YOUTUBE_TYPE_PROXY youtube_proxy_get_type() + +#define YOUTUBE_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), YOUTUBE_TYPE_PROXY, YoutubeProxy)) + +#define YOUTUBE_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), YOUTUBE_TYPE_PROXY, YoutubeProxyClass)) + +#define YOUTUBE_IS_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YOUTUBE_TYPE_PROXY)) + +#define YOUTUBE_IS_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), YOUTUBE_TYPE_PROXY)) + +#define YOUTUBE_PROXY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), YOUTUBE_TYPE_PROXY, YoutubeProxyClass)) + +typedef struct _YoutubeProxyPrivate YoutubeProxyPrivate; + +/** + * YoutubeProxy: + * + * #YoutubeProxy has no publicly available members. + */ +typedef struct { + RestProxy parent; + YoutubeProxyPrivate *priv; +} YoutubeProxy; + +typedef struct { + RestProxyClass parent_class; + /*< private >*/ + /* padding for future expansion */ + gpointer _padding_dummy[8]; +} YoutubeProxyClass; + +#define YOUTUBE_PROXY_ERROR youtube_proxy_error_quark() + +GType youtube_proxy_get_type (void); + +RestProxy* youtube_proxy_new (const gchar *developer_key); + +RestProxy* youtube_proxy_new_with_auth (const gchar *developer_key, + const gchar *user_auth); + +typedef void (*YoutubeProxyUploadCallback)(YoutubeProxy *proxy, + const gchar *payload, + const GError *error, + GObject *weak_object, + gpointer userdata); + +gboolean youtube_proxy_upload_async (YoutubeProxy *self, + const gchar *filename, + GHashTable *fields, + YoutubeProxyUploadCallback callback, + GObject *weak_object, + gpointer userdata, + GError **error); + +G_END_DECLS + +#endif /* _YOUTUBE_PROXY */ -- cgit v1.2.1