Vimeo plugin



Hi folks,

I've been developing a Grilo plugin to prove search for videos in Vimeo.

Today I finally got it cleaned and I think it is ready for a review and
possible inclusion in the plugins' codebase by you.


This plugin was based in the Flickr one.


Cheers,

--
Joaquim Rocha
From a076ab4a49d696cda66c7e5b96c3e1e1cfc7fe79 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          |   56 ++++++
 src/Makefile.am       |    6 +-
 src/vimeo/Makefile.am |   34 ++++
 src/vimeo/grl-vimeo.c |  386 ++++++++++++++++++++++++++++++++++++
 src/vimeo/grl-vimeo.h |   77 ++++++++
 src/vimeo/gvimeo.c    |  519 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/vimeo/gvimeo.h    |  112 +++++++++++
 7 files changed, 1189 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 7a1fd95..e9b2561 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,21 @@ AC_SUBST(SQLITE_LIBS)
 AC_SUBST(HAVE_SQLITE)
 AM_CONDITIONAL(HAVE_SQLITE, test "x$HAVE_SQLITE" = "xyes")
 
+PKG_CHECK_MODULES(LIBSOUP, libsoup-2.4, HAVE_LIBSOUP=yes, HAVE_LIBSOUP=no)
+AC_SUBST(LIBSOUP_CFLAGS)
+AC_SUBST(LIBSOUP_LIBS)
+AC_SUBST(HAVE_LIBSOUP)
+AM_CONDITIONAL(HAVE_LIBSOUP, test "x$HAVE_LIBSOUP" = "xyes")
+
+AC_CHECK_HEADER([gcrypt.h], HAVE_GCRYPT=yes, HAVE_GCRYPT=no)
+AM_CONDITIONAL(HAVE_GCRYPT, test "x$HAVE_GCRYPT" = "xyes")
+
+PKG_CHECK_MODULES(GTHREAD, gthread-2.0, HAVE_GTHREAD=yes, HAVE_GTHREAD=no)
+AC_SUBST(GTHREAD_CFLAGS)
+AC_SUBST(GTHREAD_LIBS)
+AC_SUBST(HAVE_GTHREAD)
+AM_CONDITIONAL(HAVE_GTHREAD, test "x$HAVE_GTHREAD" = "xyes")
+
 # ----------------------------------------------------------
 # GUPNP-AV VERSION
 # ----------------------------------------------------------
@@ -535,6 +550,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
 # ----------------------------------------------------------
 
@@ -570,6 +625,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..9823b33
--- /dev/null
+++ b/src/vimeo/grl-vimeo.c
@@ -0,0 +1,386 @@
+/*
+ * 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;
+
+  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;
+  }
+
+  GrlVimeoSource *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)
+{
+  errno = 0;
+  gint 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)
+{
+  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;
+  gint 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)
+{
+  GVimeo *vimeo = GRL_VIMEO_SOURCE (source)->priv->vimeo;
+  gint per_page;
+
+  /* Compute items per page and page offset */
+  per_page = CLAMP (1 + ss->skip + ss->count, 0, 100);
+  g_vimeo_set_per_page (vimeo, 50);
+
+  SearchData *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..7ff67cb
--- /dev/null
+++ b/src/vimeo/gvimeo.c
@@ -0,0 +1,519 @@
+/*
+ * 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_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 void
+skip_garbage_nodes (xmlNodePtr *node)
+{
+  /* Result contains "\n" and "\t" to pretty align XML. Unfortunately, libxml
+     doesn't cope very fine with them, and it creates "fakes" nodes with name
+     "text" and value those characters. So we need to skip them */
+  while ((*node) && xmlStrcmp ((*node)->name, (const xmlChar *) "text") == 0)
+  {
+    (*node) = (*node)->next;
+  }
+}
+
+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;
+  xmlXPathObjectPtr xpath_obj;
+  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)
+{
+  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");
+
+  gint i;
+  gint 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 = xmlRecoverDoc ((xmlChar *) xml_result);
+  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;
+    skip_garbage_nodes (&node);
+
+    /* Now we're at "video pages" node */
+    node = node->xmlChildrenNode;
+    skip_garbage_nodes (&node);
+    while (node)
+    {
+      video_list = g_list_prepend (video_list, get_video (node));
+      node = node->next;
+      skip_garbage_nodes (&node);
+    }
+
+    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)
+{
+  if (message->response_body == NULL)
+  {
+    return;
+  }
+
+  GVimeoVideoURLData *url_data = (GVimeoVideoURLData *) data;
+  gchar *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)
+{
+  g_return_val_if_fail (G_IS_VIMEO (vimeo), NULL);
+
+  gchar *params;
+  gchar *endpoint_encoded;
+  gchar *key;
+  gchar *escaped_str;
+  gchar *tmp_str;
+  gchar *signature;
+
+  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);
+  g_free (endpoint_encoded);
+  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 (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)
+{
+  g_return_if_fail (G_IS_VIMEO (vimeo));
+
+  SoupMessage *message;
+  GVimeoVideoSearchData *search_data;
+  gchar *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



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