Hi folks, I am sending you the patch with Xabier's review. I would like to thank him here for the review work. It got indeed better. Cheers, -- Joaquim Rocha Xabier Rodriguez Calvar wrote: > O Xov, 29-04-2010 ás 16:15 +0200, Xabier Rodriguez Calvar escribiu: >> I reviewed the patch and suggested joaquim some changes. The issue is >> that we did that in person so no comments here :/ > > Basically problems were about variables initialization, a couple of > memory leaks and about not defining a function to skip the nodes, but > opening the document with the flag of XML_PARSE_NOBLANKS . > > I consider that the rest was ok. > > About the blank nodes, I am writing patches to change this in other > plugins using the same base, but I am having some trouble to test it due > to supposed problems with my gdata installation. > > Br. > > > > ------------------------------------------------------------------------ > > _______________________________________________ > grilo-list mailing list > grilo-list gnome org > http://mail.gnome.org/mailman/listinfo/grilo-list
From baabe22ddb1393230a9075d9dd73b5f465e7eb51 Mon Sep 17 00:00:00 2001 From: Joaquim Rocha <jrocha igalia com> Date: Wed, 21 Apr 2010 10:33:00 +0200 Subject: [PATCH] Added Vimeo plugin The Vimeo plugin allows to search videos, retrieving, among other data, the video's play URL. --- configure.ac | 47 +++++ src/Makefile.am | 6 +- src/vimeo/Makefile.am | 34 ++++ src/vimeo/grl-vimeo.c | 391 +++++++++++++++++++++++++++++++++++++ src/vimeo/grl-vimeo.h | 77 ++++++++ src/vimeo/gvimeo.c | 513 +++++++++++++++++++++++++++++++++++++++++++++++++ src/vimeo/gvimeo.h | 112 +++++++++++ 7 files changed, 1179 insertions(+), 1 deletions(-) create mode 100644 src/vimeo/Makefile.am create mode 100644 src/vimeo/grl-vimeo.c create mode 100644 src/vimeo/grl-vimeo.h create mode 100644 src/vimeo/gvimeo.c create mode 100644 src/vimeo/gvimeo.h diff --git a/configure.ac b/configure.ac index 032a3c8..241379d 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,12 @@ PKG_CHECK_MODULES(SQLITE, sqlite3, HAVE_SQLITE=yes, HAVE_SQLITE=no) PKG_CHECK_MODULES(GDATA, libgdata >= 0.4.0, HAVE_GDATA=yes, HAVE_GDATA=no) +PKG_CHECK_MODULES(LIBSOUP, libsoup-2.4, HAVE_LIBSOUP=yes, HAVE_LIBSOUP=no) + +AC_CHECK_HEADER([gcrypt.h], HAVE_GCRYPT=yes, HAVE_GCRYPT=no) + +PKG_CHECK_MODULES(GTHREAD, gthread-2.0, HAVE_GTHREAD=yes, HAVE_GTHREAD=no) + # ---------------------------------------------------------- # GUPNP-AV VERSION # ---------------------------------------------------------- @@ -516,6 +522,46 @@ then fi # ---------------------------------------------------------- +# BUILD VIMEO PLUGIN +# ---------------------------------------------------------- + +AC_ARG_ENABLE(vimeo, + AC_HELP_STRING([--enable-vimeo], + [enable Vimeo plugin (default: auto)]), + [ + case "$enableval" in + yes) + if test "x$HAVE_XML" = "xno"; then + AC_MSG_ERROR([xml2 not found, install it or use --disable-vimeo]) + fi + if test "x$HAVE_LIBSOUP" = "xno"; then + AC_MSG_ERROR([libsoup not found, install it or use --disable-vimeo]) + fi + if test "x$HAVE_GCRYPT" = "xno"; then + AC_MSG_ERROR([libgcrypt not found, install it or use --disable-vimeo]) + fi + if test "x$HAVE_GTHREAD" = "xno"; then + AC_MSG_ERROR([gthread not found, install it or use --disable-vimeo]) + fi + ;; + esac + ], + [ + if test "x$HAVE_XML" = "xyes" -a "x$HAVE_LIBSOUP" = "xyes" -a "x$HAVE_GCRYPT" = "xyes" -a "x$HAVE_GTHREAD" = "xyes"; then + enable_vimeo=yes + else + enable_vimeo=no + fi + ]) + +AM_CONDITIONAL([VIMEO_PLUGIN], [test "x$enable_vimeo" = "xyes"]) +GRL_PLUGINS_ALL="$GRL_PLUGINS_ALL vimeo" +if test "x$enable_vimeo" = "xyes" +then + GRL_PLUGINS_ENABLED="$GRL_PLUGINS_ENABLED vimeo" +fi + +# ---------------------------------------------------------- # GETTEXT # ---------------------------------------------------------- @@ -551,6 +597,7 @@ AC_CONFIG_FILES([ src/shoutcast/Makefile src/apple-trailers/Makefile src/metadata-store/Makefile + src/vimeo/Makefile test/Makefile ]) diff --git a/src/Makefile.am b/src/Makefile.am index c45f9d3..e00b176 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -55,7 +55,11 @@ if METADATA_STORE_PLUGIN SUBDIRS += metadata-store endif -DIST_SUBDIRS = youtube fake-metadata filesystem jamendo lastfm-albumart upnp flickr podcasts bookmarks shoutcast apple-trailers metadata-store +if VIMEO_PLUGIN +SUBDIRS += vimeo +endif + +DIST_SUBDIRS = youtube fake-metadata filesystem jamendo lastfm-albumart upnp flickr podcasts bookmarks shoutcast apple-trailers metadata-store vimeo MAINTAINERCLEANFILES = \ *.in \ diff --git a/src/vimeo/Makefile.am b/src/vimeo/Makefile.am new file mode 100644 index 0000000..4526a2c --- /dev/null +++ b/src/vimeo/Makefile.am @@ -0,0 +1,34 @@ +# +# Makefile.am +# +# Author: Joaquim Rocha <jrocha igalia com> +# +# Copyright (C) 2010 Igalia S.L. All rights reserved. + +lib_LTLIBRARIES = libgrlvimeo.la + +libgrlvimeo_la_CFLAGS = \ + $(DEPS_CFLAGS) \ + $(XML_CFLAGS) \ + $(GTHREAD_CFLAGS) \ + $(LIBSOUP_CFLAGS) + +libgrlvimeo_la_LIBADD = \ + $(DEPS_LIBS) \ + $(XML_LIBS) \ + $(GTHREAD_LIBS) \ + $(LIBSOUP_LIBS) + +libgrlvimeo_la_SOURCES = \ + grl-vimeo.c \ + grl-vimeo.h \ + gvimeo.c \ + gvimeo.h + +libdir=$(GRL_PLUGINS_DIR) + +MAINTAINERCLEANFILES = \ + *.in \ + *~ + +DISTCLEANFILES = $(MAINTAINERCLEANFILES) diff --git a/src/vimeo/grl-vimeo.c b/src/vimeo/grl-vimeo.c new file mode 100644 index 0000000..c929163 --- /dev/null +++ b/src/vimeo/grl-vimeo.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * Contact: Iago Toral Quiroga <itoral igalia com> + * + * Authors: Joaquim Rocha <jrocha igalia com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <grilo.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include "grl-vimeo.h" +#include "gvimeo.h" + +#define GRL_VIMEO_SOURCE_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE((object), \ + GRL_VIMEO_SOURCE_TYPE, \ + GrlVimeoSourcePrivate)) + +/* --------- Logging -------- */ + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "grl-vimeo" + +/* --- Plugin information --- */ + +#define PLUGIN_ID "grl-vimeo" +#define PLUGIN_NAME "Vimeo" +#define PLUGIN_DESC "A plugin for browsing and searching Vimeo videos" + +#define SOURCE_ID "grl-vimeo" +#define SOURCE_NAME "Vimeo" +#define SOURCE_DESC "A source for browsing and searching Vimeo videos" + +#define AUTHOR "Igalia S.L." +#define LICENSE "LGPL" +#define SITE "http://www.igalia.com" + +typedef struct { + GrlMediaSourceSearchSpec *ss; + gint offset; + gint page; +} SearchData; + +struct _GrlVimeoSourcePrivate { + GVimeo *vimeo; +}; + +static GrlVimeoSource *grl_vimeo_source_new (void); + +gboolean grl_vimeo_plugin_init (GrlPluginRegistry *registry, + const GrlPluginInfo *plugin, + GList *configs); + +static const GList *grl_vimeo_source_supported_keys (GrlMetadataSource *source); + +static void grl_vimeo_source_metadata (GrlMediaSource *source, + GrlMediaSourceMetadataSpec *ss); + +static void grl_vimeo_source_search (GrlMediaSource *source, + GrlMediaSourceSearchSpec *ss); + +/* =================== Vimeo Plugin =============== */ + +gboolean +grl_vimeo_plugin_init (GrlPluginRegistry *registry, + const GrlPluginInfo *plugin, + GList *configs) +{ + const gchar *vimeo_key; + const gchar *vimeo_secret; + const GrlConfig *config; + gint config_count; + GrlVimeoSource *source; + + g_debug ("vimeo_plugin_init\n"); + + if (!g_thread_supported ()) { + g_thread_init (NULL); + } + + if (!configs) { + g_warning ("Missing configuration"); + return FALSE; + } + + config_count = g_list_length (configs); + if (config_count > 1) { + g_warning ("Provided %d configs, but will only use one", config_count); + } + + config = GRL_CONFIG (configs->data); + + vimeo_key = grl_config_get_api_key (config); + vimeo_secret = grl_config_get_api_secret (config); + + if (!vimeo_key || !vimeo_secret) { + g_warning ("Required configuration keys not set up"); + return FALSE; + } + + source = grl_vimeo_source_new (); + source->priv->vimeo = g_vimeo_new (vimeo_key, vimeo_secret); + + grl_plugin_registry_register_source (registry, + plugin, + GRL_MEDIA_PLUGIN (source)); + return TRUE; +} + +GRL_PLUGIN_REGISTER (grl_vimeo_plugin_init, + NULL, + PLUGIN_ID, + PLUGIN_NAME, + PLUGIN_DESC, + PACKAGE_VERSION, + AUTHOR, + LICENSE, + SITE); + +/* ================== Vimeo GObject ================ */ + +static GrlVimeoSource * +grl_vimeo_source_new (void) +{ + g_debug ("grl_vimeo_source_new"); + + return g_object_new (GRL_VIMEO_SOURCE_TYPE, + "source-id", SOURCE_ID, + "source-name", SOURCE_NAME, + "source-desc", SOURCE_DESC, + NULL); +} + +static void +grl_vimeo_source_class_init (GrlVimeoSourceClass * klass) +{ + GrlMediaSourceClass *source_class = GRL_MEDIA_SOURCE_CLASS (klass); + GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass); + + source_class->metadata = grl_vimeo_source_metadata; + source_class->search = grl_vimeo_source_search; + metadata_class->supported_keys = grl_vimeo_source_supported_keys; + + g_type_class_add_private (klass, sizeof (GrlVimeoSourcePrivate)); +} + +static void +grl_vimeo_source_init (GrlVimeoSource *source) +{ + source->priv = GRL_VIMEO_SOURCE_GET_PRIVATE (source); +} + +G_DEFINE_TYPE (GrlVimeoSource, grl_vimeo_source, GRL_TYPE_MEDIA_SOURCE); + +/* ======================= Utilities ==================== */ + +static gint +str_to_gint (gchar *str) +{ + gint number; + + errno = 0; + number = (gint) g_ascii_strtod (str, NULL); + if (errno == 0) + { + return number; + } + return 0; +} + +static void +update_media (GrlMedia *media, GHashTable *video) +{ + gchar *str; + + str = g_hash_table_lookup (video, VIMEO_VIDEO_ID); + if (str) + { + grl_media_set_id (media, str); + } + + str = g_hash_table_lookup (video, VIMEO_VIDEO_TITLE); + if (str) + { + grl_media_set_title (media, str); + } + + str = g_hash_table_lookup (video, VIMEO_VIDEO_DESCRIPTION); + if (str) + { + grl_media_set_description (media, str); + } + + str = g_hash_table_lookup (video, VIMEO_VIDEO_DURATION); + if (str) + { + grl_media_set_duration (media, str_to_gint (str)); + } + + str = g_hash_table_lookup (video, VIMEO_VIDEO_OWNER_NAME); + if (str) + { + grl_media_set_author (media, str); + } + + str = g_hash_table_lookup (video, VIMEO_VIDEO_UPLOAD_DATE); + if (str) + { + grl_media_set_date (media, str); + } + + str = g_hash_table_lookup (video, VIMEO_VIDEO_THUMBNAIL); + if (str) + { + grl_media_set_thumbnail (media, str); + } + + str = g_hash_table_lookup (video, VIMEO_VIDEO_WIDTH); + if (str) + { + grl_media_video_set_width (media, str_to_gint (str)); + } + + str = g_hash_table_lookup (video, VIMEO_VIDEO_HEIGHT); + if (str) + { + grl_media_video_set_height (media, str_to_gint (str)); + } +} + + +static void +search_cb (GVimeo *vimeo, GList *video_list, gpointer user_data) +{ + GrlMedia *media = NULL; + SearchData *sd = (SearchData *) user_data; + gchar *media_type; + + /* Go to offset element */ + video_list = g_list_nth (video_list, sd->offset); + + /* No more elements can be sent */ + if (!video_list) { + sd->ss->callback (sd->ss->source, + sd->ss->search_id, + NULL, + 0, + sd->ss->user_data, + NULL); + g_free (sd); + return; + } + + while (video_list && sd->ss->count) + { + media_type = g_hash_table_lookup (video_list->data, "title"); + if (media_type) { + media = grl_media_video_new (); + } + + if (media) + { + update_media (media, video_list->data); + sd->ss->callback (sd->ss->source, + sd->ss->search_id, + media, + sd->ss->count == 1? 0: -1, + sd->ss->user_data, + NULL); + } + video_list = g_list_next (video_list); + sd->ss->count--; + media = NULL; + } + + /* Get more elements */ + if (sd->ss->count) + { + sd->offset = 0; + sd->page++; + g_vimeo_videos_search (vimeo, sd->ss->text, sd->page, search_cb, sd); + } + else + { + g_free (sd); + } +} + +static void +video_get_play_url_cb (gchar *url, gpointer user_data) +{ + GrlMediaSourceMetadataSpec *ms = (GrlMediaSourceMetadataSpec *) user_data; + + if (url) + { + grl_media_set_url (ms->media, url); + } + + ms->callback (ms->source, ms->media, ms->user_data, NULL); +} + +/* ================== API Implementation ================ */ + +static const GList * +grl_vimeo_source_supported_keys (GrlMetadataSource *source) +{ + static GList *keys = NULL; + if (!keys) { + keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID, + GRL_METADATA_KEY_TITLE, + GRL_METADATA_KEY_DESCRIPTION, + GRL_METADATA_KEY_URL, + GRL_METADATA_KEY_AUTHOR, + GRL_METADATA_KEY_DATE, + GRL_METADATA_KEY_THUMBNAIL, + GRL_METADATA_KEY_DURATION, + GRL_METADATA_KEY_WIDTH, + GRL_METADATA_KEY_HEIGHT, + NULL); + } + return keys; +} + +static void +grl_vimeo_source_metadata (GrlMediaSource *source, + GrlMediaSourceMetadataSpec *ms) +{ + gint id; + const gchar *id_str; + + if (!ms->media || (id_str = grl_media_get_id (ms->media)) == NULL) + { + ms->callback (ms->source, ms->media, ms->user_data, NULL); + return; + } + + errno = 0; + id = (gint) g_ascii_strtod (id_str, NULL); + if (errno != 0) + { + return; + } + + g_vimeo_video_get_play_url (GRL_VIMEO_SOURCE (source)->priv->vimeo, + id, + video_get_play_url_cb, + ms); +} + +static void +grl_vimeo_source_search (GrlMediaSource *source, + GrlMediaSourceSearchSpec *ss) +{ + SearchData *sd; + gint per_page; + GVimeo *vimeo = GRL_VIMEO_SOURCE (source)->priv->vimeo; + + /* Compute items per page and page offset */ + per_page = CLAMP (1 + ss->skip + ss->count, 0, 100); + g_vimeo_set_per_page (vimeo, per_page); + + sd = g_new (SearchData, 1); + sd->page = 1 + (ss->skip / per_page); + sd->offset = ss->skip % per_page; + sd->ss = ss; + + g_vimeo_videos_search (vimeo, ss->text, sd->page, search_cb, sd); +} diff --git a/src/vimeo/grl-vimeo.h b/src/vimeo/grl-vimeo.h new file mode 100644 index 0000000..fde4f36 --- /dev/null +++ b/src/vimeo/grl-vimeo.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * Contact: Iago Toral Quiroga <itoral igalia com> + * + * Authors: Joaquim Rocha <jrocha igalia com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef _GRL_VIMEO_SOURCE_H_ +#define _GRL_VIMEO_SOURCE_H_ + +#include <grilo.h> + +#define GRL_VIMEO_SOURCE_TYPE \ + (grl_vimeo_source_get_type ()) + +#define GRL_VIMEO_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GRL_VIMEO_SOURCE_TYPE, \ + GrlVimeoSource)) + +#define GRL_IS_VIMEO_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GRL_VIMEO_SOURCE_TYPE)) + +#define GRL_VIMEO_SOURCE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + GRL_VIMEO_SOURCE_TYPE, \ + GrlVimeoSourceClass)) + +#define GRL_IS_VIMEO_SOURCE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), \ + GRL_VIMEO_SOURCE_TYPE)) + +#define GRL_VIMEO_SOURCE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + GRL_VIMEO_SOURCE_TYPE, \ + GrlVimeoSourceClass)) + +typedef struct _GrlVimeoSource GrlVimeoSource; +typedef struct _GrlVimeoSourcePrivate GrlVimeoSourcePrivate; + +struct _GrlVimeoSource { + + GrlMediaSource parent; + + /*< private >*/ + GrlVimeoSourcePrivate *priv; +}; + +typedef struct _GrlVimeoSourceClass GrlVimeoSourceClass; + +struct _GrlVimeoSourceClass { + + GrlMediaSourceClass parent_class; + +}; + +GType grl_vimeo_source_get_type (void); + +#endif /* _GRL_VIMEO_SOURCE_H_ */ diff --git a/src/vimeo/gvimeo.c b/src/vimeo/gvimeo.c new file mode 100644 index 0000000..4ce67f9 --- /dev/null +++ b/src/vimeo/gvimeo.c @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * Contact: Iago Toral Quiroga <itoral igalia com> + * + * Authors: Joaquim Rocha <jrocha igalia com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <gcrypt.h> +#include <libsoup/soup.h> +#include "gvimeo.h" +#include <libxml/parser.h> +#include <libxml/xpath.h> + +#define G_VIMEO_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE((object), \ + G_VIMEO_TYPE, \ + GVimeoPrivate)) + +#define PLUGIN_USER_AGENT "Grilo Vimeo Plugin" + +#define VIMEO_ENDPOINT "http://vimeo.com/api/rest/v2" +#define VIMEO_VIDEO_LOAD_URL "http://vimeo.com/moogaloop/load/clip:" +#define VIMEO_VIDEO_PLAY_URL "http://vimeo.com/moogaloop/play/clip:" + +#define VIMEO_VIDEO_SEARCH_METHOD "vimeo.videos.search" +#define VIMEO_API_OAUTH_SIGN_METHOD "HMAC-SHA1" +#define VIMEO_API_OAUTH_SIGNATURE_PARAM "&oauth_signature=%s" + +#define VIMEO_VIDEO_SEARCH \ + "full_response=yes" \ + "&method=%s" \ + "&oauth_consumer_key=%s" \ + "&oauth_nonce=%s" \ + "&oauth_signature_method=" VIMEO_API_OAUTH_SIGN_METHOD \ + "&oauth_timestamp=%s" \ + "&oauth_token=" \ + "&page=%d" \ + "&per_page=%d" \ + "&query=%s" + +typedef struct { + GVimeo *vimeo; + GVimeoVideoSearchCb search_cb; + gpointer user_data; +} GVimeoVideoSearchData; + +typedef struct { + GVimeo *vimeo; + gint video_id; + GVimeoURLCb callback; + gpointer user_data; +} GVimeoVideoURLData; + +struct _GVimeoPrivate { + gchar *api_key; + gchar *auth_token; + gchar *auth_secret; + gint per_page; + SoupSession *async_session; +}; + +enum InfoType {SIMPLE, EXTENDED}; + +typedef struct { + enum InfoType type; + gchar *name; +} VideoInfo; + +static VideoInfo video_info[] = {{SIMPLE, VIMEO_VIDEO_TITLE}, + {SIMPLE, VIMEO_VIDEO_DESCRIPTION}, + {SIMPLE, VIMEO_VIDEO_UPLOAD_DATE}, + {SIMPLE, VIMEO_VIDEO_WIDTH}, + {SIMPLE, VIMEO_VIDEO_HEIGHT}, + {SIMPLE, VIMEO_VIDEO_OWNER}, + {SIMPLE, VIMEO_VIDEO_URL}, + {SIMPLE, VIMEO_VIDEO_THUMBNAIL}, + {SIMPLE, VIMEO_VIDEO_DURATION}, + {EXTENDED, VIMEO_VIDEO_OWNER}}; + +static void g_vimeo_finalize (GObject *object); + +/* -------------------- GOBJECT -------------------- */ + +G_DEFINE_TYPE (GVimeo, g_vimeo, G_TYPE_OBJECT); + +static void +g_vimeo_class_init (GVimeoClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = g_vimeo_finalize; + + g_type_class_add_private (klass, sizeof (GVimeoPrivate)); +} + +static void +g_vimeo_init (GVimeo *vimeo) +{ + vimeo->priv = G_VIMEO_GET_PRIVATE (vimeo); + vimeo->priv->per_page = 50; + vimeo->priv->async_session = soup_session_async_new (); +} + +static void +g_vimeo_finalize (GObject *object) +{ + GVimeo *vimeo = G_VIMEO (object); + g_free (vimeo->priv->api_key); + g_free (vimeo->priv->auth_secret); + g_object_unref (vimeo->priv->async_session); + + G_OBJECT_CLASS (g_vimeo_parent_class)->finalize (object); +} + +GVimeo * +g_vimeo_new (const gchar *api_key, const gchar *auth_secret) +{ + GVimeo *vimeo = g_object_new (G_VIMEO_TYPE, NULL); + vimeo->priv->api_key = g_strdup (api_key); + vimeo->priv->auth_secret = g_strdup (auth_secret); + + return vimeo; +} + +/* -------------------- PRIVATE API -------------------- */ + +static gchar * +get_timestamp (void) +{ + time_t t = time (NULL); + return g_strdup_printf ("%d", (gint) t); +} + +static gchar * +get_nonce (void) +{ + gchar *timestamp = get_timestamp(); + guint rnd_number = g_random_int (); + gchar *rnd_str = g_strdup_printf ("%d_%s", rnd_number, timestamp); + gchar *nonce = g_compute_checksum_for_string (G_CHECKSUM_MD5, rnd_str, -1); + g_free (timestamp); + g_free (rnd_str); + + return nonce; +} + +static gchar * +get_videos_search_params (GVimeo *vimeo, const gchar *text, gint page) { + gchar *timestamp = get_timestamp (); + gchar *nonce = get_nonce (); + gchar *params = g_strdup_printf (VIMEO_VIDEO_SEARCH, + VIMEO_VIDEO_SEARCH_METHOD, + vimeo->priv->api_key, + nonce, + timestamp, + page, + vimeo->priv->per_page, + text); + g_free (timestamp); + g_free (nonce); + + return params; +} + +static gchar * +sign_string (gchar *message, gchar *key) +{ + gchar *signed_message = NULL; + gcry_md_hd_t digest_obj; + unsigned char *hmac_digest; + + gcry_md_open(&digest_obj, + GCRY_MD_SHA1, + GCRY_MD_FLAG_SECURE | GCRY_MD_FLAG_HMAC); + gcry_md_setkey(digest_obj, key, strlen (key)); + gcry_md_write (digest_obj, message, strlen (message)); + gcry_md_final (digest_obj); + hmac_digest = gcry_md_read (digest_obj, 0); + + signed_message = g_base64_encode (hmac_digest, + strlen ((gchar *) hmac_digest)); + + gcry_md_close (digest_obj); + + return signed_message; +} + +static gboolean +result_is_correct (xmlNodePtr node) +{ + gboolean correct = FALSE; + xmlChar *stat; + + if (xmlStrcmp (node->name, (const xmlChar *) "rsp") == 0) + { + stat = xmlGetProp (node, (const xmlChar *) "stat"); + if (stat && xmlStrcmp (stat, (const xmlChar *) "ok") == 0) + { + correct = TRUE; + xmlFree (stat); + } + } + + return correct; +} + +static void +add_node (xmlNodePtr node, GHashTable *video) +{ + xmlAttrPtr attr; + + for (attr = node->properties; attr != NULL; attr = attr->next) + { + g_hash_table_insert (video, + g_strconcat ((const gchar *) node->name, + "_", + (const gchar *) attr->name, + NULL), + (gchar *) xmlGetProp (node, attr->name)); + } +} + +static xmlNodePtr +xpath_get_node (xmlXPathContextPtr context, gchar *xpath_expr) +{ + xmlNodePtr node = NULL; + xmlXPathObjectPtr xpath_obj; + xpath_obj = xmlXPathEvalExpression ((xmlChar *) xpath_expr, context); + + if (xpath_obj && xpath_obj->nodesetval->nodeTab) + { + node = xpath_obj->nodesetval->nodeTab[0]; + } + xmlXPathFreeObject (xpath_obj); + + return node; +} + +static gchar * +get_node_text (xmlXPathContextPtr context, gchar *xpath_expr) +{ + xmlNodePtr node; + gchar *node_text = NULL; + + node = xpath_get_node (context, xpath_expr); + if (node) + { + node_text = (gchar *) xmlNodeGetContent (node); + } + + return node_text; +} + +static GHashTable * +get_video (xmlNodePtr node) +{ + gint i; + gint array_length; + xmlXPathContextPtr context; + gchar *video_id; + GHashTable *video = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_free); + + /* Adds the video node's properties */ + add_node (node, video); + + context = xmlXPathNewContext (node->doc); + video_id = (gchar *) xmlGetProp (node, (xmlChar *) "id"); + + array_length = G_N_ELEMENTS (video_info); + for (i = 0; i < array_length; i++) + { + /* Look for the wanted information only under the current video */ + gchar *xpath_name = g_strdup_printf ("//video[ id=%s]//%s", + video_id, + video_info[i].name); + xmlNodePtr info_node = xpath_get_node (context, xpath_name); + if (info_node) + { + if (video_info[i].type == EXTENDED) { + add_node (info_node, video); + } + else + { + g_hash_table_insert (video, + g_strdup ((const gchar *) info_node->name), + (gchar *) xmlNodeGetContent (info_node)); + } + } + g_free (xpath_name); + } + g_free (video_id); + + xmlXPathFreeContext (context); + + return video; +} + + +static void +process_video_search_result (const gchar *xml_result, gpointer user_data) +{ + xmlDocPtr doc; + xmlNodePtr node; + GList *video_list = NULL; + GVimeoVideoSearchData *data = (GVimeoVideoSearchData *) user_data; + + doc = xmlReadMemory (xml_result, + xmlStrlen ((xmlChar *) xml_result), + NULL, + NULL, + XML_PARSE_RECOVER | XML_PARSE_NOBLANKS); + node = xmlDocGetRootElement (doc); + + /* Check result is ok */ + if (!node || !result_is_correct (node)) + { + data->search_cb (data->vimeo, NULL, data->user_data); + } + else + { + node = node->xmlChildrenNode; + + /* Now we're at "video pages" node */ + node = node->xmlChildrenNode; + while (node) + { + video_list = g_list_prepend (video_list, get_video (node)); + node = node->next; + } + + data->search_cb (data->vimeo, g_list_reverse (video_list), data->user_data); + g_list_foreach (video_list, (GFunc) g_hash_table_unref, NULL); + g_list_free (video_list); + } + g_free (data); + xmlFreeDoc (doc); +} + +static void +search_videos_complete_cb (SoupSession *session, + SoupMessage *message, + gpointer *data) +{ + GVimeoVideoSearchCb *search_data = (GVimeoVideoSearchCb *) data; + process_video_search_result (message->response_body->data, search_data); +} + +static gchar * +get_play_url_from_vimeo_xml (const gchar *xml, gint video_id) +{ + xmlDocPtr doc = xmlRecoverDoc ((xmlChar *) xml); + xmlXPathContextPtr context = xmlXPathNewContext (doc); + gchar *request_signature = get_node_text (context, + "/xml/request_signature[1]"); + gchar *signature_expires = get_node_text (context, + "/xml/request_signature_expires[1]"); + + gchar *url = g_strdup_printf ("%s%d/%s/%s/?q=sd", + VIMEO_VIDEO_PLAY_URL, + video_id, + request_signature, + signature_expires); + + g_free (request_signature); + g_free (signature_expires); + xmlXPathFreeContext (context); + xmlFreeDoc (doc); + + return url; +} + +static void +get_video_play_url_complete_cb (SoupSession *session, + SoupMessage *message, + gpointer *data) +{ + GVimeoVideoURLData *url_data; + gchar *url; + + if (message->response_body == NULL) + { + return; + } + + url_data = (GVimeoVideoURLData *) data; + url = get_play_url_from_vimeo_xml (message->response_body->data, + url_data->video_id); + url_data->callback (url, url_data->user_data); + g_free (url_data); +} + +static gchar * +encode_uri (gchar *uri) +{ + return soup_uri_encode (uri, "%!*'();:@&=+$,/?#[] "); +} + +static gchar * +build_request (GVimeo *vimeo, const gchar *query, gint page) +{ + gchar *params; + gchar *endpoint_encoded; + gchar *key; + gchar *escaped_str; + gchar *tmp_str; + gchar *signature; + + g_return_val_if_fail (G_IS_VIMEO (vimeo), NULL); + + params = get_videos_search_params (vimeo, query, page); + endpoint_encoded = encode_uri (VIMEO_ENDPOINT); + key = g_strdup_printf ("%s&", vimeo->priv->auth_secret); + escaped_str = encode_uri (params); + tmp_str = g_strdup_printf ("GET&%s&%s", endpoint_encoded, escaped_str); + signature = sign_string (tmp_str, key); + g_free (escaped_str); + g_free (tmp_str); + escaped_str = encode_uri (signature); + tmp_str = g_strdup_printf ("%s?%s" VIMEO_API_OAUTH_SIGNATURE_PARAM, + VIMEO_ENDPOINT, + params, + escaped_str); + + g_free (endpoint_encoded); + g_free (params); + g_free (key); + g_free (escaped_str); + g_free (signature); + + return tmp_str; +} + +/* -------------------- PUBLIC API -------------------- */ + +void +g_vimeo_set_per_page (GVimeo *vimeo, gint per_page) +{ + g_return_if_fail (G_IS_VIMEO (vimeo)); + vimeo->priv->per_page = per_page; +} + +void +g_vimeo_videos_search (GVimeo *vimeo, + const gchar *text, + gint page, + GVimeoVideoSearchCb callback, + gpointer user_data) +{ + SoupMessage *message; + GVimeoVideoSearchData *search_data; + gchar *request; + + g_return_if_fail (G_IS_VIMEO (vimeo)); + + request = build_request (vimeo, text, page); + search_data = g_new (GVimeoVideoSearchData, 1); + search_data->vimeo = vimeo; + search_data->search_cb = callback; + search_data->user_data = user_data; + + message = soup_message_new ("GET", request); + soup_session_queue_message (vimeo->priv->async_session, + message, + (SoupSessionCallback) search_videos_complete_cb, + search_data); + g_free (request); +} + +void +g_vimeo_video_get_play_url (GVimeo *vimeo, + gint id, + GVimeoURLCb callback, + gpointer user_data) +{ + GVimeoVideoURLData *data; + gchar *url = g_strdup_printf ("%s%d", + VIMEO_VIDEO_LOAD_URL, + id); + SoupMessage *message = soup_message_new ("GET", url); + SoupMessageHeaders *headers = message->request_headers; + soup_message_headers_append (headers, "User-Agent", PLUGIN_USER_AGENT); + + data = g_new (GVimeoVideoURLData, 1); + data->video_id = id; + data->vimeo = vimeo; + data->callback = callback; + data->user_data = user_data; + + soup_session_queue_message (vimeo->priv->async_session, + message, + (SoupSessionCallback) get_video_play_url_complete_cb, + data); + g_free (url); +} diff --git a/src/vimeo/gvimeo.h b/src/vimeo/gvimeo.h new file mode 100644 index 0000000..34e17a3 --- /dev/null +++ b/src/vimeo/gvimeo.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * Contact: Iago Toral Quiroga <itoral igalia com> + * + * Authors: Joaquim Rocha <jrocha igalia com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef _G_VIMEO_H_ +#define _G_VIMEO_H_ + +#include <glib-object.h> + +#define G_VIMEO_TYPE \ + (g_vimeo_get_type ()) + +#define G_VIMEO(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + G_VIMEO_TYPE, \ + GVimeo)) + +#define G_IS_VIMEO(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + G_VIMEO_TYPE)) + +#define G_VIMEO_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + G_VIMEO_TYPE, \ + GVimeoClass)) + +#define G_IS_VIMEO_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), \ + G_VIMEO_TYPE)) + +#define G_VIMEO_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + G_VIMEO_TYPE, \ + GVimeoClass)) + +#define VIMEO_VIDEO "video" +#define VIMEO_VIDEO_ID VIMEO_VIDEO "_id" +#define VIMEO_VIDEO_TITLE "title" +#define VIMEO_VIDEO_DESCRIPTION "description" +#define VIMEO_VIDEO_URL "url" +#define VIMEO_VIDEO_UPLOAD_DATE "upload_date" +#define VIMEO_VIDEO_WIDTH "width" +#define VIMEO_VIDEO_HEIGHT "height" +#define VIMEO_VIDEO_DURATION "duration" +#define VIMEO_VIDEO_OWNER "owner" +#define VIMEO_VIDEO_THUMBNAIL "thumbnail" + +#define VIMEO_VIDEO_OWNER_NAME VIMEO_VIDEO_OWNER "_realname" + +typedef struct _GVimeo GVimeo; +typedef struct _GVimeoPrivate GVimeoPrivate; + +struct _GVimeo { + + GObject parent; + + /*< private >*/ + GVimeoPrivate *priv; +}; + +typedef struct _GVimeoClass GVimeoClass; + +struct _GVimeoClass { + + GObjectClass parent_class; + +}; + +typedef void (*GVimeoVideoSearchCb) (GVimeo *vimeo, + GList *videolist, + gpointer user_data); + +typedef void (*GVimeoURLCb) (gchar *url, gpointer user_data); + +GType g_vimeo_get_type (void); + +GVimeo *g_vimeo_new (const gchar *api_key, const gchar *auth_secret); + +void g_vimeo_video_get_play_url (GVimeo *vimeo, + gint id, + GVimeoURLCb callback, + gpointer user_data); + +void g_vimeo_set_per_page (GVimeo *vimeo, gint per_page); + +void g_vimeo_videos_search (GVimeo *vimeo, + const gchar *text, + gint page, + GVimeoVideoSearchCb callback, + gpointer user_data); + +#endif /* _G_VIMEO_H_ */ -- 1.6.3.3
Attachment:
signature.asc
Description: OpenPGP digital signature